Redis笔记

Redis作为一款高效的键值型内存数据库,值得学习。本文梳理学习过程中的笔记。

Redis笔记

钱文品. Redis深度历险:核心原理与应用实践[M]. 北京: 电子工业出版社, 2019.

1 概述

1.1 了解

Redis主要可以用作:

  • 缓存:记录点赞数、缓存热帖、用户行为、榜单……;
  • 分布式锁

1.2 安装

1
2
3
4
5
# ubuntu
sudo apt install redis

# centos
sudo yum install redis

1.3 运行

1
2
# redis command-line interface
redis-cli

2 数据结构

Redis提供5种基础数据结构,分别为:字符串string、列表list、字典hash、集合set、有序集合zset。

2.1 字符串string

Redis所有数据结构都以唯一的key字符串作为名称,以此获得相应的value数据。

Redis string内部数据结构类似Java的ArrayList,预分配冗余空间以免频繁分配内存。当字符串小于1MB时,扩容方法为加倍当前容量;当超过1MB时,每次扩充1MB空间。字符串最大长度为512MB。

2.2 列表list

Redis list类似Java中的LinkedList链表(实际上不完全是),双向链表,插入和删除时间复杂度O(1),查询时间复杂度O(N)。

当list删除最后一个元素时,该数据结构被自动删除,内存回收。

双向链表可以被用来实现队列、栈。

2.2.1 底层实现——ziplist与quicklist

Redis list底层实现是quicklist数据结构。

当list元素较少时,采用ziplist(压缩列表)。ziplist用连续内存将所有的元素连续存储。

当list元素较多时,采用quicklist(快速链表)。quicklist是将链表与ziplist结合的产物,每一个ziplist包含多个元素,却仅需两个前后指针,因此,quicklist避免了为每个元素配备prev/next双指针的空间消耗。quicklist既满足了快速的插入和删除,又避免了产生较大的空间冗余。

2.3 字典hash

Redis hash类似Java中的HashMap,无序字典,存储键值对。

hash采用数组+链表的数据结构,但hash的值只能是字符串。

当hash删除最后一个元素时,该数据结构被自动删除,内存回收。

2.3.1 渐进式rehash

Java的HashMap每次rehash需要一次性全部rehash,而Redis的hash在rehash时,为了避免阻塞服务,采用渐进式rehash。

渐进式rehash在rehash时,保留新旧两个hash结构。旧的hashtable仍可用作查询,同时将旧的hashtable持续rehash到新的hashtable上。等rehash全部完成后,才以新的hashtable取代旧的hashtable。

2.4 集合set

Redis set相当于Java中的HashSet,内部的键值对时无序的、唯一的。

set的底层实现相当于是hash,只不过hash的value村的都是NULL。

当set删除最后一个元素时,该数据结构被自动删除,内存回收。

2.5 有序集合zset

Redis zset类似Java的SortedSet和HashMap的结合体。

zset一方面是set,value元素是唯一的,另一方面其有序性是依靠为value赋予score作为排序权重实现的。

当zset删除最后一个元素时,该数据结构被自动删除,内存回收。

2.5.1 跳跃列表skiplist

zset内部的排序功能采用skiplist实现。

skiplist中,高层(level)链表跨度大,连接比较大的跨度范围。越往底层跨度越小,表示比较小的跨度范围。通过skiplist,可以从大范围缩小到小范围,快速定位插入与查询的位置。

2.6 小结

2.6.1 通用性质

list, set, hash, zset这四种容器数据结构具有两个通用性质:

  1. create if not exists:操作时,如容器不存在,则新建;
  2. drop if not elements:操作时,如容器为空,则删除。

2.6.2 过期时间

所有数据结构都可设置过期时间,过期则删除。

字符串设置过期时间后,如果字符串被修改,则过期时间失效。

3 应用

3.1 分布式锁

setnx (set if not exists)指令做锁标记,del删除锁标记。

1
2
3
> setnx lock:resource_a true
> expire lock:resource_a 5
> del lock:resource_a
  • 事务需要判单自己能够设置对争用资源的分布式锁,才能修改资源。
  • expire设置5秒过期时间,防止死锁。

3.2 延时队列

list可以作为异步消息队列。

rpush/lpush操作入队列,lpop/rpop操作出队列。

blpop/brpop可以阻塞式(blocking)地读取数据。

3.3 位图

get/set处理整个位图的内容。

getbit/setbit处理各个位。

bitcount统计范围内1的位数。

bitpos查询第一个0或1的位置。

bitfield,包含get/set/incrby子指令,可以读取、设置和自增指定范围的位。bitfield可以混合多个子指令执行。

3.4 HyperLogLog

统计PV量无需去重,incrby自增就可以。统计UV则需要去重,不是简单的自增,去重常用的set集合在数据量很大时会消耗巨大的内存空间。

HyperLogLog可以实现去重计数问题。

pfadd添加元素(增加对该元素的计数);

pfcount统计元素的计数。

pfmerge用于合并多个pf计数元素为同一个元素,合并pf计数值。

pf指的是HyperLogLog的发明人Philippe Flajolet教授。

HyperLogLog数据结构在计数较小时采用 稀疏矩阵存储,在计数超过阈值时,转变为稠密矩阵。

HyperLogLog占据12KB存储空间,在数据量很大时,比set小了太多。

HyperLogLog的原理是调整低位连续零位的最大长度K,若K越大,概率越低,则说明计数N越大,由此通过有限的连续零位K来估算计数N,K与N存在线性相关性。占用12KB则是因为Redis的HyperLogLog实现采用\(2^{14}=16384\)个桶,每个桶maxbit为6 bit,因此\(2^{14} \times 6bit \div 8bit/byte = 12KB\)

3.5 布隆过滤器(Bloom Filter)

RedisBloom

RedisBloom: Probabilistic Data Structures for Redis

The RedisBloom module provides four data structures: a scalable Bloom filter, a cuckoo filter, a count-min sketch, and a top-k. These data structures trade perfect accuracy for extreme memory efficiency, so they're especially useful for big data and streaming applications.

bf.add添加元素;

bf.exists检查元素是否存在。

bf.madd添加多个元素;

bf.mexists检查多个元素是否存在。(返回分别表示每个元素存在性的0/1)

bf.reverse在添加元素之前预设布隆过滤器的key, error_rate和initial_size。

布隆过滤器:

  • 添加时计算元素k个哈希,将对应的k个bit置为1;
  • 检查存在时计算元素k个哈希,检查对应的k个bits是否都为1,如果是,则元素存在,否则不存在。

对hash函数数量k,布隆过滤器bit数量m,预计元素数量n,错误率f,有公式: \[ k = \ln2 \times (m/n) = 0.7 \times (m / n) \\ f = 2^{-k} = 0.6185^{m/n} \]

  • 此时错误率最低。

3.6 简单限流

以zset的score范围来划定滑动窗口。score存储timestamp,这样就可以计算得出时间窗口内的元素数量,判断访问计数是否超限。

zset不适合数量很大的限流,例如:60秒内限流100万次,100万个元素的zset会占用过大的空间。

3.7 漏斗限流

redis-cell

A Redis module that provides rate limiting in Redis as a single command. Implements the fairly sophisticated generic cell rate algorithm (GCRA) which provides a rolling time window and doesn't depend on a background drip process.

漏斗(funnel)容量有限,不满时可以装入液体,漏斗满时无法装入液体,需要等漏斗内的液体慢慢流走一部分,才能继续装入。

1
2
3
4
5
6
CL.THROTTLE user123 15 30 60 1
▲ ▲ ▲ ▲ ▲
| | | | └───── apply 1 token (default if omitted)
| | └──┴─────── 30 tokens / 60 seconds
| └───────────── 15 max_burst
└─────────────────── key "user123"

3.8 GeoHash

通过GeoHash功能,可以快速找出指定经纬度周围的元素。

GeoHash将二维平面处理成网格,然后不断地行、列二分,对二维坐标进行编码,映射为一维整数。

Redis中,GeoHash将经纬度编码为52位整数,存入zset中,score是经纬度编码整数(zset的浮点数score可以无损存储52位整数),value是元素值。在zset中,借助skiplist来找出元素附近范围的其他元素是很容易的事情。使用坐标时,将编码整数解码还原为坐标即可。

geoadd添加经纬度坐标;

geodist计算元素之间的距离;

geopos读取元素的坐标;

geohash读取元素的经纬度编码字符串(base32编码的坐标值)。

georadiusbymember查询指定元素附近的其他元素。

注意:集群中,单个key下存储的坐标数量不宜过多(超过1MB),避免集群迁移出现卡顿。或者干脆采用独立实例,不做集群。

3.9 scan

keys列出符合pattern的key,采用遍历算法,时间复杂度O(N)。

scan从指定cursor开始,匹配pattern,扫描count个槽位。相较于keys,scan可以避免每次遍历整个redis内存槽。

Redis本身就相当于是一个很大的HashMap。scan的遍历顺序采用高位进位加法,以此避免字典扩容和缩容时重复或遗漏遍历槽位。

zscan遍历zset元素;

sscan遍历set元素;

hscan遍历hash元素。

3.10 避免bigkey

在业务开发中,避免大key的产生。

大key数据不论是在集群迁移时,还是在容器需要扩容时,哪怕是在回收时(因较大内存空间的分配和回收),都容易造成卡顿。

可以采用--bigkeys选项来检索大key。

1
redis-cli --bigkeys

4 原理

4.1 I/O模型

Redis是单线程程序。

Redis通过非阻塞I/O多路复用技术来提高单线程I/O处理效率。

对于每一个客户端socket连接,Redis为其关联:

  1. 一个指令队列,用于从客户端socket连接中读取指令。指令队列中的指令遵循FCFS;
  2. 一个响应队列,用于向客户端socket连接中写入指令。如果响应队列为空,说明暂无响应数据,则将该响应队列移出多路复用的write_fds以节省select代价。

对于定时任务,Redis采用最小堆进行管理:

  • 最临近的任务放在堆顶;
  • 取堆顶任务的距离时间作为select操作的timeout,这样在这段时间内就可以放心地select,不必担心错过定时任务。

4.2 通信协议

RESP (Redis Serialization Protocol)是Redis采用地通信协议,这是一种文本协议,实现简单,解析性能好。

RESP把数据分为5种最小单元类型,制定规则:

  1. 单行字符串,以+开头;
  2. 多行字符串,以$<len>开头;
  3. 整数,以:开头;
  4. 错误消息,以-开头;
  5. 数组,以*<len>开头。

4.3 持久化

4.3.1 快照

Redis采用fork机制创建子进程来导出快照。

内存空间采用COW机制,因此,父进程照常处理事务,修改的数据会记录在新的空间中,而子进程看到的仍然是fork时的内存数据,不用担心导出时数据又被更新的情况。

4.3.2 AOF日志

AOF日志记录Redis实例创建以来所有的修改性指令序列。

Redis收到客户端修改指令后,进行检查和处理,如果指令执行成功,则立即将该指令文本存储到AOF日志中。

AOF重写:长时间修改会积累大量的AOF日志,Redis可以开辟一个子进程遍历生成新的AOF指令日志,替代旧的AOF日志,起到日志瘦身的效果。(对同一个key频繁修改,会产生大量AOF日志,但实际上存一项就可以了。)

fsync:Redis定期调用fsync确保AOF日志实实在在写入磁盘,避免突然断电造成内存缓冲数据丢失。

混合持久化:快照 + AOF日志(增量)。提高重启效率,避免重做全部的AOF日志操作。

4.4 管道

Redis客户端重排指令。将读指令连续归在一起,写指令连续归在一起。这样客户端只需要向操作系统网络写缓冲区写一次,读缓冲区读一次即可,服务器端同理。节省了网络读写的次数。

4.5 事务

Redis可以实现begin, commit和rollback的事务功能。

4.6 PubSub

PubSub, Publisher Subscriber.

消息多播,一个Publisher可以向多个Subscriber提供消息。

Subscriber需要先订阅若干个channel,随后,Publisher向channel中发布数据,Redis会将数据提供给订阅该channel的所有Subscriber。

但是,如果subscriber掉线了,过后再上线,就不会再收到掉线时错过的消息了。Redis宕机时,就相当于时没有任何subscriber的情况,会造成所有的消息都被直接丢失的情况。

Redis在5.0版本开始提供新的Stream数据结构,实现了持久化的消息队列。

4.7 节省空间

4.7.1 32bit vs 64bit

32bit编译的Redis比64bit编译的版本节省一半的指针内存消耗。如使用内存不超过4GB,采用32bit即可。

4.7.2 基于ziplist的小对象压缩存储

相较于传统的链表,每个entry作为一个节点,都需要配备prev/next两个指针,ziplist则将多个entry以数组的形式存为一个节点,减少所需的指针空间。

每个ziplits节点存储:

  1. zlbytes,4字节,ziplist占用的字节数;
  2. zltail,4字节,最后一个entry的偏移地址;
  3. zllen,2字节,ziplist的entry数量;
  4. entry数组,存储若干entry;
  5. zlend,1字节,幻数255标记结尾。

4.7.3 基于intset的紧凑整数数组

inset数据结构包含:

  1. encoding,表示value的位宽;
  2. length,表示元素的个数;
  3. value数组,存储若干value。

若整数用uint16表示即可,intset就用uint16;需要升级到uint32或uint64时再动态升级。

4.7.4 内存回收机制

删除key时,内存不会立即全部回收释放交给操作系统,而是会预留部分内存给未来的使用需求。

4.7.5 内存分配算法

Redis有多种内存分配算法:

  1. jemalloc,facebook;
  2. tcmalloc,google。

Redis默认使用jemalloc,该库性能稍好。

通过info memory可以查到当前使用的内存分配库。

5 集群

多个Redis节点组成Redis集群。

5.1 Redis集群与CAP定理

5.1.1 CAP定理

CAP定理指的是分布式系统的一致性(Consistency)、可用性(Availability)和分区容忍性(Partition tolerance)不能三者兼得,最多只能满足两项。

当网络异常时,分布式节点之间无法连接,形成网络分区现象,如果要容忍分区情况,此时有两种选择:

  1. 保证可用性:即允许对每个节点的读和写,这样一来,节点之间就会因为无法立即同步而出现数据不一致的问题,放弃了强一致性,即AP;
  2. 放弃可用性:只允许对每个节点的读,禁止写,这样一来,能保证节点之间的数据一致性,但用户无法更新数据,损失了可用性,即CP[;

也就是说,网络分区发生时,一致性和可用性无法两全。

5.1.2 最终一致性

Redis的主从节点之间异步同步,不能保证严格的强一致性,因此Redis的选择是放弃一致性,转而满足可用性和分区容忍性。

Redis提供的是最终一致性(Eventually consistent),网络断开时,主从节点之间会出现不一致,但网络恢复后,会多策略地尽快同步,最终主从节点保持一致。

5.2 集群同步技术

5.2.1 主从同步与从从同步

主从同步(master-slave sync):主节点与从节点之间同步,主节点把数据复制(replicate)到从节点。

从从同步(slave-slave sync):从节点把数据复制到另一个从节点。

通过引入从从同步,可以降低主节点的同步负担。

5.2.2 增量同步

Redis同步指令流。

Redis主节点把写指令记录在本地的指令缓存(buffer)中,异步地将缓存中地指令同步到从节点,即增量同步。

指令缓存采用的是定长环形数组,因此,如果数组写满了,就会重新从头写入,也就覆盖掉了原有内容。如果网络分区发生时,有节点上产生大量写指令,为了避免指令缓存被覆盖导致写入记录丢失,不能只依赖指令缓存来保存未同步的指令。

5.2.3 快照同步

快照同步:执行bgsave操作,把内存中的数据全部快照存储到硬盘文件中。

增加从节点:增加新的从节点时,通过快照同步为从节点全量加载数据,随后再做增量同步。

快照同步死循环问题:当快照同步太慢,或者指令缓存太小时,就会出现快照同步还没结束,指令缓存就写满的情况。这样一来,指令就不得不直接写入,那快照就过期了,又得重新做一遍快照,而重新做快照可能又太慢,指令缓存又写满了……。为避免死循环,需要设置一个合适的指令缓存大小。

5.2.4 无盘复制

快照同步需要写入磁盘,有不小的文件IO代价。而且Redis执行AOF时需要做fsync,如果此时快照同步,就不得不延后fsync,这样AOF就延后了,指令执行就延后了。

为此,Redis 2.8.18开始支持无盘复制,主节点可以通过socket通信直接把快照发给从节点,避免磁盘上的文件IO代价。

5.2.5 wait指令实现同步复制

Redis的复制本身时异步执行的,因此不具备强一致性。

通过wait指令,可以实现Redis的同步复制,保证系统的(在没有网络分区情况下的)强一致性。

wait可以有限等待,也可以无限等待N个从节点同步完成,再执行后续指令。

如果无限等待时,Redis出现网络分区,那么同步无法完成,就会一直阻塞,导致Redis失去可用性。

5.3 Sentinel:自动主从切换技术

Redis Sentinel集群通常包含3~5个Sentinel节点,保证Sentinel的可用性。

Sentinel集群持续监控主节点和从节点的状态,一旦出现问题,就自动提升一个可用的从节点为主节点,取代故障的不可用的主节点。

Sentinel的具体工作流程:

  1. Client首先向Sentinel请求主节点的地址;
  2. Sentinel将最新的主节点地址返回给Client;
  3. Client访问主节点。

5.4 Codis:中心化集群方案

Codis是Redis集群方案之一,在Codis基础之上,开发出了TiDB。

单个Redis节点如果存储太多数据,会使得快照文件rdb特别大,导致同步起来很耗时,而且全量恢复也变得很慢。

Codis通过把数据分散到众多Redis节点上,来避免每个节点的数据量过大。

Codis对key做哈希,映射到1024个槽位(slots),以此求模,取得数据应该映射到的节点序号。分配完成后,Codis节点会存储槽位与Redis节点的映射关系。

Codis的扩容:可以通过增加Redis节点来扩容集群的容量。

Codis通过mget指令可以从分散的节点上取数据并汇总给用户。

5.5 Cluster:去中心化集群方案

Redis Cluster是去中心化的集群方案,每个节点都是对等的。

Redis Cluster把数据分为16384个槽位(\(2^{14}\)),每个节点负责一部分的槽位。客户端根据key来确定槽位,进而确定目标节点。如果客户端向错误的节点发送请求,该节点会计算key对应的槽位,向客户端发送重定向的响应,告知客户端目标节点。

节点迁移:迁移的最小单位是槽位,流程是从源节点获取内容,然后存到目标节点,最后从源节点中删除内容。

容错:Redis Cluster可以为每个主节点设置若干从节点,自动实现故障时从节点提升为主节点。

可能下线与确定下线:集群节点采用Gossip协议来广播自己的状态。一个节点发现某个节点失联,则进入可能下线(PFail, Possibly Fail)状态。集群中大多数节点都收到该节点失联的消息,则标记该节点为确定下线(Fail)状态。

6 扩展

6.1 Stream:Redis5.0的消息队列

Redis Stream是Redis 5.0中退出的一款新的支持多播的可持久化消息队列,极大地借鉴了Kafka的设计。

Redis Stream通过消息链表将所有加入的消息串起来,每个消息包含唯一ID(timestampInMillis-sequence)和消息内容(形如hash结构的键值对)。

消费组:每个Stream可以挂载多个消费组(Consumer Group),不同消费组互相独立,互不影响,每个消费组都有一个游标last_delivered_id在Stream数组上向前移动,表示当前已经消费到哪条消息了。

消费者:每个消费组中可以包含多个消费者(Consumer),消费者之间为竞争关系,任意一个消费者读取消息都会使消费组的游标last_delivered_id向前移动。

PEL:每个消费者有一个的PEL(Pending Entries List),PEL是一个状态列表pending_ids,记录已经被客户端读取,但尚未收到ACK的消息ID。通过PEL可以确保客户端至少消费了消息1次,而不会在网络传输中途丢失了消息。 客户端重连时,可以根据PEL重新获取一遍接收失败的消息。

分区:Redis没有原生支持分区,分区Stream可以通过在客户端设计哈希策略来实现。Kafka原生支持Partition也是通过客户端的HashStrategy来决定将不同的消息加入不同的分区的。

xgroup create:创建消费组,创建时需要指定从哪个消息ID开始消费。

xadd:加入消息;

xdel:删除消息,只设置标志位,不实际删除消息;

xrange:获取消息列表,自动过滤标记为删除的消息;

xlen:获取消息长度;

del:删除整个消息列表的所有消息。

6.2 info:状态诊断

info指令可查询:

  1. server:服务器信息;
  2. clients:客户端信息;
  3. memory:运行内存统计数据;
  4. persistence:持久化信息;
  5. stats:通用统计数据;
  6. replication:主从复制;
  7. cpu:cpu使用情况;
  8. cluster:集群信息;
  9. keyspace:键值对统计信息。

查询方式如:

Redis内:

1
> info memory

Redis外:

1
redis-cli info memory

6.3 Redlock分布式锁

Sentinel集群中,主节点挂掉后,从节点取而代之,但主节点的分布式锁没有同步到从节点,新升任主节点的从节点中没有这个分布式锁,就会造成不安全性。

对多个对等的Redis实例,Redlock基于“大多数机制”,加锁时,向过半的节点发送set指令,过半的节点加锁成功,则本次加锁成功;解锁时,向所有节点发送del指令。因为Redlock需要向多个节点进行读写,考虑出错重试、时钟漂移等问题,相对单实例Redis的性能会下降一点。

6.4 过期策略

设置了expire时间的key放在一个独立的字典里。

Redis的过期策略既有定期扫描,也有惰性策略。

定期扫描,Redis默认每秒10次过期扫描,扫描算法为:

  1. 从过期字典中随机选出20个key;
  2. 删除这20个key中已经过期的key;
  3. 如果过期key的比例超过1/4,则重复步骤1.。

为避免循环过度造成线程卡死,默认设置扫描时间上限为25ms。这个25ms的依据是,1秒10次,每次25ms,总共最多占用250ms,即1/4的CPU时间。Redis实际上限制的是CPU时间,避免过期扫描耗费超过1/4的CPU时间。

如果大量key同时过期,Redis就会循环扫描字典,删除key,直到过期字典中的过期key比例变低。当过期的key数量很多的时候,扫描时间是完全可能撞到25ms的上限的。再加上内存回收的代价,就会产生比较多的CPU消耗。如果此时新来的请求设置的超时时间很短,例如10ms,就会导致刚设置数据,就开始扫描,等25ms扫描完,才来得及处理客户端的读取操作时,key早就过期了。客户端就发现自己刚设置的值,立即去修改就会超时过期,实际上是因为Redis的过期策略在间隔中消耗了时间。

为了避免以上问题,一方面,考虑到过期策略扫描耗时,过期时间不宜设置的过短;另一方面,避免大量key同时过期,哪怕对统一的过期时间加上一个随机量也好。

惰性策略:访问key时对key的过期时间进行检查,如果过期了就删除。

从节点不会主动执行过期策略,主节点删除节点并同步del给从节点,从节点收到后写入AOF,跟着主节点照做就是。不过因为同步是异步的,所以主从节点之间强一致性无法保证。

6.5 内存淘汰算法

Redis不允许发生swap,因为会造成性能急剧下降。

当Redis实际内存超过maxmemory时,有几种maxmemory-policy:

  1. noeviction:可读不可写;
  2. volatile-lru:淘汰过期集合中最少使用的(LRU);
  3. volatile-ttl:淘汰过期集合中剩余寿命TTL最小的key;
  4. volatile-random:淘汰过期集合中随机key;
  5. allkeys-lru:全体key中淘汰LRU;
  6. allkeys-random:全体key中淘汰随机的key。

6.6 懒惰删除

del直接删除,通常非常快,但对象非常大时,删除操作会造成单线程卡顿。

Redis 4.0引入的unlink可以解决卡顿问题,unlink卸下待删除对象,然后交给后台线程去异步地回收内存。

Redis 4.0为flushdb和flushall都引入了异步化,加上async选项即可,如:flushall async

异步删除借助异步队列实现,MainThread通过submitTask将待删除对象放入ConcurrentQueue,懒惰删除线程LazyFreeThread从中fetchTask并执行异步删除。

Redis的AOF Sync需要将AOF日志同步到磁盘,需要调用sync函数,因为sync比较耗时,因此采用异步线程去调用,该异步线程也有自己的任务队列,存放AOF Sync任务。

Redis在del和flush以外,也会在key过期、LRU淘汰、rename指令执行时回收内存。节点接受全量同步rdb文件后也会清空内存以载入数据。这些删除场景涉及额外的选项:

  1. slave-lazy-flush:从节点接受rdb文件后的flush操作;
  2. lazyfree-lazy-eviction:内存达到maxmemory时进行淘汰;
  3. lazyfree-lazy-expire-key:过期删除;
  4. lazyfree-lazy-server-del rename:指令删除destKey。

6.7 Jedis

Jedis是Java的Redis开源客户端。

因为Jedis对象不是线程安全的,所以使用Jedis是从Jedis连接池JedisPool中取出一个Jedis对象归该线程独占,用完了再还给连接池。

Jedis默认没有重试机制,网络抖动造成连接断开,再发送指令就会报错。需要手动捕获JedisConnectionException进行重连处理。

6.8 Redis安全

6.8.1 指令安全

rename-command既可以将已有命令更名,也可以更名为空字符串,从而屏蔽该命令被调用。

6.8.2 端口安全

bind指令规定监听的IP地址。

requirepass设置密码访问限制,从节点masterauth设置于主节点同步连接密码。

6.8.3 脚本安全

避免UGC的Lua脚本。

避免以root权限启动Redis。

6.8.4 SSL代理

使用SSH保护Redis连接。

使用官方推荐的spiped工具对SSH通道进行二次加密。spiped是一款加密代理软件。