Redis–狂神說Redis基礎匯總(完結)
- 2021 年 6 月 15 日
- 筆記
Redis–狂神說Redis基礎匯總(完結)
2021.6.12-2021.6.14:端午學學玩玩弄完了Redis基礎的匯總,越學越覺得自己知識量的匱乏。
參考鏈接:狂神說Java–Redis匯總://www.bilibili.com/video/BV1S54y1R7SB?p=1
Redis五種基本數據類型:
-
1.String字元串
OK 127.0.0.1:6380> set k1 1 OK 127.0.0.1:6380> INCR k1 2 127.0.0.1:6380> DECR k1 1 127.0.0.1:6380> keys * k1 127.0.0.1:6380> set k2 huyuqiao OK 127.0.0.1:6380> GETRANGE K2 0 3 127.0.0.1:6380> GETRANGE k2 0 3 huyu 127.0.0.1:6380> GETRANGE k2 0 -1 huyuqiao 127.0.0.1:6380> SETRANGE k2 1 XX 8 127.0.0.1:6380> get k2 hXXuqiao 127.0.0.1:6380> SETEX k3 30 "hello, world" #set exist:存在或不存在都替換 OK 127.0.0.1:6380> ttl k3 28 127.0.0.1:6380> ttl k2 -1 127.0.0.1:6380> keys * k1 k2 127.0.0.1:6380> ttl k3 -2 127.0.0.1:6380> setnx mykey "redis" #set if not exist:不存在就set,存在就set失敗,還是原來值 1 127.0.0.1:6380> ttl mykey -1 127.0.0.1:6380> setnx mykey "mongodb" 0 127.0.0.1:6380> get mykey redis 127.0.0.1:6380> ttl k2 #永久返回-1 -1 127.0.0.1:6380> ttl k3 #過期返回-2 -2 127.0.0.1:6380> 127.0.0.1:6380> mset user:1:name huyuqiao user:1:age 22 OK 127.0.0.1:6380> mget user:1:name user:1:age huyuqiao 22 127.0.0.1:6380>
-
List
127.0.0.1:6380> FLUSHALL OK 127.0.0.1:6380> clear 127.0.0.1:6380> LPUSH list 1 1 127.0.0.1:6380> LPUSH list 2 2 127.0.0.1:6380> LPUSH list 3 3 127.0.0.1:6380> LRANGE list 0 -1 3 2 1 127.0.0.1:6380> RPUSH list a 4 127.0.0.1:6380> LPOP list 3 127.0.0.1:6380> LRANGE 0 -1 ERR wrong number of arguments for 'lrange' command 127.0.0.1:6380> LRANGE list 0 -1 2 1 a 127.0.0.1:6380> LINDEX list 0 2 127.0.0.1:6380> LLEN list 3 127.0.0.1:6380> FLUSHALL OK 127.0.0.1:6380> clear 127.0.0.1:6380> LPUSH list one 1 127.0.0.1:6380> LPUSH list two 2 127.0.0.1:6380> LPUSH list two 3 127.0.0.1:6380> LREM list 1 one #移除list中等於one的一個元素(最後加入的移除掉) 1 127.0.0.1:6380> LRANGE list 0 -1 two two 127.0.0.1:6380> LREM list 2 one 0 127.0.0.1:6380> LREM list 2 two 2 127.0.0.1:6380> LRANGE list 0 -1 127.0.0.1:6380> FLUSHALL 127.0.0.1:6380> LPUSH list one 1 127.0.0.1:6380> LPUSH list two 2 127.0.0.1:6380> LPUSH list three 3 127.0.0.1:6380> LPUSH list four 4 127.0.0.1:6380> LTRIM list 1 2 OK 127.0.0.1:6380> LRANGE list 0 -1 three two 127.0.0.1:6380>
-
Set
127.0.0.1:6380> FLUSHALL OK 127.0.0.1:6380> SADD myset "hello" 1 127.0.0.1:6380> sadd myset "world" 1 127.0.0.1:6380> sadd myset "huyuqiao" 1 127.0.0.1:6380> smembers myset hello huyuqiao world 127.0.0.1:6380> SISMEMBER myset huyuqiao 1 127.0.0.1:6380> sadd myset "HYQ" 1 127.0.0.1:6380> SMEMBERS myset hello huyuqiao HYQ world 127.0.0.1:6380> SREM myset hello 1 127.0.0.1:6380> scard myset 3 127.0.0.1:6380> SMEMBERS myset huyuqiao HYQ world 127.0.0.1:6380> SRANDMEMBER myset world 127.0.0.1:6380> SRANDMEMBER myset huyuqiao 127.0.0.1:6380>
-
Hash
huyuqiao 127.0.0.1:6380> FLUSHALL OK 127.0.0.1:6380> clear 127.0.0.1:6380> hset myhash field1 huyuqiao 1 127.0.0.1:6380> hmset myhash field1 hello field2 world OK 127.0.0.1:6380> hmget myhash field1 field2 hello world 127.0.0.1:6380> hgetall myhash field1 hello field2 world 127.0.0.1:6380> hlen myhash 2 127.0.0.1:6380> HEXISTS myhash field1 1 127.0.0.1:6380> HKEYS myhash field1 field2 127.0.0.1:6380> HVALS myhash hello world 127.0.0.1:6380> HSETNX myhash field4 hello #hash設置 1 127.0.0.1:6380> HGETALL myhash field1 hello field2 world field4 hello 127.0.0.1:6380>
-
Zset
127.0.0.1:6380> zadd salary 100 huyuqiao 1 127.0.0.1:6380> zadd salary 200 HUYUQIAO 1 127.0.0.1:6380> zadd salary 300 HYQ 1 127.0.0.1:6380> ZRANGEBYSCORE salary -inf +inf huyuqiao HUYUQIAO HYQ 127.0.0.1:6380> zrange salary 0 -1 huyuqiao HUYUQIAO HYQ 127.0.0.1:6380> zrem salary huyuqiao 1 127.0.0.1:6380> ZRANGEBYSCORE salary -inf +inf HUYUQIAO HYQ
Redis三種特殊數據類型:
1.GeoSpatical
適用場景:獲取附近人,好友距離,網約車定位功能。
# 添加位置經緯度
127.0.0.1:6379> GEOADD china:city 116.41667 39.91667 北京
(integer) 1
127.0.0.1:6379> GEOADD china:city 121.43333 31.23000 上海
(integer) 1
127.0.0.1:6379> GEOADD china:city 106.45000 29.56667 重慶
(integer) 1
127.0.0.1:6379> GEOADD china:city 114.06667 22.61667 深圳
(integer) 1
127.0.0.1:6379> GEOADD china:city 120.20000 30.26667 杭州
(integer) 1
127.0.0.1:6379> GEOADD china:city 108.95000 34.26667 西安
(integer) 1
# 查看不同位置經緯度
127.0.0.1:6379> GEOPOS china:city 北京 西安
#查看不同位置之間距離(默認是m)
127.0.0.1:6379> GEODIST china:city 北京 上海
"1066981.1340"
127.0.0.1:6379> GEODIST china:city 北京 上海 km
"1066.9811"
127.0.0.1:6379> GEODIST china:city 北京 重慶 km
"1465.8918"
127.0.0.1:6379>
# 查看指定經緯度周邊範圍內的地方(可以以此擴散到周圍指定範圍內的指定人數的好友)
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km
重慶
西安
深圳
杭州
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km
重慶
西安
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withdist #查看某經緯度周圍500km的所有城市和距離/km
重慶
346.0548
西安
484.7511
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withcoord #查看某經緯度周圍500km的所有城市和經緯度
重慶
106.4500012993812561
29.56666939001875249
西安
108.95000249147415161
34.2666710302806834
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km count 1 #查看某經緯度周圍500km的三個城市
重慶
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km count 3 #查看某經緯度周圍500km的三個城市
重慶
西安
127.0.0.1:6379>
#查看地點周邊的所有城市(類似於定位)
127.0.0.1:6379> GEORADIUSBYMEMBER china:city 北京 1000 km
北京
西安
127.0.0.1:6379> GEORADIUSBYMEMBER china:city 上海 1000 km
杭州
上海
127.0.0.1:6379>
#將城市的二維經緯度轉成一位hash字元串
127.0.0.1:6379> GEOHASH china:city 北京 重慶
wx4g14s53n0
wm78nq6w2f0
127.0.0.1:6379>
#GEO底層是zset,所有可以用zset命令來操作
127.0.0.1:6379> zrange china:city 0 -1 #查看所有城市
重慶
西安
深圳
杭州
上海
北京
127.0.0.1:6379> zrem china:city 北京 #刪除北京這個城市
2.Hyperloglog
適用場景: 網站UV量。傳統用set統計,但若存在大量用戶id,則太消耗內容且麻煩,若只為計數且允許有錯誤率(0.81%),則可行,否則還是用set統計
基數:集合中不重複元素個數。如{1, 3, 5, 5 ,7}則為{1,3,5,7},基數為4
127.0.0.1:6379> clear
127.0.0.1:6379> PFADD mykey a b c d e f g h i j #設置mykey 集合
(integer) 1
127.0.0.1:6379> PFCOUNT mykey #統計mykey 集合基數數量
(integer) 10
127.0.0.1:6379> PFADD mykey2 i j z x c v b n m
(integer) 1
127.0.0.1:6379> PFCOUNT mykey2
(integer) 9
127.0.0.1:6379> PFMERGE mykey3 mykey mykey2 #2個集合取並集
OK
127.0.0.1:6379> PFCOUNT mykey3
(integer) 15
127.0.0.1:6379>
3.Bitmaps
適用場景:判斷、統計活躍、不活躍,登錄、未登錄這些非1即0的場景
[root@VM-8-11-centos ~]# redis-cli -a root --raw
127.0.0.1:6379> setbit sign 0 1 #設置sign用戶某天是否打卡
(integer) 0
127.0.0.1:6379> SETBIT sign 1 0
(integer) 0
127.0.0.1:6379> SETBIT sign 2 0
(integer) 0
127.0.0.1:6379> SETBIT sign 3 1
(integer) 0
127.0.0.1:6379> SETBIT sign 4 1
(integer) 0
127.0.0.1:6379> SETBIT sign 5 0
(integer) 0
127.0.0.1:6379> SETBIT sign 6 0
(integer) 0
127.0.0.1:6379> GETBIT sign 3 #得到sign用戶某天打卡情況
(integer) 1
127.0.0.1:6379> GETBIT sign 6
Redis事務:
Reidsi事務:沒有隔離級別概念,即事務不保證原子性(事務內部有多條命令,不一定,可以實現部分成功、部分失敗),但是單條命令保證原子性
1.Redis事務流程:
- 開啟事務(multi)
- 命令入隊
- 執行事務(exec)
#開啟-結束事務
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> exec
OK
OK
v2
OK
127.0.0.1:6379>
#放棄事務:之前隊列程式碼全部rollback
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 asdfasdf
QUEUED
127.0.0.1:6379> DISCARD
OK
127.0.0.1:6379> EXEC
ERR EXEC without MULTI
127.0.0.1:6379> get k2
v2
2.Redis事務非原子性
之所以事務非原子性,是因為有兩種異常:
-
編譯性異常(檢查性異常):程式碼錯了,根本就沒某個語句、函數、配置文件等
127.0.0.1:6379> MULTI OK 127.0.0.1:6379> set k1 v1 QUEUED 127.0.0.1:6379> set k2 v2 QUEUED 127.0.0.1:6379> gasdfa k3 #語句錯誤,相當於沒執行,在編譯時候就出錯了 ERR unknown command 'gasdfa' 127.0.0.1:6379> set k4 v4 QUEUED 127.0.0.1:6379> EXEC EXECABORT Transaction discarded because of previous errors. 127.0.0.1:6379> get k4 127.0.0.1:6379> get k1
-
運行時異常(非檢查性異常):沒有某個變數、某個對象沒有new等(不會全部rollback)
127.0.0.1:6379> set k1 "v1" OK 127.0.0.1:6379> multi OK 127.0.0.1:6379> incr k1 QUEUED 127.0.0.1:6379> set k2 v2 QUEUED 127.0.0.1:6379> get k2 QUEUED 127.0.0.1:6379> EXEC ERR value is not an integer or out of range OK v2 #上面出錯了,但是依然不影響下面語句執行,所以證明了redis事務是不保證原子性的 127.0.0.1:6379>
Redis樂觀鎖:
樂觀鎖:不會上鎖,更新數據時才會比較version是否被人修改過(redis事務中如果被修改過,則事務不會執行成功)
悲觀鎖:無論做什麼,都會上鎖,效率低下但是安全
Redis事務中用<font color=red size=3>Watch</font>
實現樂觀鎖,中途如果被修改,導致version變更,則事務全部都不會成功
127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> watch money #Redis樂觀鎖:watch監視money
OK
127.0.0.1:6379> MULTI #在另一個窗口將money改成了101
OK
127.0.0.1:6379> DECRBY money 10
QUEUED
127.0.0.1:6379> incrby out 10
QUEUED
127.0.0.1:6379> EXEC
127.0.0.1:6379> get money
101
127.0.0.1:6379>
SpringBooot–配置Redis源程式碼
Jedis:直連,多執行緒下不安全,類似於BIO模式(Springboot2.X後被淘汰)
Lettuce:採用Netty,多執行緒下共享,類似於NIO模式
autoconfig->srping.factories中找到redis相關配置
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
SpringBooot–自定義RedisTemplate、RedisUtil
1.RedisTemplate序列化配置
package com.empirefree.springboot.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* @program: springboot
* @description: RedisTemplate配置
* @author: huyuqiao
* @create: 2021/06/13 16:05
*/
@Configuration
public class RedisConfig {
@Bean
@SuppressWarnings("all")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(factory);
// Json序列化配置
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL,
JsonTypeInfo.As.WRAPPER_ARRAY);
jackson2JsonRedisSerializer.setObjectMapper(om);
// String 的序列化
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key採用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也採用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式採用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value序列化方式採用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
2.RedisUtil配置(CRUD操作string,map,list,set)
package com.empirefree.springboot.utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* @program: springboot
* @description: Redis工具類
* @author: huyuqiao
* @create: 2021/06/13 16:14
*/
@Component
public final class RedisUtil {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// =============================common============================
/**
* 指定快取失效時間
* @param key 鍵
* @param time 時間(秒)
*/
public boolean expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根據key 獲取過期時間
* @param key 鍵 不能為null
* @return 時間(秒) 返回0代表為永久有效
*/
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* 判斷key是否存在
* @param key 鍵
* @return true 存在 false不存在
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 刪除快取
* @param key 可以傳一個值 或多個
*/
@SuppressWarnings("unchecked")
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(key));
}
}
}
// ============================String=============================
/**
* 普通快取獲取
* @param key 鍵
* @return 值
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 普通快取放入
* @param key 鍵
* @param value 值
* @return true成功 false失敗
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 普通快取放入並設置時間
* @param key 鍵
* @param value 值
* @param time 時間(秒) time要大於0 如果time小於等於0 將設置無限期
* @return true成功 false 失敗
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 遞增
* @param key 鍵
* @param delta 要增加幾(大於0)
*/
public long incr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("遞增因子必須大於0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* 遞減
* @param key 鍵
* @param delta 要減少幾(小於0)
*/
public long decr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("遞減因子必須大於0");
}
return redisTemplate.opsForValue().increment(key, -delta);
}
// ================================Map=================================
/**
* HashGet
* @param key 鍵 不能為null
* @param item 項 不能為null
*/
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}
/**
* 獲取hashKey對應的所有鍵值
* @param key 鍵
* @return 對應的多個鍵值
*/
public Map<Object, Object> hmget(String key) {
return redisTemplate.opsForHash().entries(key);
}
/**
* HashSet
* @param key 鍵
* @param map 對應多個鍵值
*/
public boolean hmset(String key, Map<String, Object> map) {
try {
redisTemplate.opsForHash().putAll(key, map);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* HashSet 並設置時間
* @param key 鍵
* @param map 對應多個鍵值
* @param time 時間(秒)
* @return true成功 false失敗
*/
public boolean hmset(String key, Map<String, Object> map, long time) {
try {
redisTemplate.opsForHash().putAll(key, map);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一張hash表中放入數據,如果不存在將創建
*
* @param key 鍵
* @param item 項
* @param value 值
* @return true 成功 false失敗
*/
public boolean hset(String key, String item, Object value) {
try {
redisTemplate.opsForHash().put(key, item, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一張hash表中放入數據,如果不存在將創建
*
* @param key 鍵
* @param item 項
* @param value 值
* @param time 時間(秒) 注意:如果已存在的hash表有時間,這裡將會替換原有的時間
* @return true 成功 false失敗
*/
public boolean hset(String key, String item, Object value, long time) {
try {
redisTemplate.opsForHash().put(key, item, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 刪除hash表中的值
*
* @param key 鍵 不能為null
* @param item 項 可以使多個 不能為null
*/
public void hdel(String key, Object... item) {
redisTemplate.opsForHash().delete(key, item);
}
/**
* 判斷hash表中是否有該項的值
*
* @param key 鍵 不能為null
* @param item 項 不能為null
* @return true 存在 false不存在
*/
public boolean hHasKey(String key, String item) {
return redisTemplate.opsForHash().hasKey(key, item);
}
/**
* hash遞增 如果不存在,就會創建一個 並把新增後的值返回
*
* @param key 鍵
* @param item 項
* @param by 要增加幾(大於0)
*/
public double hincr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, by);
}
/**
* hash遞減
*
* @param key 鍵
* @param item 項
* @param by 要減少記(小於0)
*/
public double hdecr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, -by);
}
// ============================set=============================
/**
* 根據key獲取Set中的所有值
* @param key 鍵
*/
public Set<Object> sGet(String key) {
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 根據value從一個set中查詢,是否存在
*
* @param key 鍵
* @param value 值
* @return true 存在 false不存在
*/
public boolean sHasKey(String key, Object value) {
try {
return redisTemplate.opsForSet().isMember(key, value);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 將數據放入set快取
*
* @param key 鍵
* @param values 值 可以是多個
* @return 成功個數
*/
public long sSet(String key, Object... values) {
try {
return redisTemplate.opsForSet().add(key, values);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 將set數據放入快取
*
* @param key 鍵
* @param time 時間(秒)
* @param values 值 可以是多個
* @return 成功個數
*/
public long sSetAndTime(String key, long time, Object... values) {
try {
Long count = redisTemplate.opsForSet().add(key, values);
if (time > 0)
expire(key, time);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 獲取set快取的長度
*
* @param key 鍵
*/
public long sGetSetSize(String key) {
try {
return redisTemplate.opsForSet().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 移除值為value的
*
* @param key 鍵
* @param values 值 可以是多個
* @return 移除的個數
*/
public long setRemove(String key, Object... values) {
try {
Long count = redisTemplate.opsForSet().remove(key, values);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
// ===============================list=================================
/**
* 獲取list快取的內容
*
* @param key 鍵
* @param start 開始
* @param end 結束 0 到 -1代表所有值
*/
public List<Object> lGet(String key, long start, long end) {
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 獲取list快取的長度
*
* @param key 鍵
*/
public long lGetListSize(String key) {
try {
return redisTemplate.opsForList().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 通過索引 獲取list中的值
*
* @param key 鍵
* @param index 索引 index>=0時, 0 表頭,1 第二個元素,依次類推;index<0時,-1,表尾,-2倒數第二個元素,依次類推
*/
public Object lGetIndex(String key, long index) {
try {
return redisTemplate.opsForList().index(key, index);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 將list放入快取
*
* @param key 鍵
* @param value 值
*/
public boolean lSet(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 將list放入快取
* @param key 鍵
* @param value 值
* @param time 時間(秒)
*/
public boolean lSet(String key, Object value, long time) {
try {
redisTemplate.opsForList().rightPush(key, value);
if (time > 0)
expire(key, time);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 將list放入快取
*
* @param key 鍵
* @param value 值
* @return
*/
public boolean lSet(String key, List<Object> value) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 將list放入快取
*
* @param key 鍵
* @param value 值
* @param time 時間(秒)
* @return
*/
public boolean lSet(String key, List<Object> value, long time) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
if (time > 0)
expire(key, time);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根據索引修改list中的某條數據
*
* @param key 鍵
* @param index 索引
* @param value 值
* @return
*/
public boolean lUpdateIndex(String key, long index, Object value) {
try {
redisTemplate.opsForList().set(key, index, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 移除N個值為value
*
* @param key 鍵
* @param count 移除多少個
* @param value 值
* @return 移除的個數
*/
public long lRemove(String key, long count, Object value) {
try {
Long remove = redisTemplate.opsForList().remove(key, count, value);
return remove;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
}
Redis–持久化
持久化:在指定時間間隔內將記憶體數據存入磁碟中,斷電也能恢複數據,使用快照文件讀到記憶體中。
1.Redis–RDB(默認推薦)
-
保存過程:父進程fork一個子進程,將數據持久化到臨時文件中,持久化結束,再替換上次的RDB正式文件。
-
觸發條件:
- save滿足:命令
save 900 1
即是在15分鐘內修改了1次 即會觸發RDB。 - 執行FlushAll命令
- 退出Redis,會產生RDB文件
- save滿足:命令
-
適用場景:適合大規模數據恢復且數據完整性不敏感的情況。
2.Redis–AOF(重啟後是默認先載入AOF,因為數據更完整)
-
保存過程:父進程fork一個子進程,以日誌形式將所有指令記錄下來(讀操作不記錄),然後將數據只追加不改寫到AOF文件,然後替換上次的AOF文件
-
觸發條件:
appendfsync always/everysec/no
命令 -
適用場景:對恢複數據完整性要求嚴格
-
重寫場景:不斷追加文件到一個閾值,則會重寫aof文件
Redis–發布訂閱
發布訂閱:可以做消息推送、聊天室等等
#發布者:往Redis某渠道中發消息,所有訂閱者都可以接收到
127.0.0.1:6379> PUBLISH huyuqiao "hello,world"
1
#訂閱者:訂閱Redis中某個渠道channel
127.0.0.1:6379> SUBSCRIBE huyuqiao
subscribe
huyuqiao
1
message
huyuqiao
hello,world
Redis–主從複製(主寫從讀)
1.Linux配置文件
- daemonize的no改成yes
- port、pidfile、logfile、dbfilename改成對於新redis名
- 如果redis有密碼,需要在slave中配置或者在master中去掉密碼
#master斷了,slaver依然是原來master的slaver。但是slaver斷了,master就沒有slaver了
>>slaveof no one #關閉slave狀態,變成master
>>shutdown #停止redis,其slave狀態停止,下次啟動變成master,且原來master下的該redis就沒有了
redis-server redis80.conf #啟動80redis窗口。
kill -s 9 pid #關閉某個進程
2. 複製原理
- 全量複製:slave啟動後,發送sync同步命令給master,然後同步master中所有數據
- 增量複製:master寫數據後,slave實時得到。
Redis–哨兵模式
流程:如下,在啟動哨兵後,關閉79主服務,就從80,81中投票選舉了80為主服務,然後把79,80都當成了slave,所以下次79啟動的時候會默認是slave。另外,由於數據是增量複製,所以數據在80中保存完好,且79啟動後就全量複製到79中了
#哨兵模式下的日誌
8044:X 14 Jun 16:35:06.885 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
8044:X 14 Jun 16:35:06.885 # Sentinel ID is c0ce22fc8365ff48663b7db710ce8c359529c3d9
8044:X 14 Jun 16:35:06.885 # +monitor master mymaster 127.0.0.1 6379 quorum 1
8044:X 14 Jun 16:35:51.556 # +sdown master mymaster 127.0.0.1 6379
8044:X 14 Jun 16:35:51.556 # +odown master mymaster 127.0.0.1 6379 #quorum 1/1
8044:X 14 Jun 16:35:51.556 # +new-epoch 1
8044:X 14 Jun 16:35:51.556 # +try-failover master mymaster 127.0.0.1 6379
8044:X 14 Jun 16:35:51.572 # +vote-for-leader c0ce22fc8365ff48663b7db710ce8c359529c3d9 1
8044:X 14 Jun 16:35:51.572 # +elected-leader master mymaster 127.0.0.1 6379
8044:X 14 Jun 16:35:51.572 # +failover-state-select-slave master mymaster 127.0.0.1 6379
8044:X 14 Jun 16:35:51.624 # +selected-slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
8044:X 14 Jun 16:35:51.624 * +failover-state-send-slaveof-noone slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
8044:X 14 Jun 16:35:51.684 * +failover-state-wait-promotion slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
8044:X 14 Jun 16:35:51.826 # +promoted-slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
8044:X 14 Jun 16:35:51.826 # +failover-state-reconf-slaves master mymaster 127.0.0.1 6379
8044:X 14 Jun 16:35:51.914 * +slave-reconf-sent slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
8044:X 14 Jun 16:35:52.831 * +slave-reconf-inprog slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
8044:X 14 Jun 16:35:52.831 * +slave-reconf-done slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
8044:X 14 Jun 16:35:52.912 # +failover-end master mymaster 127.0.0.1 6379
8044:X 14 Jun 16:35:52.912 # +switch-master mymaster 127.0.0.1 6379 127.0.0.1 6380
8044:X 14 Jun 16:35:52.912 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6380
8044:X 14 Jun 16:35:52.912 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380
Redis–快取穿透、擊穿、雪崩
- 快取穿透:快取和資料庫中都沒有數據
- 解決方案:1.布隆過濾器 2.存儲空對象:資料庫沒找到後,redis中臨時存一個空對象
- 快取擊穿:某個key值過期,來了大量訪問
- 解決方案:1.永不過期 2.分散式鎖:一個執行緒獲取,其他執行緒等待
- 快取雪崩:海量key值過期,來了大量訪問
- 解決方案:1.redis高可用:多設置幾台redis 2.限流降級:快取失效後,通過加鎖或者隊列來控制讀資料庫寫快取的數量 3.數據預熱:大量數據載入到快取中,根據不同訪問量來設置不同過期時間