Redis知识图谱
2021-12-02 16:31:20 15 举报
AI智能生成
redis相关知识点
作者其他创作
大纲/内容
数据类型
string
SDS, Simple Dynamic String
capacity
len
content:以字节\0结尾
扩容:< 1M 之前,加倍扩容; > 1M后,每次扩容1M
内部编码
raw
int
embstr
常用命令
set k v
setnx k v: 不存在才设置(add)
set k v xx: key存在 才设置(update)
setex k 60 v: 生存时间
getset: 返回旧值
append:
strlen:
incr / decr / incrby / decrby
mget / mset
del
hash
dict -> dictht[2] -> disctEntry
扩容时机:元素的个数等于第一维数组的长度;
bgsave时不扩容,除非达到dict_force_resize_ratio
扩容:扩容 2 倍
缩容:元素个数低于数组长度10%时
渐进式rehash
内部编码
hashtable
ziplist
ziplist: 元素个数较小时,用ziplist节约空间
配置
hash-max-ziplist-entries
hash-max-ziplist-value
常用命令
hget / hset name k v
hsetnx name k v
hdel
hincrby / hincrbyfloat
hgetall name
hvals name: 返回所有value
hkeys name: 返回所有key
list
内部编码
早期:元素少时用 ziplist,元素多时用 linkedlist
后期:quicklist
常用命令
lpush / rpop
lpush list-name c b a
rpush / lpop
linsert key before|after v new-v
lset key index v
llen
lrem key count v
count = 0: 删除所有v
count > 0: 从左到右删除count个
count < 0: 从右到左删除count个
ltrim key start end: 修剪
lrange key start end: 范围
blop / brpop key timeout: 阻塞pop
应用
lpush + lpop --> stack
lpush + rpop --> queue
lpush + ltrim --> capped collection
lpush + brpop --> MQ
set
内部编码
hashtable
IntSet
当元素都是整数并且个数较小时,使用 intset 来存储
常用命令
sadd key e
srem key e
scard: 集合大小
sismember: 判断存在
srandmember: 随机取元素
smembers: 所有元素,慎用
sdiff / sinter / sunion
应用
sadd --> tagging
spop / srandmember --> random item
sadd + sinter --> social graph
zset
hash: value -> score
内部编码
skiplist
二分查找
ziplist
元素个数较小时,用ziplist节约空间
常用命令
zadd key score e
zscore key e
zrem key e
zremrangebyrank key start end
zremrangebyscore key min-score max-score
zincrby key score e
zcard
zrange key start end [withscores]
zrevrange
zrangbyscore key min-score max-score
zcount key min-score max-score
zinterstore / zunionstore
原理
通讯协议:RESP, Redis Serialization Protocal
多路复用
指令队列
响应队列
epoll事件轮询API
pipeline
节省网络开销
注意每次pipeline携带的数据量
注意m操作与pipeline的区别:原子 vs 非原子
pipeline每次只能作用在一个redis节点上
事务
multi/exec/discard
隔离性
不能保证原子性
结合pipeline
watch
过期策略
惰性策略
定时扫描
实践:过期时间随机化
内部存储结构
redisObject
type 数据类型
encoding 编码方式
ptr 数据指针
vm 虚拟内存
其他
持久化
RDB
触发条件
SAVE
同步
阻塞客户端命令
不消耗额外内存
BGSAVE
异步
不阻塞客户端命令
fork子进程,消耗内存
配置文件: save seconds changes
BGSAVE
不建议打开
SHUTDOWN 命令时
从节点SYNC时 (=BGSAVE)
原理
fork子进程生成快照
内存越大,耗时越长
info: latest_fork_usec
Copy On Write
缺点
不可控、会丢失数据
耗时 O(n)、耗性能、耗内存
AOF
触发条件
always
every second
no
原理
写命令刷新到缓冲区
每条命令 fsync 到硬盘AOF文件
AOF重写
bgrewriteaof 命令
类似bgsave, fork子进程重新生成AOF
配置文件
auto-aof-rewrite-min-size
auto-aof-rerwrite-percentage
推荐配置
动态应用配置
config set appendonly yes
config rewrite
AOF追加阻塞
对比上次fsync时间,>2s则阻塞
info: aof_delayed_fsync (累计值)
建议
混合
持久化操作主要在从节点进行
集群
主从
配置
slaveof
slave-read-only yes
查看主从状态:info replication
主从复制流程
全量复制
1.【s -> m】psync runId offset
2.【m -> s】+FULLRESYNC {runId} {offset}
3.【s】save masterInfo
4.【m】bgsave / write repl_back_buffer
5.【m -> s】send RDB
6.【m -> s】send buffer
7.【s】flush old data
8.【s】load RDB
部分复制
1.【s -> m】psync runId offset
2.【m -> s】CONTINUE
3.【m -> s】send partial data
问题
开销大
【m】bgsave时间开销
【m】RDB网络传输开销
【s】清空数据时间开销
【s】加载RDB时间开销
【s】可能的AOF重写时间
读写分离问题
复制数据延迟
读到过期数据
从节点故障
主从配置不一致
maxmemory配置不一致
可能丢失数据
数据结构优化参数不一致
内存不一致
规避全量复制
第一次全量复制
不可避免
优化:小主节点(小分片),低峰
节点runId不匹配导致复制
主节点重启后runId变化
优化:故障转移(哨兵、集群)
复制积压缓冲区不足
网络中断后无法进行部分复制
优化:rel_backlog_size(默认1m)
规避复制风暴
主节点重启,多个从节点复制
优化:更换复制拓扑
sentinel
原理
三个定时任务
每1秒,sentinel对其他sentinel和redis执行ping
心跳检测
失败判定依据
每2秒,sentinel通过master的channel交换信息
master频道:__sentinel__:hello
交换对节点的看法、以及自身信息
每10秒,sentinel对m/s执行info
发现slave节点
确认主从关系
故障转移流程
sentinel 集群可看成是一个 ZooKeeper 集群
【1. 故障发现】多个sentinel发现并确认master有问题
主观下线
客观下线
【2. 选主】选举出一个sentinel作为领导
原因:只有一个sentinel节点完成故障转移
实现:通过sentinel is-master-down-by-addr命令竞争领导者
【3. 选master】选出一个slave作为master, 并通知其余slave
选新 master 的原则
选slave-priority最高的
选复制偏移量最大的
选runId最小的
对这个slave执行slaveof no one
【4. 通知】通知客户端主从变化
【5. 老master】等待老的master复活成为新master的slave
sentinel会保持对其关注
客户端流程
【0. sentinel集合】 预先知道sentinel节点集合、masterName
【1. 获取sentinel】遍历sentinel节点,获取一个可用节点
【2. 获取master节点】get-master-addr-by-name masterName
【3. role replication】获取master节点角色信息
【4. 变动通知】当节点有变动,sentinel会通知给客户端 (发布订阅)
JedisSentinelPool -> MasterListener --> sub "+switch-master"
sentinel是配置中心,而非代理!
消息丢失
min-slaves-to-write 1
min-slaves-max-lag 10
运维
上下线节点
下线主节点
sentinel failover
下线从节点
考虑是否做清理、考虑读写分离
上线主节点
sentinel failover进行替换
上线从节点
slaveof
上线sentinel
参考其他sentinel节点启动
高可用读写分离
client关注slave节点资源池
关注三个消息
+switch-master: 从节点晋升
+convert-to-slave: 切换为从节点
+sdown: 主观下线
codis
用zookeeper存储槽位关系
代价
不支持事务
rename 操作也很危险
为了支持扩容,单个 key 对应的 value 不宜过大
网络开销更大
需要运维zk
cluster
创建
原生
配置文件:cluster-enabled yes
启动: redis-server *.conf
gossip通讯:cluster meet ip port
分配槽(仅对master):cluster addslots {0...5461}
配置从节点:cluster replicate node-id
脚本
安装ruby
安装rubygem redis
安装redis-trib.rb
验证
cluster nodes
cluster info
cluster slot
redis-trib.rb info ip:port
特性
复制
主从复制 (异步):SYNC snapshot + backlog队列
快照同步
增量同步
无盘复制
wait 指令
高可用
分片
slots
16384个
槽位信息存储于每个节点中
Rax
每个节点通过meet命令交换槽位信息
定位:crc16(key) % 16384
计算槽位
cluster keyslot k
扩展性:迁移slot (同步)
dump
restore
remove
原理
伸缩
扩容
准备新节点
加入集群
meet
redis-trib.rb add-node
迁移槽和数据
手工
1_对目标节点:cluster setslot {slot} importing {sourceNodeId}
2_对源节点:cluster setslot {slot} migrating {targetNodeId}
3_对源节点循环执行:cluster getkeysinslot {slot} {count},每次获取count个键
4_对源节点循环执行:migrate {targetIp} {targetPort} key 0 {timeout}
5_对所有主节点:cluster setslot {slot} node {targetNodeId}
pipeline migrate
redis-trib.rb reshard
收缩
迁移槽
忘记节点
cluster forget {downNodeId}
redis-trib.rb del-node
关闭节点
迁移slot过程中如何同时提供服务?--> ask
0.先尝试访问源节点
1.源节点返回ASK转向
2.向新节点发送asking命令
3.向新节点发送命令
故障转移
故障发现
通过ping/pong发现故障
主观下线
客观下线
当半数以上主节点都标记其为pfail
故障恢复
资格检查
准备选举时间
选举投票
替换主节点
一致性: 保证朝着epoch值更大的信息收敛
客户端
客户端路由
moved
1.向任意节点发送命令
2.节点计算槽和对应节点
3.如果指向自身,则执行命令并返回结果
4.如果不指向自身,则回复-moved (moved slot ip:port)
5.客户端重定向发送命令
tips
redis-cli -c 会自动跳转到新节点
moved vs. ask
都是客户端重定向
moved: 表示slot确实不在当前节点(或已确定迁移)
ask: 表示slot在迁移中
批量操作
问题:mget/mset必须在同一个槽
实现
串行 mget
串行IO
客户端先做聚合,crc32 -> node,然后串行pipeline
并行IO
客户端先做聚合,然后并行pipeline
hash_tag
运维
集群完整性
cluster-require-full-coverage=yes
要求16384个槽全部可用
节点故障或故障转移时会不可用:(error) CLUSTERDOWN
大多数业务无法容忍
带宽消耗
来源
消息发送频率
消息数据量
节点部署的机器规模
优化
避免“大”集群
cluster-node-timeout: 带宽和故障转移速度的均衡
尽量均匀分配到多机器上
集群状态下的pub/sub
publish在集群中每个节点广播,加重带宽
解决:单独用一套sentinel
倾斜
数据倾斜
节点和槽分配不均
redis-trib.rb info 查看节点、槽、键值分布
redis-trib.rb rebalance 重新均衡(慎用)
不同槽对应键值数量差异较大
CRC16一般比较均匀
可能存在hash_tag
cluster countkeyinslot {slot} 查看槽对应键值数
包含bigkey
从节点执行 redis-cli --bigkeys
优化数据结构
内存配置不一致
请求倾斜
原因:热点key、bigkey
优化
避免bigkey
热键不要用hash_tag
一致性不高时,可用本地缓存,MQ
热点key解决思路
客户端统计
实现简单
内存泄露隐患,只能统计单个客户端
代理统计
增加代理端开发部署成本
服务端统计(monitor)
monitor本身问题,只能短时间使用
只能统计单个redis节点
机器段统计(抓取tcp)
无侵入
增加了机器部署成本
读写分离
只读连接
从节点不接受任何读写请求
会重定向到负责槽的主节点(moved)
readonly命令
读写分离客户端会非常复杂
共性问题:复制延迟、过期数据、从节点故障
cluster slaves {nodeId} 获取从节点列表
数据迁移
redis-trib.rb import
只能 单机 to 集群
不支持在线迁移
不支持断点续传
单线程迁移,影响速度
在线迁移
唯品会 redis-migrate-tool
豌豆荚 redis-port
缺点
key批量操作支持有限
mget/mset 必须在同一个slot
key事务和lua支持有限
操作的key必须在同一个slot
key是数据分区最小粒度
bigkey无法分区
分支主题
复制只支持一层
无法树形复制
应用
分布式锁
命令
setnx + expire
set xx ex 5 nx
集群问题
Redlock算法
延时队列
lpush / rpush
rpop / lpop -> brpop / blpop
位图
type: string, 最大512M
命令
setbit k offset v
getbit
bitcount k [start end] 统计
bitop op destKey key1 key2 位运算
bitpos k targetBit [start] [end] 查找
bitfield操作多个位
HyperLogLog
极小空间完成独立数量统计
type: string
缺点
有错误率 0.81%
不能返回单条元素
命令
添加:pfadd key e1 e2...
计数:pfcount key
合并:pfmerge destKey sourceKey1 sourceKey2
布隆过滤器
操作
bf.exists / bf.mexists
bf.add / bf.madd
原理
参数
m 个二进制向量
n 个预备数据
k 个哈希函数
构建
n个预备数据,分别进行k个哈希,得出offset,将相应位置的二进制向量置为1
判断
进行k个哈希,得出offset,如果全为1,则判断存在
误差率
与 k (哈希函数)个数成反比
与 n (预备数据)个数成正比
与 m (二进制向量)长度成反比
简单限流: zset实现滑动时间窗口
key: clientId-actionId
value: ms
score: ms
漏斗限流: redis-cell模块
cl.throttle key capacity count period 1
GeoHash
用于地理经纬度计算
type: zset
命令
添加:geoadd key longitude latitude member
获取:geopos key member
距离:geodist key member1 member2 [unit]
范围:georadius/georadiusbymember
删除:zrem key member
geohash
搜索key
keys
scan
Stream
PubSub
publish channel-name msg
subscribe/unsubscribe channel-name
运维
eviction
LRU: Least Recently Used
近似LRU
LFU: Least Frequently Used
内存
内存回收
无法保证立即回收已经删除的 key 的内存
flushdb
内存查看:info memory
used_memory
redis自身内存
对象内存
优化
key: 不要过长
value: ziplist / intset 等优化方式
内存碎片
缓冲内存
客户端缓冲区
输出缓冲区
普通客户端
normal 0 0 0
默认无限制,注意防止大命令或 monitor:可能导致内存占用超大!!
找到monitor客户端:client list | grep -v "omem=0"
slave 客户端
slave 256mb 64mb 60
可能阻塞:主从延迟高时,从节点过多时
pubsub 客户端
pubsub 32mb 8mb 60
可能阻塞:生产大于消费时
输入缓冲区
最大 1GB
复制缓冲区
repl_back_buffer
默认1M,建议调大 例如100M
防止网络抖动时出现slave全量复制
AOF 缓冲区
无限制
lua内存
used_memory_rss
从操作系统角度看redis进程占用的总物理内存
mem_fragmentation_ratio
内存碎片 used_memory_rss / used_memory > 1
内存碎片必然存在
优化
避免频繁更新操作:append, setrange
安全重启
mem_allocator
子进程内存消耗
场景
bgsave
bgrewriteaof
优化
去掉 THP 特性
观察写入量
overcommit_memory = 1
内存管理
设置内存上限
一般预留 30%
config set maxmemory 6GB
config rewrite
动态调整内存上限
内存回收策略
删除过期键值
惰性删除
访问key
expired dict
del key
定时删除
每秒运行 10 次,采样删除
慢模式:随机检查 20 个key
如果超过25%的key过期
循环执行:快模式??
否则退出
内存溢出控制策略
配置
maxmemory-policy
策略
noeviction
默认策略,拒绝写入操作
volatile-lru
LRU算法删除 有expire的key
allkeys-lru
LRU算法删除所有key
allkeys-random
随机删除所有key
volatile-random
随机删除过期key
volatile-ttl
删除最近将要过期key
序列化与压缩
拒绝Java原生
推荐protobuf, kryo, snappy
保护
spiped: SSL代理
设置密码
server: requirepass / masterauth
client: auth命令 、 -a参数
rename-command flushall ""
不支持config set动态配置
bind 内网IP
懒惰删除
del -> unlink
flushdb -> flushdb async
慢查询
配置
slowlog-max-len
先进先出队列、固定长度、内存
默认10ms, 建议1ms
slowlog-log-slower-than
建议1000
命令
slowlog get [n]
slowlog len
slowlog reset
开发规范
kv设计
key设计
可读性、可管理型
简洁性
string长度会影响encoding
embstr
int
raw
通过 `object encoding k` 验证
排除特殊字符
value设计
拒绝bigkey
最佳实践
string < 10K
hash,list,set元素不超过5000
bigkey的危害
网络阻塞
redis阻塞
集群节点数据不均衡
频繁序列化
bigkey的发现
应用异常
JedisConnectionException
read time out
could not get a resource from the pool
redis-cli --bigkeys
scan + debug object k
主动报警:网络流量监控,客户端监控
内核热点key问题优化
bigkey删除
阻塞(注意隐性删除,如过期、rename)
unlink命令 (lazy delete, 4.0之后)
big hash渐进删除:hscan + hdel
选择合适的数据结构
多个string vs. 一个hash
分段hash
节省内存、但编程复杂
计算网站独立用户数
set
bitmap
hyperLogLog
过期设计
object idle time: 查找垃圾kv
过期时间不宜集中,避免缓存穿透和雪崩
命令使用技巧
O(N)命令要关注N数量
hgetall, lrange, smembers, zrange, sinter
更优:hscan, sscan, zscan
禁用危险命令
keys, flushall, flushdb
手段:rename机制
不推荐select多数据库
客户端支持差
实际还是单线程
不推荐事务功能
一次事务key必须在同一个slot
不支持回滚
monitor命令不要长时间使用
连接池
连接数
maxTotal
如何预估
业务希望的 redis 并发量
客户端执行命令时间
redis 资源:应用个数 * maxTotal < redis最大连接数
maxIdle
minIdle
等待
blockWhenExhausted
maxWaitMillis
有效性检测
testOnBorrow
testOnReturn
监控
jmxEnabled
空闲资源监测
testWhileIdle
timeBetweenEvictionRunsMillis
numTestsPerEvictionRun
0 条评论
下一页