Redis
2021-01-11 11:53:19 0 举报
AI智能生成
redis
作者其他创作
大纲/内容
数据结构
String
内部编码
int
8个字节的长整型
embstr
小于等于39个字节的字符串
raw
大于39个字节的字符串
优化
基于合适的长度选择合适的编码
object encoding key
相当于Java数据结构
HashMap
常用命令
SET key value
设置指定 key 的值
GET key
获取指定 key 的值
MSET key value [key value …]
批量设置字符串键值
MGET key1 [key2…]
获取所有(一个或多个)给定 key 的值
DEL key [key …]
删除指定key(一个或多个)
EXPIRE key seconds
设置一个key的过期时间(秒)
PEXPIRE key milliseconds
设置一个key的过期时间(毫秒)
INCRBY key increment
对数字key进行{increment}的增加
DECRBY key decrement
对数字key进行{decrement}的减少
INCR key
对数字key自增1
DECR key
对数字key自减1
适用场景
简单的键值对存储
对数字进行增递减操作
hash
内部编码
ziplist
hashtable
相当于Java中的数据结构
HashMap<String, HashMap<String, String>> h = new HashMap<String, HashMap<String, String>> ()
常用命令
设置值
hset key field val
hset user:1 name tom
hmset key field value [field value ..
获取值
获取单个field
hget key field
hget user:1 name
批量获取field
hmget key field [field ...]
获取所有field
hkeys key
获取所有value
hvals key
获取所有的field-value
hgetall key
但 key较多存在阻塞redis可能
删除值
hdel key field [field ...]
hdel user:1 name
判断field是否存在
hexists key fie
hexists user:1 name
0
不存在
1
存在
list
特点
栈或队列
有序
元素可重复
内部编码
ziplist
linkedlist
quicklist
结合了ziplist和linkedlist两者的优势
常用命令
插入
从右边插入元素
rpush key value [value ...]
rpush listkey c b a
从左边插入元素
lpush key value [value ...]
向某个元素前或者后插入元素
linsert key before|after pivot value
linsert listkey before b java
查询
从左到右获取列表的所有元素
lrange0-1
获取指定范围内的元素列表
命令
lrange key start end
特点
索引下标从左到右分别是0到N-1
从右到左分别是-1到-N
lrange中的end选项包含了自身
获取列表指定索引下标的元素
lindex key index
获取列表长度
llen key
删除
从列表左侧弹出元素
lpop key
t lpop listkey
从列表右侧弹出
rpop key
删除指定元素
lrem key count value
count>0,从左到右,删除最多count个元素
count<0,从右到左,删除最多count绝对值个元素
count=0,删除所有
按照索引范围修剪列表
ltrim key start end
ltrim listkey 1 3
保留2-4
阻塞弹出
命令
blpop key [key ...] timeout
brpop key [key ...] timeout
列表为空
timeout=0
客户端一直等待
期间添加数据
立刻返回
timeout=3
客户端等待三秒返回
修改
修改指定索引下标的元素
lset key index newValue
使用场景
简单的分布式队列服务
set
zset
HyperLogLog
适用场景
用户签到
Bitmap
GEO
命名规范
表:主键:主键值:列名
student:id:1:name
数据持久化机制
AOF(Append Only File)
执行方式
先执行命令
再记录日志命令
优缺点
优点
不用检查命令正确性
在命令执行后记录,所以不会阻塞写操作
缺点
执行命令完redis宕机,数据丢失
为下一条命令造成阻塞
文件过大,追加命令效率变低
解决办法
重写机制,操作多条key的命令恢复最终转换为一条命令
方式
使用两个日志保证重写的数据不丢失
一个日志拷贝redis内存中的数据
一个日志保证新的数据写入不丢失
bgrewriteaof子进程
采用额外线程进行数据重写,不会阻塞主线程
缺点
fork子进程,在fork的瞬间一定会阻塞主线程
恢复数据缓慢
回写策略
配置项
appendfsync
Always
同步回写
每个写命令执行完,立马同步将日志写回磁盘
优点
可靠性高,数据基本不丢失
缺点
每个命令都要落盘,性能影响较大
Everysec
每秒回写
优点
性能适中
缺点
宕机时丢失1秒内的数据
No
每个命令执行完后只是把日志写入到AOF文件的内存缓存区,何时写入磁盘由操作系统自己决定
优点
性能好
宕机时丢失数据多
RDB(Redis DataBase)
全量快照
生成RDB文件命令
save
在主线程执行,会阻塞主线程
bgsave
创建一个子线程,专门用于写入RDB文件
Redis默认配置
在bgsave时主线程写操作也会生成该数据副本,然后bgsave线程同时把这个副本数据写入RDB文件中
机制选择
数据不丢失机制
AOF RDB混合使用
允许分钟级别丢失
RDB
使用AOF优先使用everysec配置,因为他在可靠性和性能之间去了一个平衡
集群
主从
采用读写分离
读可以在任何实例
优点
减少锁,实例间的协调
数据同步
同步方式
RDB
数据写入到master,然后同步给其他slaver
原因
RDB文件进过压缩的二进制文件,文件小
RDB文件执行的更快
缺点
从库太多fork子进程生成RDB会阻塞主线程
生成RDB和传输RDB都非常耗时
解决方式
采用 主-从-从架构
网络导致断线问题
基于repl_backlog_buffer缓存区实现增量重读
主库将收到的写入操作写入replication buffer,同时将命令写入repl_backlog_buffer
repl_backlog_buffer
主库记录自己写到的位置
从库记录自己已经读到的位置
环形缓存区,如果从库读取数据过慢,可能导致从库还没读取到操作然后被主库覆盖,导致主从数据不一致
解决方式
调大repl_backlog_size
计算公式: repl_backlog_size = (主库写入命令速度*操作大小-主从库间网络传输命令速度*操作大小)*2
例子
主库每秒写入 2000个操作
每个擦操作大小为 2kb
网络每秒能传输1000个操作
repl_backlog_size
4MB
主库高可用
哨兵机制
作用
监控
心跳检测
PING命令
主观下线
单哨兵判断下线
客观下线
N/2+1判断下线
选主
规则
必要条件
从库在线
之前的网络连接状态
断连超过阈值也不行
阈值设置
down-after-millisecnds*10
down-after-millisecnds表示断连最大时间
打分
1. 通过 slave-priority设置不同的优先级
打分相应变高
2.同步数据最接近主库
从库的slave_repl_offset最接近master_repl_offset
3.id号小的从库得分高
通知
减少哨兵主观判断失误
哨兵集群,多哨兵判断
哨兵见的通信
哨兵配置
主库ip和端口
通信
基于redis的 pub/sub
发布自己的ip和端口让其他哨兵知道
topic
__sentinel__:hello
获取从库信息
INFO命令
主库接受到哨兵的INFO命令后返回给从库列表信息给哨兵
监控哨兵的各个事件
topic
主库下线事件
+sdown
实例进入主观下线状态
-sdown
实例退出主观下线状态
+odown
实例进入客观下线状态
-odown
实例退出客观下线状态
slave重新配置事件
+slave-reconf-sent
哨兵发送SLAVEOF命令重新配置从库
+slave-reconf-inprog
slave配置了新主库,但尚未进行同步
+slave-reconf-done
从库配置了新主库,且和新主库完成了同步
由哪个哨兵进执行主从切换
Leader选举
成为Leader条件
拿到半数以上的赞成票
拿到的票数大于等于哨兵配置文件中的quorum值
主从切换读写
读
如果客户端使用了读写分离,那么读请求可以在从库上正常执行,不会受到影响
写
由于此时主库已经挂了,而且哨兵还没有选出新的主库,所以在这期间写请求会失败,
失败持续的时间 = 哨兵切换主从的时间 + 客户端感知到新主库 的时间
失败持续的时间 = 哨兵切换主从的时间 + 客户端感知到新主库 的时间
主库挂了
读操作可以继续路由到从节点
写操作需要等待新的master选举出来
切片集群
优点
保证每个节点数据不会太多,从而减少RDB fork进程时间
保存更多数据
横向扩展
添加Redis的实例个数
缺点
随着内存增加导致RDB时间变成
优点
实施简单直接
纵向扩展
升级单个Redis实例的资源配置
Redis Cluster
数据所在实例
哈希槽(Hash Slot)
一个切片共16384个哈希槽
定位
1.根据key按CRC16算法计算一个16bit的值
2. 由这个16bit的值对16384取模
3. cluster create会自动将槽为平均分布在集群实例上
每个实例槽为: 16384/N
也可手动指定
手动需要把16384个槽位分配完,否则集群无法正常工作
变化
新增或删除实例
重新分配哈希槽
客户端定位数据
实例相互连接后相互发送信息,让每个实例获取所有哈希槽的映射关系
客户端获取哈希槽信息缓存到本地,然后通过请求key计算发送到哪个实例
问题
新增实例客户端无法感知
解决方案
重定向机制
客户端给一个实例发送读写操作,实例没有相应的数据,客户端给新实例发送操作命令
codis集群
主从数据一致性
数据同步
异步复制
问题
无法保证数据强一致性
解决方式
1. 保证良好的网络
2. 使用应用程序监控从库复制进度,一旦从库复制进度超过阈值,不让客户端连接从库
脑裂
产生原因
主库假死,从库切换主库中途,主库获取到客户端数据丢失
问题排查
1.主库数据还未同步到从库发生主从切换
通过判断master_repl_offset和slave_repl_offset的差值
发现保持一致,证明主从数据一致
2. 排查客户端操作日志,发现脑裂
主从切换一段时间内,仍然有客户端与原从库进行通信
解决方式
配置
min-slaves-to-write
主库能进行数据同步的最少从库数量
设置公式:K/2+1
min-slaves-max-lag
主从库间数据复制,从库给主库返回ACK消息的最大延迟
建议设置时间:10~20s
缓存一致性
一致性
缓存中有数据,缓存的数据必须和数据库中的值相同
缓存中没有数据,数据库的值必须是最新的
回写策略
同步直写策略
写缓存时,也同步写到数据库,缓存和数据库中的数据一致
要想缓存和数据库一致必须使用这种策略
注意事项
需要保证数据库写入和Reids缓存更新操作原子性
异步写回策略
写缓存时不同步到数据库,等到数据从缓存中淘汰时,再写回数据库
缺点
redis宕机,数据库没有最新数据
数据操作
新增数据
直接写入数据库,不需要删除缓存数据
删除更新数据
1.先删除缓存,再更新数据库
导致问题
A线程删除先删除缓存,还未更新数据库
B线程同时读取到旧数据刷新旧数据到缓存中
缓存污染
解决方式
延迟双删
A更新完数据后sleep一段时间再执行删除缓存操作
2.先更新数据库在删除缓存
导致问题
A更新数据还未来得及更新缓存
B在数据更新缓存未更新时读取到旧缓存
数据更新成功,缓存删除失败
解决方式
重试缓存删除
缓存异常
缓存雪崩
1 大量缓存同一时间失效
解决方式
缓存过期时间添加随机数
添加服务降级,熔断
2. redis宕机
redis集群高可用
缓存击穿
访问频繁的热点数据突然失效,导致大量请求到数据库
解决方式
热点数据不设置过期时间
缓存穿透
访问的数据即不在缓存中,也不在数据库,导致大量请求给数据库造成压力
产生原因
数据误删除
恶意攻击
解决方式
缓存空值或缺省值
布隆过滤器
缓存删除策略
惰性删除
当一个数据过期时间到了,并不会立即删除数据,而是等待读写请求过来对数据进行检查,检查数据过期再删除
优点
减少删除操作对CPU资源的使用,不需要浪费时间对过期数据进行检查和删除
导致问题
过期缓存长时间保存在内存中,占用内存
3.2之前版本从库不会删除过期缓存,可能导致主从不一致,3.2版本后从库也不会删除过期缓存,但会查询返回null
定期删除
每隔一段时间(默认100ms),随机选出一定数据,检测是否过期,并把过期数据及时删除
分布式锁
单节点
集群
0 条评论
下一页