分布式锁线上真实案例架构设计哲学本质解剖
1. 业务场景驱动:
- 交易商品库锁定,防止用户重复下单.
- MQ消息去重,防止消息重复消费: a. 发送端去重 b. 消费端去重
- 订单操作变更协同: a. 在用户对商品下单后,订单状态为待支付,在某一时刻用户正在对该订单做支付操作,商家对该订单进行改价操作. b. 其它类似状态的修改行为,也需要做串行处理,避免出现数据不一致性.
2. 业务场景共性:
(1). 共享资源:
- 有哪些共享资源? a. 用户、订单消息、订单.
- 举例: a. 比如分布式系统中,下单过程中,请求两台order服务的过程中,什么资源是可以控制的? b. 如用户id下过单,用一种方式记录下来,再来下单的时候,就不能下单了. c. 此时,用户id就变成一个共享资源了.
(2). 解决思路:
- 共享资源互斥.
- 共享资源串行化.
(3). 解决方案:
- 在并发下,通过加锁对共享资源进行串行化.
- 锁的问题: a. 本地锁弊端 (1). 如果只是在分布式某一个服务加本地锁,是没有作用的. (2). 可以使用分布式锁(集中化管理). b. 分布式锁 (1). 只有申请共享资源通过,才能使用.否则,拒绝.
3. 基于redis的分布式锁实现方案:
redis集群,原理是因为redis单线程串行处理.
(1). SETNX方案:
- SETNX(Set if not exists): a. 命令在指定的key不存在时,为key设置指定的值. b. SETNX Key Value设置成功,返回1.设置失败,返回0. c. 没有有效期的
- 原子操作(多个执行命令): Multi SETNX Key Value expire key seconds // 设置失效时间 exec
- 两条命令要求是原子操作: a. 如果有可能会某一条命令失败,如expire失败了,这把锁就没有有效期,就会变成死锁. b. 要求是要么都成功、要么都失败. c. redis把原子性的操作变成一个lua脚本.
(2). set方案:
- set key value NX PX milliseconds: a. 命令在指定的key不存在时,为key设置指定的值,并设置生存时间.