分布式锁

Redisson

实现原理

  • 先获取锁,如果获取不到会循环等待
  • 获取锁后执行lua脚本进行加锁,这时会产生一个hash值作为key
  • 加锁成功后会开启一个watch dog 后台线程用于检测锁时间
  • 加锁时间默认为30秒,watch dog 每10秒检测一次 如果锁key还存在则延时10秒
  • 任务执行完成后执行删除锁
  • 加锁
    • RedissonAtomicLong


多机实现的分布式锁Redlock+Redisson

实现原理

  • 获取当前时间,以毫秒为单位。
  • 按顺序向5个master节点请求加锁。客户端设置网络连接和响应超时时间,并且超时时间要小于锁的失效时间。(假设锁自动失效时间为10秒,则超时时间一般在5-50毫秒之间,我们就假设超时时间是50ms吧)。
  • 如果超时,跳过该master节点,尽快去尝试下一个master节点。
  • 客户端使用当前时间减去开始获取锁时间(即步骤1记录的时间),得到获取锁使用的时间。当且仅当超过一半(N/2+1)的Redis master节点都获得锁,并且使用的时间小于锁失效时间时,锁才算获取成功。(如上图,10s> 30ms+40ms+50ms+4m0s+50ms)
  • 如果取到了锁,key的真正有效时间就变啦,需要减去获取锁所使用的时间。
  • 如果获取锁失败(没有在至少N/2+1个master实例取到锁,有或者获取锁时间已经超过了有效时间),客户端要在所有的master节点上解锁(即便有些master节点根本就没有加锁成功,也需要解锁,以防止有些漏网之鱼)。

事务

redis事务执行过程

  • 开始事务。
  • 命令入队。
  • 执行事务。

Redis 事务命令

命令 描述
DISCARD 取消事务,放弃执行事务块内的所有命令。
EXEC 执行所有事务块内的命令。
MULTI 标记一个事务块的开始。
UNWATCH 取消 WATCH 命令对所有 key 的监视。
WATCH key [key …] 监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。

FAQ

为什么Redis不支持事务回滚?

多数事务失败是由语法错误或者数据结构类型错误导致的,
语法错误说明在命令入队前就进行检测的,而类型错误是在执行时检测的,
Redis为提升性能而采用这种简单的事务,这是不同于关系型数据库的,特别要注意区分。

数据类型

Hash

Redis hash 是一个 string 类型的 field(字段) 和 value(值) 的映射表,hash 特别适合用于存储对象。
Redis 中每个 hash 可以存储 232 - 1 键值对(40多亿)。

hash 命令

下表列出了 redis hash 基本的相关命令:

命令 描述
HDEL key field1 [field2] 删除一个或多个哈希表字段
HEXISTS key field 查看哈希表 key 中,指定的字段是否存在。
HGET key field 获取存储在哈希表中指定字段的值。
HGETALL key 获取在哈希表中指定 key 的所有字段和值
HINCRBY key field increment 为哈希表 key 中的指定字段的整数值加上增量 increment 。
HINCRBYFLOAT key field increment 为哈希表 key 中的指定字段的浮点数值加上增量 increment
HKEYS key 获取所有哈希表中的字段
HLEN key 获取哈希表中字段的数量
HMGET key field1 [field2] 获取所有给定字段的值
HMSET key field1 value1 [field2 value2 ] 同时将多个 field-value (域-值)对设置到哈希表 key 中。
HSET key field value 将哈希表 key 中的字段 field 的值设为 value 。
HSETNX key field value 只有在字段 field 不存在时,设置哈希表字段的值。
HVALS key 获取哈希表中所有值。
HSCAN key cursor [MATCH pattern] [COUNT count] 迭代哈希表中的键值对。

List

Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)
一个列表最多可以包含 232 - 1 个元素 (4294967295, 每个列表超过40亿个元素)。

命令 描述
BLPOP key1 [key2 ] timeout 移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
BRPOP key1 [key2 ] timeout 移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
BRPOPLPUSH source destination timeout 从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它; 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
LINDEX key index 通过索引获取列表中的元素
LINSERT key BEFORE|AFTER pivot value 在列表的元素前或者后插入元素
LLEN key 获取列表长度
LPOP key 移出并获取列表的第一个元素
LPUSH key value1 [value2] 将一个或多个值插入到列表头部
LPUSHX key value 将一个值插入到已存在的列表头部
LRANGE key start stop 获取列表指定范围内的元素
LREM key count value 移除列表元素
LSET key index value 通过索引设置列表元素的值
LTRIM key start stop 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。
RPOP key 移除列表的最后一个元素,返回值为移除的元素。
RPOPLPUSH source destination 移除列表的最后一个元素,并将该元素添加到另一个列表并返回
RPUSH key value1 [value2] 在列表中添加一个或多个值
RPUSHX key value 为已存在的列表添加值

其他

哨兵

  • 监控
    • 不断检测master和slave是否正常运行
    • master存活检测,master与slver运行情况检测
  • 通知(提醒):当被监控的服务器出现问题时,向其他(哨兵间,客户端)发送通知
  • 自动故障转移:断开master与slave连接,选取一个slave作为master,将其他slave连接新的master,并告知客户端新的服务器地址

如果一个slave跟master断开连接已经超过down-after-milliseconds的10倍,外加master宕机的时长,那么slave就被认为不适合选举为master。
接下来会对slave进行排序:
首先按照slave优先级进行排序,slave priority越低,优先级越高;其次如果slave priority相同,那么看replica offset,哪个slave复制了越多的数据,offset越靠后,优先级越高;如果上面两个条件相同,那么选择一个run id比较小的那个slave。

雪崩

  • 在较短时间内,缓存中较多的key集中过期
  • 此周期内的请求访问过期的数据,redis未命中,redis向数据库获取数据

解决方案:

  • 更多的页面静态化处理
  • 构建多级缓存架构
    • Nginx缓存+redis缓存+ehcache缓存
  • 检测MySQL严重耗时业务进行优化
    • 对数据库瓶颈排查:例如超时查询,耗时较高查询
  • 限流、降级
    • 短时间范围内牺牲一些客户体验,限制一部分请求访问,降低应用服务器压力,待业务低速运转后再逐步开放访问

击穿

  • Redus中某个key过期,该key访问量巨大
  • 多个数据请求从服务器直接压到redis后,均未命中
  • redis在短时间内发起了大量对数据库中同一数据的访问

解决方案:

  • 现场调整
    • 监控访问量,对自然流量激增的数据延长过期时间或设置永久性key
  • 后台刷新数据
    • 启动定时任务,高峰期之前刷新数据有效期
  • 二级缓存
    • 设置不同的失效时间,保障不会被同时淘汰
  • 加锁
    • 分布式锁,防止被击穿,但是也要注意性能瓶颈

穿透

  • Redis中大面积出现未命中
  • 出现非正常URL访问

解决方案:

  • 白名单策略
    • 使用布隆过滤器
  • 实时监控
    • 实时监控redis命中率(业务正常范围内,通常会有一个波动值)与null数据的占比
      • 非活动时段波动:通常检测3-5倍,超过5倍纳入重点排查对象
      • 活动时段波动:通常检测10-50倍,超过50倍纳入重点排查对象
    • 根据倍数不同,启动不同的排查流程,然后使用黑名单进行防控(运营)
  • key加密