分布式锁
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倍纳入重点排查对象
- 根据倍数不同,启动不同的排查流程,然后使用黑名单进行防控(运营)
- 实时监控redis命中率(业务正常范围内,通常会有一个波动值)与null数据的占比
- key加密