redis深度历险 pdf_Redis深度历险

5a17ae77238d41e7262b7406f0222d2c.png

说明:

本文章为书籍《Redis深度历险》的读书笔记,如有侵权,马上删除,不承担任何法律责任。

目录:

  • 应用
  • 原理
  • 集群
  • 拓展
  • 源码

应用

内容列表:ZSET,分布式锁,HyperLogLog,布隆过滤器

1.ZSET 有序列表 sorted set

  • 形式
    • value 1 - score 1
    • value 2 - score 2
    • value 3 - score 3
  • 数据结构
    • value -> score : HashMap
    • score -> value : SkipList
  • 应用
    • 粉丝列表:value是粉丝的用户id,score是关注时间,根据关注时间排序
    • 学生成绩:value是学生的id,score是学生成绩,根据分数进行排名
    • 延时队列:value是字符串,score消息到期时间;
  • SKIPLIST 跳表
    • 平均查找和插入 O(log n)
    • 新插入元素层数
      • 采用随机策略
      • level1概率100%,level2概率50%,level3概率25%

6b69a3fc5d03526531c0e8326715e3df.png

2.分布式锁

  • 使用场景
    • 多进程/线程间需要串行执行操作

2958cae74d8eed98420569f20091ed18.png
  • 原理
    • 使用redis键代表锁
    • set表示占用锁,del表示释放锁
    • setnx: set if not exists
  • 方法1
    • setnx -> do -> del
    • 问题:do过程中出现异常,del未被执行,锁不会被释放
>
  • 方法2
    • setnx -> expire -> do -> del
    • 问题:setnx后进程挂掉,expire未被执行,锁不会被释放
>
  • 方法3
    • setnx & expire (version 2.8) -> do -> del
    • 问题1:do太久会导致锁被释放,多个进程进入临界区代码
    • 问题2:del可能被其他进程执行
>
  • 方法4
    • 生成随机数val -> setnx & expire -> do -> del (验证key的val随机数是否一致)
    • delifequals需要使用lua脚本,保证原子性
    • 可以解决上面问题2,没有解决问题1
  • 结论
    • 方法3使用简单,如果执行时间不是太长,用方法3就好;可以加报警。
    • 集群的问题:主的锁未同步到备之间,主挂了,会导致锁未被释放。
    • 锁冲突解决方法:
      • 直接抛异常,通知用户稍后再试
      • sleep一会重试
      • 请求转移至消息队列

3.HyperLogLog

  • 应用:统计页面user view,需要除重
  • 千万级别时set集合会浪费空间
  • 记录低位连续零位的最大长度maxbit,可以估算出随机数的数量。
  • 空间12KB,误差小于1%。

c40a1b65b296c021609b6f2feca89ceb.png

4.布隆过滤器bloom filter

  • 应用:当查询某个row,可以先通过内存布隆过滤器滤掉大量不存在的row,再去查磁盘
  • 其他:爬虫系统对url去重;垃圾邮件;
  • 特征:当它说某个值存在时,这个值可能不存在;当它说不存在时肯定不存在;
    • 上图中,如果命中了1/4/5,那么oracle大概率存在;因为这些可能是被其他key设置;
    • 上图中,如果没命中1,那么oracle肯定不存在;
  • 原理:
    • h1, h2, h3为多个hash函数
    • 当错误率为0.1%时,一个元素大约需要15bit
    • 当不使用时,一个字符串可能就有几十个字节;set本身还有4或8字节的指针
    • 所以布隆过滤器的空间优势还是非常明显的

d619471970c3307acdcd290f61877faf.png
redis主进程模型;费时操作在子进程中进行。

原理

1.数据结构

  • 总:为加速内存操作和空间使用做了很多优化
  • string:
    • sds(simple dynamic string)
    • 带长度
    • 内存预分配,加快扩容与缩容
  • list
    • 存储string列表
    • 元素少时用ziplist,连续内存,无prev/next指针,但插入删除O(N)
    • 元素多时用linkedList
  • map
    • murmurhash
    • 元素少时用ziplist
    • 元素多时用hashtable
    • 扩容/缩容rehash:读先访问源表,写访问目标表
  • set
    • intset
    • map的特殊形式
  • sorted set
    • 元素少时用ziplist
    • 元素多时用skiplist+hashtable

2.单线程模型

  • 为何快,benchmark的set可达10w/s
    • 单线程模型(main thread,还有辅助thread),避免多线程上下文切换的问题
    • 纯内存操作(almost)
    • 非阻塞IO
      • 阻塞IO:read(n) 表示有n个字节数据才返回
      • 非阻塞IO:
        • NIO: Non-blocking IO
        • 读写方法不会阻塞,能读多少读多少
        • 读方法和写方法都会通过返回值告知程序实际读写了多少字节
        • 没有通知机制,通过os提供的事件轮询api来解决,epoll
    • 管道
      • mget,pipeline,合并多个网络来回
    • 定时任务
      • 大型hash表的渐进式迁移,过期key的主动淘汰,bgsave,bgaofrewrite

3.持久化

  • RDB snapshot
    • 全量模式
    • 快照,内存数据的二进制序列化,存储紧凑
    • fork子进程进行持久化;使用cow(copy on write)
    • save 900 1
    • save 300 10
    • save 60 10000
    • 满足上面任一触发bgsave,比如60秒内有至少10000key改变
    • 性能更好
  • AOF
    • 增量模式
    • append only file
    • 连续增量备份,内存数据修改的指令记录文本
    • appendfsync everysec [默认]
    • appendfsync always
    • appendfsync no
    • 独立thread进行
  • 比较
    • rdb文件大,aof慢
    • redis重启,先加载rdb内容,再增量重放aof日志。

83a9ed353f65841652a75c73de0c0e5f.png

集群

0.集群组建方式

  • sharding: 客户端自己实现,扩缩容需要改代码(client->redis)
  • proxy: 扩展性强,proxy增加耗时(client->proxy->redis)
  • cluster: gossip协议,排查问题复杂(client->redis cluster)

1.Redis Sentinel (实现备份)

  • master -> slave
    • 正常:aof增量
    • 新节点:先rdb,再aof
    • sentinel
      • 使用raft
      • 可以看成是一个zookeeper的集群
      • 负责持续监控主从节点的健康
      • 当主节点挂,自动选择最优从节点为新主节点
      • 客户端连接,先连接sentinel,查询主节点地址,在进行数据交换

2.Twemproxy (实现扩容)

  • client -> {twemproxy} -> redis
  • 优点:当业务增长时,无需修改client端代码,实现扩容。
  • 缺点:
    • 性能比直接连接损失大概20%
    • 扩容时需要修改配置文件,停服务重启。
  • 高可用:{twemproxy} 为多个twemproxy实例,以实现高可用,client需要感知多个twemproxy;
  • node-ejection:打开时,请求可能到其他server;不打开,请求到同一个server,此时可使用slave+sentinel实现高可用。
  • hash:可配置不同的hash算法。

3.Codis (实现扩容)

  • 提升twemproxy扩容时的体验,实现平滑扩容,架构更复杂
  • client -> codis -> redis
  • key -> crc32 -> mod 1024 计算槽位 -> 对应redis实例 (1024个槽位)
  • 集群配置中心使用zookeeper来实现

4.Redis Cluster (实现扩容)

  • 没有Sentinel,没有proxy
  • master节点对外提供读写服务,slave只提供读;每个都保存了集群的配置信息
  • gossip协议
  • 生产环境使用较少,出问题不好排查
  • 作者思想来源于cpu的多核

拓展

1.stream: 小kafka

2.redlock

  • 解决master挂,锁未同步至slave的问题
  • 加锁时,向过半节点发送set指令,过半set成功即成功
  • 释放锁时,向所有节点发送del指令
  • 使用场景:redis集群;
  • 需要考虑出错重试,时钟漂移等问题,复杂

3.过期策略

  • 定期扫描
    • 每10s一次
    • 贪心策略
      • 过期字典中随机选20个key
      • 删除
      • 如果过期key比例超过1/4,则重复
      • 保证不能超过25ms
  • 惰性策略
    • 访问时检查是否过期,过期了则删除
  • 大量key同时过期卡顿
    • 将过期时间随机化
  • 从节点
    • 不进行定期扫描
    • 主节点在key过期时,在aof文件中增加一条del
  • 持久化
    • 使用volatile-xxx过期策略
    • 只会淘汰设置了过期时间的key

源码

nothing special

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值