Redis 应用
- 分布式锁
- 统计计数
- bitcount : 位图统计指令
- bitpos: 位图查找指令
- 统计计数-估算去重
- HyperLogLog :去重统计 UV,标准误差是0.81%
- pfadd
- pfcount
- incrby 指令统计PV
- HyperLogLog :去重统计 UV,标准误差是0.81%
- 延时队列 list FIFO (rpush + lpop or lpush + rpop)
- 阻塞读 brpop or brpop
- 并发处理延时队列: zrem 多线程争抢任务的关键,返回当前实例是否抢到任务
- 布隆过滤器: 文章推送,去重内容等
-
- 说某个值存在,那么这个值可能不存在;当说某个值不存在,就肯定不存在
- bf.add bf.madd (批量)
- bf.exists bf.mexists (批量)
- 布隆过滤器JAVA 实现
- Guava Bloom Filter ( 缓存、限流、集合。。。) (不可删除,因为hash碰撞)
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- <version>19.0</version>
- </dependency>
- CountingBloomFilter带计数器的布隆过滤器 (可删除)
- <dependency>
<groupId>com.baqend</groupId>
<artifactId>bloom-filter</artifactId>
</dependency>
- <dependency>
- Guava Bloom Filter ( 缓存、限流、集合。。。) (不可删除,因为hash碰撞)
- 限流: Redis-Cell (漏斗算法) 「Redis 深度历险 P72」
- cl.throttle
- cl.throttle keyname 15 30 60 1
- 15: capatity 漏斗容量
- 30 operations / 60 seconds 这是漏斗速率
- need 1 quota(可选参数,默认1)
- GeoHash 附近的人
- Scan
Redis 持久化操作:
- RDB快照:glib 函数fork产生一个子进程。持久化操作交给子进程处理,父进程处理客户端请求
- 缺点:内存快照必须进行文件IO操作,但是文件IO操作不能使用多路复用API。Redis使用多进程COW(Copy on write)机制实现快照持久化。数据段是由多个操作系统的页面组合而成,每个页面大小只有4kb,当父进程需要对其中一个页面的数据进行修改时,会将被共享的页面复制一份分离出来,然后对这个复制的页面进行修改。这时子进程相应的页面是没有变化的,还是进程产生时的那一瞬间的数据。
- AOF日志:redis是先执行指令后将日志存盘; linxu的glibc 会提供了fsync(int fd)函数可以将指定文件的内容强制从内核缓存刷到磁盘。
- 优点:
- 缺点:文件指令过多重写时间会很长,
- 混合持久化
Redis 事务
- multi 开始事务;
- do something 。。。 (queued)命令后返回 标识 OK
- exex 事务的执行
- discard 事务的丢弃
优化
- 内部指令较多时,需要的网络IO时间也会线性增长,所以通常Redis客户端在执行事务时会结合pipeline一起使用,这样可以将多次IO操作压缩为单次IO操作
分布式锁
- 悲观锁
- 原子指令:
- setnx(se if not exists) + del
- set(key ,value ,nx=True,ex=xxx)
- 非原子指令:setnx + expire(死锁)
Watch 机制
- 乐观锁
- 会在事务开始之前顶住一个或多个关键变量,当事务执行时,也就是服务器收到了exec指令要顺序执行缓存的事务队列时,Redis会检查关键字变量自watch 之后是否被修改了(包括当前事务所在的客户端),如果关键变量被修改了,exec指令就会返回NULL回复告知客户端事务执行失败,这个时候客户端一般会选择重试
- 使用方式:
Redis PubSub 消息队列
- 消费者:
- 生产者:
- 必须先启动消费者,再执行生产者。这样可以保证多个消费者收到相同的消息序列
- 模式订阅
- subscribe
- psubscribe 模式订阅 >psubscribe codehole.* 主题是以codehole.开头的消息都可以收到
- PubSub缺点
- 当一个消费者都没有时,消息直接丢掉
- 断连期间生产者发送的消息,对于这个消费者来说就是彻底丢失
- Redis重启,Pubsub消息不会持久化,直接丢弃
小对象压缩存储 ziplist: 压缩列表是一块连续的内存空间,元素之间紧挨着没有任何冗余空隙;长度为8kb
- hash 元素个数超过512个 就必须用标准结构存储
- hash 的任意元素的k /v 的长度超过64 就必须用标准结构存储
- list 的元素个数超过512 就必须用标准结构存储
- list 的任意元素的长度超过64 就必须用标准结构存储
- zset 的元素个数超过128 就必须用标准结构存储
- zset 的任意元素长度超过 64 就必须用标准结构存储
- set 的整数元素个数超过512 就必须用标准结构存储
- object encoding [keyname] 输出对象的存储结构
redis 内存回收机制
- Redis并不是总是将空闲内存立即归还给操作系统
- 当删除了1G的key时,内存并没有变化太大,原因是操作系统是以页为单位来回收内存的,页上只要有一个key在使用,那就不能被回收
- flushdb 执行后会被回收内存,是因为所有key都被删除了
redis 内存分配算法
- redis 可以使用jemalloc(facebook) 库来管理内存,也可以切换到tcmalloc(google)库, je库性能要比tc 要好一些。 默认jemalloc
- 使用info memory 查下内存管理
redis 主从同步
- 增量同步
- 环形数组-buffer 重头覆盖
- 快照同步
- 无盘同步:
- 主服务器直接通过套接字将快照内容发送到从节点,生成快照是一个遍历的过程, 主节点会一边遍历内存,一边将序列化的内容发送到从节点,从节点还是跟之前一样,先将接收到的内容存储到磁盘文件中,再进行一次性加载
- wait 命令 :3.0版本后出现-将异步复制变成同步复制
- wait N t : 第一次参数是从节点数量N,第二个参数是同步等待时间t,t=0 时表示无限等待直到N个从节点同步完成
- 缺点:出现网络分区时,主从同步无法继续进行,wait指令会永远阻塞,Redis丧失可用性
redis Sentinel 高可用方案
- 防止消息丢失:Sentinel无法保证消息不丢失,也能尽量保证消息少丢失
- min-slaves-to-write 1 : 表示主节点必须至少有一个从节点在进行正常复制,否则就停止对外写服务,丧失可用性
- min-slaves-max-lag 10 :单位是秒,表示如果再10s内没有收到从节点的反馈,就意味着从节点同步不正常,要么是网络断开,要么就是一直没有给反馈
redis 集群方案1 Codis
- Codis :豌豆荚中间件团队
- 数据划分为1024个槽位,可修改
- 需要额外的分布式存储槽位信息
- 槽位算法:默认会对key 值使用crc32 算法进行hash,hash后的整数值对1024 进行取模得到余数就是槽位
redis 集群方案2 Redis Cluster
- 数据划分为16384个槽位
- 槽位信息存放于每个节点中
- 槽位算法:默认会对key值使用crc16算法进行hash,得到一个整数值,然后用这个整数值对16384 取模得到具体槽位 (允许用户强制将某个key挂到某个槽位上)
redis容错
- cluster-require-full-coverage 允许部分节点发生故障,其他节点还可以继续提供对外访问
网络抖动
- cluster-node-timeout 表示某个节点持续timeout的时间失联时,才可以认定该节点出现故障。需要主从切换,否则,redis会因为网络抖动导致频繁切换(数据重新复制 )
- cluster-slave-validity-factor 作为倍数系数放大这个超时时间来宽松容错的紧急程序,如果为0,那么主从切换是不会抗拒网络抖动
可能下限 PFail 与确定下线 Fail
- Cluster 去中心化,收到节点失联后会广播出去 PFail ,当PFail count为大多数时,判断为Fail