Redis备忘(一)
hash:
渐进式rehash:同时查询新旧两个hash,然后在后续定时任务以及hash的子指令中,循序渐进将旧的迁移到新的hash表
Redis应用:
1.分布式锁:
实现1:setnx+expire+del,setnx后客户端宕机怎么办,根本原因在于没有原子性
解决:redis2.8引入了原子性的命令进行设置。
2.分布式锁超时问题:1执行时间太长,超过了锁的时间,2拿到了锁,这时候1执行完了,就释放了锁,于是3在2执行完成之前拿到了锁
解决:(1) 别做长时间的任务
(2) value上标记一个随机数,释放锁时先看看随机数是否一致,如一致再删除(匹配value和删除key不是原子操作,可以借助lua实现“delete if equals”)。
3.消息队列:
blpop
4.位图:本质就是字符串,用getbit,setbit操作,也可以用getString获取整个字符串。可以用来实现用户签到等。
5.hyperloglog: 可以用来统计UV,不能做到精准去重,有时间可以继续研究下。
6.bloomfilter: hyperloglog只能计数,没有"contains"语义,不知道某个值是否包含,Redis4.0后可以使用bloomfilter。
注意,元素超出设定个数后需要重建布隆过滤器。
7.限流:用zset的zremrangeByScore可以获取一定时间内发生次数
8.漏斗限流
9.GeoHash: 传统数据库实现方式:select id from positions where x0-r < x < x0+r and y0-r < y < y0+r
GeoHash将二维经纬度映射到一维整数
本质是对地图用二刀法进行切割,每个小正方形有一个编码,距离越近的编码越接近
Redis将GeoHash值存储在了zset中
10.scan: 生产环境不要用keys命令,没有offset,limit参数,会一次性吐出所有key,由于Redis的单线程特性会造成卡顿。
scan通过游标进行,有limit参数(仅仅是限定了服务器单次遍历字典槽位的数量),结果有重复,需要client端去重
举例:获取大key,用scan遍历所有key。实际运维中使用redis-cli -h 127.0.0.1 -p 7001 –-bigkeys即可。
原理:
为啥快:数据在内存,单线程,所以要注意O(N)复杂度的操作,使用非阻塞IO,IO多路复用
Redis给每个客户端一个FIFO的命令队列,响应队列
定时任务的实现:最小堆,每次循环执行一个
redis每次循环会将最小堆的任务处理完后将即将要执行的任务还需要多少时间记下来,作为select 的timeout参数
通信协议:RESP文本协议,没什么可说的,性能瓶颈不在于网络
持久化:快照+AOF
COW机制:fork一个子进程,共享父进程代码段和数据段,之后父进程继续写入,会复制 一个新的页面,不影响子进程的工作。
AOF重写原理:bgrewriteaof指令开启子进程遍历内存并转换成AOF指令,序列化到一个新的AOF文件,完成后用这个文件替代旧的AOF文件。
写AOF文件实际写到了内核给文件描述符分配的一个内存缓存区中,内核异步将脏数据写盘。可以设置如fsync周期1s写盘。
优劣:使用rdb会丢失数据,使用aof会导致重启太慢,故redis4.0中支持了混合持久化。(重启的时候,可以先加载rdb的,然后再重放增量 AOF 日志)
pipline:
将原本的写->读->写->读流程,用了管道可以写写读读,其实服务器端根本没有任何区别
为啥能提高性能?因为写操作只是写到本地buffer,不会阻塞,可以很快返回。
事务:
指令在exec 之前不执行,而是缓存在服务器的一个事务队列中,即使某个命令执行失败,后续命令依然正常执行,没有实现原子性。
小对象压缩:
元素少的时候没必要使用hashmap,直接用一维数组搞定
我们用object encoding key可以查看底层用的什么数据结构