这一块学的不是太透,简要做了一些笔记,分享一下,有机会再完善
Redis基本特性
- 非关系型键值数据库,O(1)时间复杂度存取值
- 数据存在内存
- 核心支持String,hash,list,set,sorted set 5大数据类型
- 支持磁盘持久化,LUA脚本、客户端缓存
- 支持哨兵及RedisCluster两种高可用集群模式
应用场景
- 数据缓存
- 计数器、分布式ID、会话缓存
- 数据队列、分布式锁、热数据存储等
- 排行榜、延迟队列(利用sorted_set实现)
字符串数据结构
- key的类型表面支持字符串数字,其实key都是string
- redis底层对string类型是sds数据结构(simple dynamic string)
- 底层为C语言实现,思考为啥不直接用C的字符串类型?
- sds结构:
sds:
free: 3 // 减少内存扩容,以空间换时间
len: 10 // 容量只增不减,以成倍机制扩容
char buf[] = "qinchen" -> "qinchen123\0"
- sds支持根据字符串长度,用不同sdshdr类型存储,支持内存对齐
- sdshdr5
- sdshdr8
- sdshdr16
- sdshdr32
- sdshdr64
数据结构
- 数组 + 链表
- 将key进行hash,再以数组长度取模,得到数组下标
- hash特点:任意相同输入,得到相同输出,不同输入,可能得到相同输出
- 也就是说将会出现hash碰撞,然后使用链表解决
(k1, v1),(k2, v2),(k3, v3)
hash(k1) % 4 = 0
hash(k2) % 4 = 1
hash(k3) % 4 = 1
arr[0] -> (k1, v1, next -> null)
arr[1] -> (k3, v3, next -> k2) (k2,v2, next -> null)
- c中的数据结构
redisDb:
dictEntry
*key // sds
union {
*val
u64
s64
d
} v;
*next
redisObject
type // 数据类型 string/list/set...
encoding // 底层的编码形式 int/embstr...
lru // 内存淘汰策略相关
refcount // 引用计数,内存回收用
*ptr; // 指向数据在内存中实际地址
dictht
**table
size
sizemask // size-1
used;
- 对于
set a 1000
redis底层做了优化,其值类型为int,直接利用引用指针属性存储,减少内存及CPU开销 - 字符串长度小于等于44,底层类型为embstr,超出后则为raw,目的提升性能
set 44byte aaaaaaaa... // 44个a
object encoding 44byte // 打印embstr
- bitmap类型
setbit login_12_12 USERID 1 // 值只能为0/1
getbit login_12_12 USERID // 返回1
bitcount login_12_12 // 统计
// 以上可用于统计用户日活场景,也可统计连续登陆统计(多个值做按位与)
bitop and login_1212-1113 login_12_12 login_12_13
// 还可按位或
bitop or xxxx xxxx xxxx
扩展知识:汉明重量;GeoHash经纬度编码
缓存穿透
- 多层缓存不命中
- 黑客可能攻击
- 可能对系统造成瘫痪
- 可通过缓存空对象、布隆过滤器等解决
缓存失效(击穿)
- 批量设置缓存,过期时间一致
- 在某一时间将批量失效
- 解决:将缓存超时时间+随机数设置
缓存雪崩
- Redis集群挂掉,导致流量全部导到数据库
- 数据库扛不住,应用不可用,最终整个系统瘫痪
- 解决方案:服务限流与降级,提前压测演练
热点缓存key重建优化
- 比如热点新闻事件,冷门商品突然抢购火爆
- 利用分布式锁挡
缓存与数据库双写不一致问题
- 多线程下,执行更新缓存先后顺序不能保证
- 解决方案:线程排队、延迟双删、分布式锁(推荐)
- 利用分布式锁的读写锁(读读不互斥,读写互斥)
开发规范与性能优化
- key设置:设前缀、防冲突,尽可能短,不含特殊符
- 防止bigkey(针对value),一般认为超10kb就是bigkey
- bigkey的危害:导致redis阻塞、网络拥塞、过期删除等问题
- 解决bigkey,尽可能拆小