从门外到门内: redis
相关资料:
- 官网
- 文档
- 好书: redis实战(书中的代码), Redis开发运维实践指南
- 好文: 关于Redis的常识
Redis数据类型的使用
具体那五种数据类型有什么特点,请看关于Redis的常识里面总结得非常好。需要补充一下的是对于列表,散列,有序集合,当长度和里面的值得大小在一定范围时,redis会选择使用ziplist结构来存储。对于集合会使用zset来存储,节省内存。长度和大小都可以通过配置文件来指定,默认是少于512个且最长那个值不超过64字节时使用。由于涉及到编码解码问题,设置太大会有性能问题。重点,可以通过conn.debug_object(key)来查看,相关信息。
在redis的各种数据结构中,要注意O(n)的操作,尽量规避,由于redis是单线程模型,进行一个耗时操作时,会影响其它客户端的操作。
使用哪种数据结构需要根据使用场景来决定,主要的思路(确定后看看文档相关操作的时间复杂度):
- 是否有序
- 是否需要不重复
- 侧重点是增删改查的哪一种或多种
- 内存占用, 注意,集合的个数*集合里面的元素个数,不同的搭配会有不同的效果, 即便
- 性能要求
- 是否存在O(n)类型的操作
redis的最佳实践
del 操作性能问题:
del set, sorted set , hash 的时间复杂度是O(m),m是集合里面的数据, 因此对于数据量较大的结合,不要直接一次性删除。应当采用:重命名 + 批量删除逐渐删除
用scan代替keys命令, 因为keys会遍历整个数据
查看redis的状况的命令:
-
info commandstats: 查看命令的运行情况, 可以看出哪些命令运行的慢
-
config resetstat:重置commandstats
-
redis-cli -h localhost -p 6379 client list | grep -v "omem=0": 快速排查redis慢的神奇命令,阻塞是omem升高
-
slowlog get|len | reset
尽可能使用不超过1M大小的KV
因为这样会导致redis的内存碎片增加,导致rss占用较大
尽可能使用pipeline操作&批量操作命名,减少网络传输的开销
对于set, sorted set,使用pipeline时可以将集合操作的结果存入一个预先定义的key中,这样就可以
减少对大数据集合的高时间复杂度的操作
性能提升思路,减少操作中集合元素的数量。
- 将宽集合转为高集合(类似于mysql中的水平切分)
- 遇到set 和 sorted set 有很多交集和并集执行,调整多个交集并集的顺序,较少redis的处理量。(一般来说,先进行交集操作再进行并集操作)
降低内存的方式
列表,散列,有序集合,当长度和里面的值得大小在一定范围时,redis会选择使用ziplist结构来存储。对于集合会使用zset来存储,节省内存。利用短结构结合分片,来降低内存的使用。由于长度的限制,需要利用分片技术来将扩展存储空间。instagram节约内存的著名例子
redis内存过期策略
过期策略
-
定时删除(基本没人用)
含义:在设置key的过期时间的同时,为该key创建一个定时器,让定时器在key的过期时间来临时,对key进行删除
缺点:若key过多,删除key太耗cpu, 定时器创建耗时
优点:保证内存尽快释放 -
惰性删除
含义:key过期的时候不删除,每次从数据库获取key的时候去检查是否过期,若过期,则删除
缺点:若大量的key在超出超时时间后,很久一段时间内,都没有被获取过,那么可能发生内存泄露
优点:删除操作只发生在从数据库取出key的时候发生,而且只删除当前key,所以对CPU时间的占用是比较少的 -
定期删除
含义:每隔一段时间执行一次删除过期key操作
特点:CPU时间友好和内存友好方面介于定时删除和惰性删除直接,折中。
难点:定期删除的频率?删除执行的时长
redis采用的方式: 惰性删除+定期删除
redis的常见应用场景
-
取最新的N个数据操作
例如取文章最新的100条评论的ID放到list集合中
-
以某些条件未权重的排行榜应用,取TOP N操作
利用sorted set 结构,排序条件设置为score, 具体数据设置成value
-
各种计数
例如,电商的商品的各种计数(喜欢,评论,点赞),利用hash结构存储不同维度的计数
-
存储社交关系
将用户的好友,存在sorted set 中,value为用户id, score为时间戳,求两个人的好友,交集即可。好友巨多应该就不能这样用
-
构建队列系统
List可以很方便的利用相关命令来构建队列
-
缓存
redis提供丰富的数据类型,本身就可以当分布式缓存使用。很多框架本身提供相应的功能,通过配置就可以让redis作为缓存层,例如将redis配置为django的缓存层。
-
实现boomfilter
redis中的string类型提供位操作,所以可以利用redis实现一个分布式的boomfilter。 这里有别人实现的
-
分布式锁
主要思路是利用setnx,和BLPOP或者是BRPOP命令来实现。例如阻塞锁的实现思路,clientA通过setnx设置锁,ClientB再设置的时候,已经存在了,此时则通过BLPOP阻塞,当ClientA通过del 命令删除锁并LPUSH到相对于的队列中,此时ClientB就结束阻塞了。 具体,可以参考某大大的实现
-
搜索引擎
例如文档内容搜索,利用set构建反向索引,并集搜索同义词,交集搜索多个次,差集做搜索排除。利用sort命令对搜索结果进行排序,利用sorted set 的交集对搜索结果进行多维度排序。具体,可以看redis 实战的第七章
一些与redis相关的github热门项目
retools
scrapy-redis
redis-tools
python-redis-lock
pyreBloom