技术分享之redis经验之谈

前言:

前些日子完成了整个交易服务的缓存系统的构建,颇有些心得。就在公司组织了一场关于redis的技术分享,算是个我这几年对redis的经验总结。

这次主要分享redis的数据结构原理及缓存的方案。虽然公司内同事工作中时常用到redis,但对redis的底层结构及社区里的redis方案了解有限。😅 相信这次的分享对大家都有所帮忙,对我的kpi打分也有帮忙。😁

地址:

下面是ppt中的一些内容截图,另外完整的pdf已经推到github里,有兴趣的可以看下。https://github.com/rfyiamcool/share_ppt/blob/master/redis_qa.pdf

redis 底层数据结构
redis 底层数据结构
redis 底层数据结构
redis sds简单动态字符串
redis ziplist压缩列表
redis dict
redis inset
redis quicklist
redis skiptable
redis 模块
redis cache 缓存一致性
redis 缓存一致性
redis 缓存 雪崩
redis缓存方案
redis lock
维护经验
redis6 介绍

文本 (for seo):

// xiaorui.cc

Redis经验之谈
xiaorui.cc
github.com/rfyiamcool

menu
数据结构
底层数据结构
功能点
使用经验
高级场景
Redis6

Redis数据结构
String 字符串
key value
List 列表
timeline
未读消息
简单消息队列
Hash 字典
一对多的关系映射

Set 集合
好友的交集, 并集, 差集
去重
Sorted Set 有序集合
排行榜
排序
Stream 流
类似kafka模型的消息队列
消费组, ack, 偏移量 …

Redis数据结构
bitmap 位图
用户签到
用户在线状态

geo 地理位置
查看附近的人

状态位标记

外卖

…

…

pubsub 发布订阅
不靠谱的发布订阅

hyperloglog 基数统计
每日访问的ip数统计
…

Redis底层数据结构
SDS

Dict

LinkedList

Inset

Ziplist

SkipTable

Quicklist

RadixTree
…

Redis数据结构组成
String
sds
Hash
ziplist
dict
Set
inset
dict

List
ziplist
quicklist
Sorted Set
hash + skiptable
Stream
radix-tree

key的规范
每个业务单独的database ( cluster不⽀持 )
加⼊项⽬的前缀
⼀级key不要超过千万
尽量都加⼊TTL
使⽤ { hashtag } 来绑定亲和性

Value的规范
选择合适的数据结构
长字符压缩存取 (snappy, msgpack, more…)
避免big key ( 删除和迁移时阻塞)
避免hot key (单点性能)

ziplist更省内存
hash (order_id_1000)

hash (order_id)

oid_1000

oid_1000

oid_1002

oid_1002

oid_1333

oid_1333

hash (order_id_2000)

oid_2111

oid_2111

oid_2333

oid_2333

hash-max-ziplist-entries = 1000
hash-max-ziplist-value = 128

省内存
连续内存, 紧凑的编码, 减少了碎⽚, 减少了指针引⽤
ziplist⽀持的数据结构
hash-max-ziplist-entries && hash-max-ziplist-value
list-max-ziplist-size
zset-max-ziplist-entries && zset-max-ziplist-value

持久化
RDB
快照备份

加载顺序
先 AOF

AOF
日志追加
always
every sec
RewriteAOF
混合模式 RDB + AOF

后 RDB
加载速度
快 RDB
慢 AOF

持久化
RDB

AOF

redis-main
rdb snapshot

child-process

fork

注意事项
避免使⽤O(n)的指令
keys *, hgetall, smembers, zrange all, lrange all
直接在redis.conf⾥rename-command阻塞指令
使⽤scan, hscan, sscan, zscan
使⽤unlink异步删除key

提⾼吞吐
使⽤pipeline批量传输, 减少⽹络RTT
使⽤多值指令 (mset, hmset)
使⽤script lua
⼲掉aof ?

(big key) or (hot key)
big key
scan / small range get
unlink (redis 4.0 async del)
hash shard

hot key
hash shard

redis lua
减少RTT消耗
⾃定义函数
保证多指令原⼦性

注意阻塞问题

redis module
自定义注册新命令
自定义新数据结构
性能比redis lua更强劲
redis4.0 以上

RedisJson
RedisBloom
RedisTimeSeries
more …

排查问题
外部 (慎用)
redis-cli monitor
内部
keyspace
slow log
redis-cli —latency

内存碎片
- - bigkeys
string, bytes空间
set, list, zset, hash, 元素个数
redis-rdb-tool
分析内存分布

memory usage key_name
memory stats
memory purge
阻塞及延迟
redis-cli --intrinsic-latency 10
redis-cli --latency-history

info -> instantaneous_ops_per_sec
info -> used_memory_human
connected_clients

单线程 ?
主线程
aof⽇志
bio线程

过期key的实现
惰性删除
定时删除
默认每100ms进行一次
从过期字典中随机取出 20 个键
删除这 20 个键中过期的键
如果过期键的比例超过 25%, 重复步骤 1 和 2
直到25ms最长执行时间退出
触发MaxMemory时尝试删除

缓存淘汰
CONFIG SET maxmemory 50 GB
volatile-lru (默认)
从设置过期数据集⾥查找最近最少使⽤
volatile-ttl
从设置过期的数据集⾥清理已经过期的
key.
volatile-random
从设置过期的数据集中任意选择数据淘汰

allkeys-lru
从数据集中挑选最近最少使⽤的数据淘汰
allkeys-random
从数据集中任意选择数据淘汰
no-enviction
不清理

事务
client

command
watch 监听key的变化

is
transaction

mutli 开启事务
exec 执⾏事务
discard 放弃事务
unwatch 放弃监听
pipeline vs 事务

watch/unwatch

watch

multi

queue

pipeline会被打断
事务不会被打断
redis不⽀持标准的acid事务

exec/discard

单机进化到多实例
什么是多实例
为什么要多实例化
多实例化需要注意什么?

单机多实例

what 多实例

6379

6380

6381

++

redis1

redis2

redis3

more

why 多实例
最⼤程度的使⽤内存
避免单实例RDB Write时
被kernel oom
使⽤swap造成阻塞.

copy on write will block
绕开redis单⼯作线程的问题
阻塞指令
系统调⽤

单实例启动太慢

busy event

扩展, 迁移, 内存随便整理

hashcrc & codec
more …

How 多实例
如 128G 内存
11G 为⼀个实例, 启动个10实例
redis server关闭⾃动rdb及aof
后台脚本来控制bgsave.
启动时也是⼀个个的启动

简约集群
主从模式
vip多线程版 twemproxy
codis
redis cluster

集群

master/slave
sentinal

sentinal

sentinal

update

proxy

master

write

rdb

psync

fullsync

read

slave

slave

codis vs redis cluster
cluster

codis

hash_tag

y

y

design

中⼼化

去中⼼化

pipeline

client move order

⽀持

slot

y

y

多租户

y

y

性能

high

this < cluster

code

复杂

简单

范围

⼴

也有不少⼤⼚

codis
codis-proxy

codis-proxy

zookeeper
redis
redis-m

主机组
redis-s

dashboard

redis cluster

常⽤场景
缓存
缓存⼀致性
缓存穿透
缓存击穿
缓存雪崩

分布式锁
redlock
延迟队列
…

缓存⼀致性
write cache -> write db
write db -> write cache

evict cache -> write db
write db -> evict cache
evit cache -> write db ->
evit cache

缓存⼀致性
write db -> write cache
更新cache失败

write cache -> write db
更新db失败

并发引起脏数据
client1更新了DB
client2更新了DB
client2更新cache
但client1覆盖了client2的cache

缓存⼀致性
evict cache -> write db
延迟引起脏数据

write db -> evict cache
脏数据概率⼩于先evit cache

client1先删除缓存

client1查询数据库, 得⼀个旧值

client2查询发现缓存不存在

client2将新值写⼊数据库

client2数据库查询得到旧值

client2删除缓存

client2将旧值写⼊缓存

client1将查到的旧值写⼊缓存

client1将新值写⼊数据库

evit cache -> write db -> evit cache
脏数据概率更⼩

规避脏数据
TTL
定时更新
Binlog订阅更新
Delay Queue

sync-srv

缓存⼀致性
所以, 在更新策略上, 难以保证绝对⼀致性, 但可以最终⼀致性
拼概率, 减少产⽣脏数据的可能
write db; write cache; binlog or ttl 双写
多数公司的选择
write db; binlog更新
didi, iqiyi
write db; evct cache 也是个好选择 !
facebook, 58

穿透&雪崩
穿透 (访问⼀个不存在的key)
在缓存中加⼊该key的null值
bloomfilter

雪崩 (⼤量key的失效)
不主动配置TTL
后台同步缓存时, 加⼊jitter ttl

击穿
击穿 (⼤量请求未缓存的key)
实现redis分段锁, 同样的请求争夺⼀把锁
拿到锁的去数据库查询
未拿到锁的等待, 再尝试访问缓存
缓存中还没有数据, 尝试数据库拿取

分布式锁
安全可靠

lua make ( compare and set ) !!!

say no
可重⼊锁

set + nx + ex
client_1

Redis

true

say yes
公平调度
say hard

{ bll_lock_key: ident }
client_2

set + nx + ex
false

redlock

client2

2/n + 1
推荐
最少5个实例

Redis

Redis

Redis
Redis

Redis

3个及以上拿到锁
未拿到锁的, 释放锁

client1

redis单节点性能
⼤约
单命令并发可达 10w TPS
管道及多指令可达 50w TPS
单命令时延在 150 us 左右

单节点

经历过的性能指标
1w 的稳定长连接
10w TPS
队列千万级别
百万数量key

单节点

维护经验
300个redis实例
30台服务器 (混部)
每个实例10G
约 3T 内存

集群

redis6
新增的resp3加⼊缓存特性
返回key的属性, ⽐如频率
更新范围
acl⽤户权限控制
控制命令及key
redis cluster proxy
兼容各类sdk

io多线程
io线程负责read, decode, encode, write
操作内存还是主线程

other ppt
redis cluster那些事⼉
https://github.com/rfyiamcool/share_ppt/blob/master/redis_cluster.pdf
redis之⾼级应⽤
https://github.com/rfyiamcool/share_ppt/blob/master/redis_advance.pdf
⼤话redis设计与实现
https://github.com/rfyiamcool/share_ppt/blob/master/rediscode.pdf

“Q&A!”
–峰云就她了

大家觉得文章对你有些作用! 如果想赏钱,可以用微信扫描下面的二维码,感谢!
另外再次标注博客原地址  xiaorui.cc