redis学习
本文为学习《Redis深度历险》的学习笔记
第一章 基础和应用
1.1、redis安装
redis支持dock安装和直接安装。
这里使用github镜像https://github.com/tporadowski/redis/releases
将下载下来的zip包解压,放到合适的位置(这里放在了D:\redis)
进入D:\redis,打开cmd命令行运行下列命令启动服务端
redis-server.exe redis.windows.conf
另起一个窗口运行下列命令启动客户端
redis-cli.exe -h 127.0.0.1 -p 6379
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ii8hDZBZ-1622899251406)(.\images\permissionDenied.png)]
如果redis报以上错误,说明redis没有权限读取rdb文件,查看redis.windows-service.conf的working directory是否在c盘,将其改成其他盘即可。
1.2、redis数据结构
1.2.1、String
1、Redis中String是动态String,类似于Java中的ArrayList。
2、扩容:当字符串长度小于1MB时加倍扩容,当字符串长度大于1MB时每次扩容1MB,Redis的String长度最大是512MB。
3、crud
#插入一条记录,key=name,value=codehole
set name codehole
#获取name
get name
#判断key是否存在
exists name
#删除
del name
批量操作
set name1 codehole
set name2 holycoder
#批量查询
mget name1 name2
#批量插入
mset name3 value3 name4 value4
mget name1 name2 name3 name4
设置过期
set name value
expire name 5 #设置name的过期时间为5s
数值型字符串
#如果value是一个整数,redis提供了自增操作,redis的整数最大是Long.Max,即9223372036854775807,超出这个值会报错
set age 30
#自增操作
incr age
#加5
incrby age 5
incrby age -5
1.2.2、List
Redis中List相当于Java语言里的LinkedList,Redis中的List是双向列表,支持rpush、rpop、lpush、lpop操作。
1、curd
由于redis中的list是双向列表,所以可以实现队列和栈两种数据结构。
队列:
#从右侧向books列表中插入3条数据
rpush books java python go
#查询列表长度
llen books
#从左侧取出一条数据,此时的list相当于一个队列(先进先出)
lpop books
>"java"
lpop books
>"python"
lpop books
>"go"
栈:
#从右侧向books插入三条数据
rpush books java Python go
#从右侧取出一条数据,此时的list相当于一个栈
rpop books
>go
rpop books
>python
rpop books
>java
lindex和ltrim操作
lindex:类似于java中链表的get(int index)操作,其时间复杂度为O(n)。index可以为负数,0表示第一个元素,-1表示倒数第一个元素。
ltrim:类似于java中String的trim,有两个参数start_index和end_index,保留着两个参数之间的元素。
rpush books java Python go
lindex books 1 #取出第二个元素
>python
lrange books 0 -1 #获取所有元素
ltrim books 1 -1 #保留第二个元素到最后一个元素
ltrim books 1 0 #删除所有元素
redis的list底层使用“快速链表”(quicklist)实现。它是由多个压缩列表(ziplist)通过双向指针连接而成。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v6lMuZEw-1622899251408)(.\images\1.3quicklist.png)]
1.2.3、hash
redis中的字典相当于java中的hashmap,无序,以数组+链表的二维结构。redis字典的value只能是String,且redis为了不阻塞服务,使用渐进式rehash策略逐步替换旧的hash结构。
curd
#向books散列添加一个键值对
hset books java "think in java"
hset books go "concurrency in go"
#获取元素
hget books
#获取散列中的所有元素
hgetall books
#获取长度
hlen books
#批量插入
hmset books java "effective java" python "learning python"
hash的value如果是整型也支持自增操作
hset human age 28
#自增操作
hincrby human age 1
1.2.4、set
redis中的set类似于java中的hashset
curd
sadd books python
sadd books java
#列出set中所有元素
smembers books
#查询某个元素是否存在
sismember books rust
#查询set集合大小
scard books
#弹出一个元素
spop books
1.2.5、zset
redis中的zset类似于java中的sortedSet和HashMap的结合体,通过一个score对元素进行排序。
zadd books 9.0 "think in java"
zadd books 8.5 "learning python"
zadd books 8.0 "golang"
#列出所有元素,按score顺序排出(从小到大)
zrange books 0 -1
#列出所有元素,按score逆序排出(从大到小)
zrevrange books 0 -1
#查询zset大小
zcard books
#查询单个元素得分大小
zscore books "think in java"
#查询单个元素排名,从0开始
zrank books "think in java"
#遍历分值范围内的元素
zrangebyscore books 0 8.9
#遍历元素,并返回对应的score,-inf表示负无穷大
zrangebyscore books -inf 8.9 withscore
#删除元素
zrem books "golang"
跳表:参考https://www.cnblogs.com/hunternet/p/11248192.html
1.2.6、过期时间
1、redis所有对象都支持过期时间设置
2、如果一个字符串设置了过期时间,但是又使用set方法修改,过期时间会失效
3、redis失效是一整个对象失效,比如hash失效是整个hash对象的失效,而非hash中的某个value。
set book codehole
#设置失效时间5s
expire book 5
#查询剩余有效时间
ttl book
#设置失效时间为500毫秒
pexpire book 500
1.3、分布式锁
1.3.1、分布式锁实现
setnx(set if not exists)如果不存在返回1,如果存在返回0
setnx lock true #加锁
del lock #解锁
1、死锁问题,如果某个线程获取到锁之后发生异常退出,其他线程无法再次拿到锁执行。
解决方法:redis锁添加超时时间,一定时间后自动释放锁。
setex lock 5 true #设置lock的过期时间为5s
2、可重入性,redis本身不支持可重入锁,可以通过客户端代码实现可重入。
public class RedisWithReentrantLock{
private ThreadLocal<Map<String, Integer>> lockers =new ThreadLocal<>();
private Jedis jedis;
public RedisWithReentrantLock(Jedis jedis){
this.jedis = jedis;
}
private boolean _lock(String key){
return jedis.set(key,"","nx","ex",5L)!=null;
}
private void _unlock(String key){
jedis.del(key);
}
private Map<String,Integer> currentLockers(){
Map<String,Integer> refs = lockers.get();
if(refs!=null){
return refs;
}
lockers.set(new HashMap<>());
return lockers.get();
}
//第一次加锁时使用redis,之后的每次加锁只在本地+1;
public boolean lock(String key){
Map<String,Integer> refs=currentLockers();
Integer refCnt = refs.get(key);
if(refCnt!=null){
refs.put(key,refCnt+1);
return true;
}
boolean ok =this._lock(key);
if(!ok){
return false;
}
refs.put(key,1);
return true;
}
//每次解锁时本地-1,只有最后一次解锁时释放redis
public boolean unlock(String key){
Map<String,Integer> refs = currentLockers();
Integer refCnt = refs.get(key);
if(refCnt==null){
return false;
}
refCnt -=1;
if(refCnt>0){
refs.put(key,refCnt);
}else{
refs.remove(key);
this._unlock(key);
}
return true;
}
public static void main(String[] args){
Jedis jedis =new Jedis();
RedisWithReentrantLock redis =new RedisWithReentrantLock(jedis);
System.out.println(redis.lock("codehole"));
System.out.println(redis.lock("codehole"));
System.out.println(redis.unlock("codehole"));
System.out.println(redis.unlock("codehole"));
}
}
1.3.2、锁冲突处理
1、直接抛出异常。这种适合客户端直接发起的请求,可以返回一段文字让用户稍后重试。
2、sleep。sleep会阻碍当前线程,导致队列的后续消息处理出现延迟。
3、延迟队列。将冲突的请求扔到另一个队列延后处理。
**1.4、**使用redis实现简单消息队列
1.4.1、使用redis的list数据结构可以实现简单消息队列,使用lpush和lpop实现消息的生产和消费。
1.4.2、消息空了,消费者会一直pop,导致redis性能问题怎么办?
答:消息为空时让消费者sleep,以减少redis压力。
1.4.3、sleep会造成消费延迟,比如sleep1秒,当list为空时下一次消费会在一分钟后,如果此时来了消息会有一分钟延迟,怎么办?
答:使用blpush和blpop实现阻塞读,当读不到内容时阻塞当前线程,直到来消息。
1.4.4、使用阻塞读如何应对空闲连接自动断开问题(一定时间内没有生产消息连接会自动断开)?
答:捕获异常,对异常进行处理(报错或重新连接)。
1.4.5、延迟队列实现代码
//暂无
1.5、位图(bitmap)
顾名思义,使用bit存储。比如记录一个员工365天哪几天打卡,如果使用hash结构,其存储空间是惊人的,如果使用位图,只需要365/8+1=46个字节。
1、位图操作
比如h的ASCII码是0b1101000
零存零取
#01101000
setbit str 1 1 #第一位是1
setbit str 2 1 #第二位是1
setbit str 4 1 #第四位是1
get str #获取bitmap对应的string
零存零取
setbit str 1 1
setbit str 2 1
setbit str 4 1
getbit str 1 #获取bitmap第一位的值 0或1
整存零取
set str h #整存
getbit str 1 #获取"h"的第1位
bitcount和bitpos
bitcount:统计一定范围内1的个数
bitpos:统计一定范围内第一个出现的0或1
set str hello
bitcount str #获取hello中1的个数
bitcount str 0 1 #获取前两个字符中1的个数(注意一个字符是八位)
bitpos str 0 #查询第一个出现0的位置
bitpos str 1 2 3 #查询第二个字符和第三个字符中第一次出现1的位置
bitfield:可以对bitmap进行多位操作
#he:01101000 01100101
set str he
#从下标为0开始取4位,返回一个无符号数
bitfield str get u4 0
>6
#从下标为2的bit开始取三位,返回一个有符号数
bitfield str get i3 2
>-3 #为什么是-3,我们取到的是 101,第一个位为 1,是符号位数,表示这是个负数。然后我们把 01 取反得 10,然后取补码即 加1 得 11,结果就是 -3 了
1.6、HyperLogLog
背景:记录一个网页每天的UV(unique visitor)。
方法1:使用set集合对用户id进行去重。
优点:实现简单,结果精确。
缺点:如果用户规模上亿,set会很大,存储空间占用多。
方法2:使用HyperLogLog估算。
优点:存储占用少。
缺点:结果不精确,对用户id随机性要求较高。
HyperLogLog提供了两个指令pfadd和pfcount。
pfadd:类似于set的sadd
pfcount:类似于scard
pfadd usrs usr1
pfadd usrs usr2
pfcount usrs
实现原理:HyperLogLog记录value中bit的低位最高连续0出现的长度K,估算出数据量N在2K~2(K+1)之间(实际算法比这复杂多,比如使用桶增加准确性)。
HyperLogLog需要额外12KB的存储空间,因为HyperLogLog使用214个桶,每个桶占6bit,共214*6/8=12KB的空间来存放低位最高连续0的个数。
1.7、布隆过滤器(bloom filter)
原理:将value映射成bit放入bitmap中,查询时对照bitmap。
布隆过滤器如果返回true表示可能存在,如果返回false表示一定不存在。因为映射实际上是一种hash,可能有hash冲突。
bf.add usr usr1 #向布隆过滤器中添加元素
bf.exists urs usr1 #判断是否存
1.8、基于redis的限流器
背景:需要判断一个用户行为是否合法,比如1分钟内点赞次数不能超过5次。
分析:使用用户的点赞行为做key,使用zset存储,将每次请求的时间戳作为score。当一次新点赞时将该行为加入zset中,并且剔除1分钟之前的记录,最后返回zset中集合总数,即为这1分钟内的点赞数。代码如下:
public class SimpRateLimiter{
private Jedis jedis;
public SimpRateLimiter(Jedis jedis){
this.jedis = jedis;
}
public boolean isActionAllowed(String usrId,String actionKey,int period,int maxCnt){
String key = String.format("hist:%s:%s",usrId,actionKye);
long nowTs=System.currentTimeMillis();
Pipeline pipe = jedis.pipelined();
pipe.multi();
pipe.zadd(key,nowTs,""+nowTs);//添加当前记录
pipe.zremrangeByScore(key,0,nowTs-period*1000);//删除时间窗口之前的记录
Response<Long> cnt=pipe.zcard(key);//计算当前时间窗口内记录数
pipe.expire(key,period+1);//如果下个窗口内没有action则失效当前zset
pipe.exec();
pipe.close();
return cnt.get()<=maxCnt;
}
}
1.9、redis-cell
1.10、GoeHash
背景:微信的附近的人是怎么实现的?
GoeHash算法思想:将地球想像成一个二维平面。将这个平面切割成4块,分别标记为00、01、10、11。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3JtH4hbB-1622899251410)(.\images\geohash1.png)]
再将这四个小块分别细分
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2RCdaqDT-1622899251413)(.\images\geohash2.png)]
最终将一个二维坐标映射成一串二进制数进行存储。
redis中geo的基本用法
geoadd graph 111.48105 39.996794 meituan #添加一个坐标meituan
geoadd graph 111.514203 39.905409 ireader #添加一个坐标掌阅
geodist graph meituan ireader km #计算距离
geopos graph meituan #获取美团的坐标
geohash graph meituan #获取base32编码的hash值,可以去http://geohash.org/${hash}获取真是地址坐标。
georadiusbymember graph ireader 20 km count 3 asc #获取iReader附近20km之内的3个地址,按距离升序排序。
1.11、keys和scan
keys和scan用于返回redis中有哪些key,支持简单的正则表达式。
keys * #获取所有的key
keys j*va #获取带正则表达式的key
keys的缺点:没有offset、limit,只能返回所有结果。
scan的基本用法
scan 0 match java* count 1000 #从游标为为0开始扫描,每次扫描1000个槽位(slot)
这里有两点需要解释:(1)、游标位置不是递增的,因为redis扫描采用高位进位法;(为了应对扩容时不重复扫描)(2)、count 1000也不是会返回1000条数据,这里1000是指会扫描1000个槽位。
并且像set、zset、hash都提供ssan、zscan、hscan来遍历集合的key。
第二章、核心原理
第三章、集群篇
3.1、主从同步
3.1.1、CAP原理:
C:Consistent,一致性
A:Availability,可用性
P:Partition tolerance,分区容错性
网络分区:分布式系统间网络断开的场景。
用一句话概括CAP定理就是,当网络分区发生时,一致性和可用性无法同时满足。
Redis支持主从同步和从从同步
增量同步:redis将指令流存在内存buffer中,然后将buffer中的指令流异步同步到从节点。从节点一边执行指令流一边反馈自己同步到哪了(偏移量)。buffer是一个定长环形数值,这就存在当主从节点隔离较久,数组满了之后后面的指令会覆盖前面的指令,造成数据丢失。
快照同步:主节点先将内存中的所有数据生成一个快照文件,并将这个快照文件发送给从节点。从节点加载这个文件到内存,之后再通过增量同步写入同步期间增量的指令。如果同步较慢导致主节点buffer中内容被覆盖,则又要进行一次快照同步。因此需要设置一个合适大小的buffer。
无盘复制:主节点一边遍历内存,一边将序列化的内容发送到从节点。
wait指令确保强一致性
set key value
wait 5 0 #wait N t ,强制redis同步复制,5个从节点接受到数据时返回,否则无限等待t=0表示无限等待。
3.2、Sentinel(哨兵模式)
哨兵用于持续监测主节点和从节点,当有主节点发生异常时Sentinel会选出一个新的主节点,并将其他从节点与新的主节点建立复制关系。等旧的主节点恢复正常之后也成为其中一个从节点。
3.2.1、消息丢失问题
redis使用异步同步模式,当主节点挂了之后有可能数据没有同步到从节点,这时会发生数据丢失。
redis提供两个参数尽量避免数据丢失(不是一定能避免)。
min-slaves-to-write 1 #表示至少有一个从节点进行正常复制,否则就停止对外服务。
min-slaves-max-log 10 #表示10s内收到从节点的反馈,否则就是不正常。
第四章、拓展篇
4.1、Stream
Stream是redis的一种特殊数据结构,借鉴自Kafka。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hm3gAziR-1622899251415)(\images\redisStreamStructure.jpg)]
1、Stream支持多个消费组(Consumer Group),每个组有一个游标last_delivered_id。消费组中任意成员消费,游标向前移动(消息在同一个消息组被唯一消费)。
2、每个消息者内部会有一个状态变量pending_ids,记录已被读取但未接受到ack的消息。用于确保消息被成功消费。
xadd msg * name sun #xadd key ID field string,*表示让redis自动生成ID
xlen msg #获取消息长度
xrange msg - + #xrange msg - +,获取消息列表,-表示最小值,+表示最大值
4.2 Info
info指令用于获取redis系统信息,分为9大块
info server XXX #服务器信息
info client XXX #客户端信息
info memory XXX #内存信息
info persistence XXX #持久化信息
info stats XXX #通用统计信息
info replication XXX #主从复制信息
info cpu XXX #cpu使用情况
info cluster XXX #集群信息
info keyspace XXX #键值对统计数量信息
使用info获取redis全部系统信息
info
# Server
redis_version:5.0.10
redis_git_sha1:1c047b68
redis_git_dirty:0
redis_build_id:76de97c74f6945e9
redis_mode:standalone
os:Windows
arch_bits:64
multiplexing_api:WinSock_IOCP
atomicvar_api:pthread-mutex
process_id:4576
run_id:db0774d7a59e46e7c063240ab37fdbae6b061ac9
tcp_port:6379
uptime_in_seconds:33
uptime_in_days:0
hz:10
configured_hz:10
lru_clock:12033865
executable:D:\redis\redis-server.exe
config_file:D:\redis\redis.windows.conf
# Clients
connected_clients:1
client_recent_max_input_buffer:2
client_recent_max_output_buffer:0
blocked_clients:0
# Memory
used_memory:723808
used_memory_human:706.84K
used_memory_rss:681808
used_memory_rss_human:665.83K
used_memory_peak:723808
used_memory_peak_human:706.84K
used_memory_peak_perc:100.14%
used_memory_overhead:710510
used_memory_startup:660264
used_memory_dataset:13298
used_memory_dataset_perc:20.93%
allocator_allocated:39416784
allocator_active:511705088
allocator_resident:515899392
total_system_memory:0
total_system_memory_human:0B
used_memory_lua:37888
used_memory_lua_human:37.00K
used_memory_scripts:0
used_memory_scripts_human:0B
number_of_cached_scripts:0
maxmemory:0
maxmemory_human:0B
maxmemory_policy:noeviction
allocator_frag_ratio:12.98
allocator_frag_bytes:472288304
allocator_rss_ratio:1.01
allocator_rss_bytes:4194304
rss_overhead_ratio:0.00
rss_overhead_bytes:-515217584
mem_fragmentation_ratio:1.00
mem_fragmentation_bytes:0
mem_not_counted_for_evict:0
mem_replication_backlog:0
mem_clients_slaves:0
mem_clients_normal:49950
mem_aof_buffer:0
mem_allocator:jemalloc-5.2.1-redis
active_defrag_running:0
lazyfree_pending_objects:0
# Persistence
loading:0
rdb_changes_since_last_save:0
rdb_bgsave_in_progress:0
rdb_last_save_time:1622646568
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:-1
rdb_current_bgsave_time_sec:-1
rdb_last_cow_size:0
aof_enabled:0
aof_rewrite_in_progress:0
aof_rewrite_scheduled:0
aof_last_rewrite_time_sec:-1
aof_current_rewrite_time_sec:-1
aof_last_bgrewrite_status:ok
aof_last_write_status:ok
aof_last_cow_size:0
# Stats
total_connections_received:1
total_commands_processed:1
instantaneous_ops_per_sec:0
total_net_input_bytes:31
total_net_output_bytes:11483
instantaneous_input_kbps:0.00
instantaneous_output_kbps:0.00
rejected_connections:0
sync_full:0
sync_partial_ok:0
sync_partial_err:0
expired_keys:0
expired_stale_perc:0.00
expired_time_cap_reached_count:0
evicted_keys:0
keyspace_hits:0
keyspace_misses:0
pubsub_channels:0
pubsub_patterns:0
latest_fork_usec:0
migrate_cached_sockets:0
slave_expires_tracked_keys:0
active_defrag_hits:0
active_defrag_misses:0
active_defrag_key_hits:0
active_defrag_key_misses:0
# Replication
role:master
connected_slaves:0
master_replid:3ecda06233f1018c4c582d35fb82da35607bf0de
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
# CPU
used_cpu_sys:0.109375
used_cpu_user:0.156250
used_cpu_sys_children:0.000000
used_cpu_user_children:0.000000
# Cluster
cluster_enabled:0
# Keyspace
db0:keys=5,expires=0,avg_ttl=0
redis-cli.exe info stats #获取通用统计数据
4.5、redis淘汰策略–LRU
当redis内存不足时提供一下几种策略用于内存回收。
- noviction:禁止写服务,允许读服务,能够有效防止数据丢失。
- volatile-lru:使用lru算法淘汰设置了过期时间的key。
- volatile-ttl:淘汰剩余过期时间最小的key。
- volatile-random:随机删除设置了过期时间的key。
- allkeys-lru:使用lru策略淘汰全体key,不光是设置了淘汰时间。
- allkeys-random:随机淘汰全体key中的一部分。
总结:volatie针对设置了过期时间的key,allkey针对全体key。
4.5.2、近似LRU算法
LRU算法需要记录每个key的最近访问频次,需要消耗大量内存空间,并且在每次访问时需要更新链表。
近似LRU算法为每个key设置一个最后一次被访问的时间戳(占24bit),当redis发现内存超出maxmemory时随机采样。删除样本中时间戳最早的,如果是allkeys策略,则随机删除一个key。
4.6、懒惰删除
redis del命令可用来删除redis对象,但如果一个key是一个特别大的对象,会造成redis的卡顿。
redis4.x版本引入了unlink命令,用于异步回收。
unlink key #等同于del key,异步回收资源
4.6.2、flush
flushall #删除当前db并持久化(rdb文件恢复到最初状态)
flushdb #删除当前db但不持久化(可以通过重启redis恢复)
4.7、Jedis
public class JedisTest{
public static void main(String[] args){
JedisPool pool = new JedisPool();//新建一个jedis连接池
Jedis jedis = pool.getResource();//获取连接
doSomething(jedis);//处理业务逻辑
jedis.close();//关闭连接
}
}
上述代码在业务逻辑发生异常时无法归还连接给连接池
public class JedisTest{
public static void main(String[] args){
JedisPool pool = new JedisPool();
try(Jedis jedis = pool.getResource()){
doSomething(jedis);
}catch(Exception e){
...
}
}
}
上述代码会自动关闭try()中的资源,前提是这些资源实现了AutoCloseable接口。
上述代码还有问题,我们怎么保证所有程序员一定会这么写,从而确保jedis资源被释放。开发人员应该关注业务实现。因此需要对JedisPool再次封装。
public class JedisPool{
private JedisPool pool = new JedisPool();
public void excute(RedisCaller caller){
try(Jedis jedis = pool.getResource()){
caller.call(jedis);
}
}
}
public interface RedisCaller{
public void call(Jedis jedis);
}
public class RedisTest{
public static void main(String[] args){
JedisPool pool = new JedisPool();
pool.excute(new RedisCaller(){
@Override
public void call(Jedis jedis){
jedis.doSomething();
}
})
}
}
redis重试
我们只要在excute方法里进行重试即可
public class RetryJedisPool extends JedisPool{
@Override
public void excute(RedisCaller caller){
try(Jedis jedis = pool.getResource()){
caller.call(jedis);
}catch(JedisConnectionException e){
caller.call(jedis);
}finnaly{
jedis.close();
}
}
}
4.8、rename-command 指令
rename-command 指令用于将某些危险的指令修改成别的名称或禁用,比如flushall
rename-command flushall flushall-force #重命名
rename-command flushall "" #禁用flushall
4.9、spiped
redis本身不支持SSL连接,所以当redis数据在公网进行传输时有泄漏的风险。redis提供的方案时使用spiped。
原理时spiped会在客户端和服务端同时启动两个spiped进程,客户端先将数据发送给客户端spiped,再由客户端spiped将数据加密后发送给服务端spiped,服务端spiped将数据解密后发送给redis服务器。
{
jedis.doSomething();
}
})
}
}
redis重试
我们只要在excute方法里进行重试即可
```java
public class RetryJedisPool extends JedisPool{
@Override
public void excute(RedisCaller caller){
try(Jedis jedis = pool.getResource()){
caller.call(jedis);
}catch(JedisConnectionException e){
caller.call(jedis);
}finnaly{
jedis.close();
}
}
}
4.8、rename-command 指令
rename-command 指令用于将某些危险的指令修改成别的名称或禁用,比如flushall
rename-command flushall flushall-force #重命名
rename-command flushall "" #禁用flushall
4.9、spiped
redis本身不支持SSL连接,所以当redis数据在公网进行传输时有泄漏的风险。redis提供的方案时使用spiped。
原理时spiped会在客户端和服务端同时启动两个spiped进程,客户端先将数据发送给客户端spiped,再由客户端spiped将数据加密后发送给服务端spiped,服务端spiped将数据解密后发送给redis服务器。