架构师进阶之 Redis 专题系列, 今天主要分享 Redis 高并发竞争解决方案.
01. 并发竞争的由来
1.Redis 高并发的问题
Redis 缓存的高性能有目共睹, 应用的场景也是非常广泛, 但是在高并发的场景下, 也会出现问题: 缓存击穿, 缓存雪崩, 缓存和数据一致性, 以及今天要谈到的缓存并发竞争.
这里的并发指的是多个 Redis 的 client 同时 set key 引起的并发问题.
2. 出现并发设置 Key 的原因
Redis 是一种单线程机制的 nosql 数据库, 基于 key-value, 数据可持久化落盘. 由于单线程所以 Redis 本身并没有锁的概念, 多个客户端连接并不存在竞争关系, 但是利用 jedis 等客户端对 Redis 进行并发访问时会出现问题.
比如: 同时有多个子系统去 set 一个 key. 这个时候要注意什么呢?
3. 举一个例子
多客户端同时并发写一个 key, 一个 key 的值是 1, 本来按顺序修改为 2,3,4, 最后是 4, 但是顺序变成了 4,3,2, 最后变成了 2.
如何解决 Redis 的并发竞争 key 问题呢? 下面给到 2 个 Redis 并发竞争的解决方案.
02. 第一种方案: 分布式锁 + 时间戳
1. 整体技术方案
这种情况, 主要是准备一个分布式锁, 大家去抢锁, 抢到锁就做 set 操作.
加锁的目的实际上就是把并行读写改成串行读写的方式, 从而来避免资源竞争.
2.Redis 分布式锁的实现
主要用到的 Redis 函数是 setnx()
用 SETNX 实现分布式锁
利用 SETNX 非常简单地实现分布式锁. 例如: 某客户端要获得一个名字 youzhi 的锁, 客户端使用下面的命令进行获取:
SETNX lock.youzhi<current Unix time + lock timeout + 1>
如返回 1, 则该客户端获得锁, 把 lock.youzhi 的键值设置为时间值表示该键已被锁定, 该客户端最后可以通过 DEL lock.foo 来释放该锁.
如返回 0, 表明该锁已被其他客户端取得, 这时我们可以先返回或进行重试等对方完成或等待锁超时.
2. 时间戳
由于上面举的例子, 要求 key 的操作需要顺序执行, 所以需要保存一个时间戳判断 set 顺序.
系统 A key 1 {ValueA 7:00}
系统 B key 1 { ValueB 7:05}
假设系统 B 先抢到锁, 将 key1 设置为{ValueB 7:05}. 接下来系统 A 抢到锁, 发现自己的 key1 的时间戳早于缓存中的时间戳(7:00<7:05), 那就不做 set 操作了.
3. 什么是分布式锁
因为传统的加锁的做法 (如 java 的 synchronized 和 Lock) 这里没用, 只适合单点. 因为这是分布式环境, 需要的是分布式锁.
当然, 分布式锁可以基于很多种方式实现, 比如 zookeeper,Redis 等, 不管哪种方式实现, 基本原理是不变的: 用一个状态值表示锁, 对锁的占用和释放通过状态值来标识.
03. 第二种方案: 利用消息队列
在并发量过大的情况下, 可以通过消息中间件进行处理, 把并行读写进行串行化.
把 Redis.set 操作放在队列中使其串行化, 必须的一个一个执行.
这种方式在一些高并发的场景中算是一种通用的解决方案.
end~
以上就是 Redis 并发竞争 key 技术方案详解, 喜欢的可以点赞关注哦~
[文章彩蛋]
给大家推荐一个 Java 进阶内推交流群 967051845, 不管你在地球哪个方位, 不管你参加工作几年都欢迎你的入驻!(群内会免费提供一些群主收藏的免费学习书籍资料以及整理好的几百道面试题和答案文档!)
来源: http://www.jianshu.com/p/efb39bf64942