论 大并发 下的 乐观锁定 Redis锁定 和 新时代事务
在 《企业应用架构模式》 中 提到了 乐观锁定,
用 时间戳 来 判定 交易 是否有效, 避免 传统事务 的 表锁定 造成 的 瓶颈 。
在 现在的 大并发 的 大环境下, 传统事务 及其 表锁定 以及 事务带来 的 性能消耗, 确实 不能适应 当今 的 大并发 的 场景 了 。
感觉 传统事务 也就只能用在 办公系统 了, 哈哈哈哈 。
但是 传统事务 的 表锁定 是 合理的, 表锁定 使得 事务中 其它 线程 不能 读写 表 。
不能 写, 这个容易理解, 不能 读 是怎么回事 ?
因为 读取表的结果 会 作为 系统 决策行为 的 依据, 所以 也不能 读 。
比如, 一个商品已经卖出去了, 就不能再卖给其它用户 。
那么, 用 乐观锁定 能解决这些问题吗 ?
乐观锁定 的 提出 是 很有意义的,
但是不太靠得住, 为什么呢 ?
时间戳 通常 取到 毫秒, 但 如果 并发密度 超过了 毫秒级, 达到了 比如 微秒级, 那 ?
当然 理论上 可以随机的取 其中 一个用户 作为 成功者, 其他用户作为 失败者(牺牲者?) 。
不过 理论上, 这还不是一种 完备 的 做法 。
我们再来看看 Redis 锁定,
Redis 提供了 对 数据(对象) 的 锁(Lock), 以及 队列 Queue 等 数据类型, 以及 对 Queue 的 Block 操作 。
我们可以利用 Redis 来锁定 某个 ID 的 业务实体 (某个 订单号 的 订单), 然后 对 这个 业务实体(订单) 进行 相关的操作(事务 / 交易),
这样 达到 多个 线程(用户) 对 同一个 订单 的 操作(交易) 顺序进行 。
这种做法 的 实质 是 行锁定 , 那为什么不通过 数据库 来实现 行锁定 呢 ?
因为 我 不知道 数据库 行锁定 的 语法,,,,,,
其次, 数据库 行锁定 的 实现 会 更加 复杂 和 重量,
Redis 的 对象锁 则 简单 而 轻量 。
事实上, 如果 把 上述过程 简化 下来,
我们 只是 需要 有一个 “Flag” 在 Redis 里 可以 供 多台 Server 的 线程间 同步协作 即可 。
严格的讲, 在 负载均衡 (集群), 即 多台 Server 的 情形下 才需要使用 Redis (分布式缓存) ,
如果 是 单台 Server , 则 使用 进程内 的 变量 来 作为 “Flag” Lock 即可 。
此时, 情况 则 退化为 进程内 多线程 之间的 同步协作 。
同时, 严格的讲, Redis 只需要提供一个 “Flag”, 或者说, Redis 只需要提供一个 锁机制 ,
并不需要 将 具体的 业务数据(对象) 保存 到 Redis,
进行 一笔交易 时, 不需要 到 Redis 查找 业务对象(比如 订单), 如果查不到再到 数据库 查, 更新时 先更新 Redis, 再更新 数据库 ,
没有必要这样 。
Redis 只要提供 “Flag” Lock 来 确保 顺序进入(同步协作), 具体的操作直接 读写 数据库 即可 。
所以, Redis 的 真正意义 在于 共享内存, 而不是 数据缓存 。
可以看看我昨天写的 《论 业务系统 架构 的 简化 (二) 用 关系数据库 作 缓存》 https://www.cnblogs.com/KSongKing/p/9928412.html
除了 锁, 事务 还有 另一方面, 数据完整性 。
比如, 更新 A, B, C 三张表, A, B 成功, C 失败, 于是事务会回滚, A, B 恢复原来的数据 。
要实现 数据完整性, 需要 表锁定 和 事务日志(这会带来 性能消耗),
可见,
数据完整性 同样也会成为 大并发 的 瓶颈 。
所以,我们这里 提出 一个 “乐观事务” 概念,
即 对于 每次交易, 都是 Insert , 而不会 反复 的 去 Update 。
假如一个交易 要 更新 3 个表, A 表 为 主表, B, C 表通过 A 表 的 ID 关联,
那么, 这个交易 对 A,B,C 3 个表都是 Insert 操作, 在 最后 事务成功 时 将 A 表里的 “生效” 栏位 更新 为 “Y”,
以此 表示 事务成功 。
显然, 这种做法 并不是对 所有场合 都适用, 它会让一些 小场景 变得麻烦 。
但是, 对于 前端 海量 用户 海量 并发 的 场景, 可以使用 这种做法 。
P : 我们大概可以把 每秒 100万 ~ 1000万 的 交易量 称为 “海量”, 把 每秒 1000万 以上 的 交易量 称为 “天量” 。
Redis 锁 + 乐观事务 = 新时代事务
我们来看一下 新时代事务 处理 3 个场景 :
1 订单(交易)
2 秒杀
3 商品库存计数
1 订单(交易),
用 Redis 锁 来 锁定, 用 乐观事务 执行 更新数据, 具体的 大家 自己想象 吧
2 秒杀
用 Redis 存一个 对象 记录 被 哪个 用户秒杀, 同时 加上 Redis 锁, 这样 用户 就可以 顺序 的 获取这个对象,
第一个获得这个对象的用户 就 秒杀 成功, 并更新这个 对象 的 状态 表示 秒杀成功 。
3 商品库存计数
同 Redis 存一个 对象 记录 库存量, 用户将 商品 放入 购物车 则 对象.库存量 - 1, 用户将 商品 从 购物车 删除 则 对象.库存量 + 1 ,
通过 对象锁 来 确保 用户顺序 获取对象 查看 和 更新 库存量 。
可以看看我前几天写的 《一个类似 Twitter 雪花算法 的 连续序号 ID 产生器 SeqIDGenerator》 https://www.cnblogs.com/KSongKing/p/9918412.html
通常, 一个实体 会 对应一张表,
我们以 订单 为例,
一笔 订单 对应 订单表 里的 一笔资料,
订单表 会有一张 对应的 订单_Trans 表, 用来记录 发生 在 订单 上的 乐观事务,
订单_Trans 表 会有一个 ID , 表示 Transaction ID ,
订单_Trans 表 同时还包含 订单 需要更新 的 栏位,
这样, 发生 一次 事务 时, 会向 订单_Trans 表 insert 一笔资料, 栏位 的 值 是 订单 本次 更新的 栏位 的 值,
事务 成功 后, 会将 订单 表 里的 “trans” 栏位 更新为 这次 事务 的 ID, 即 订单_Trans 表 的 ID ,
这样, 通过 订单.Trans = 订单_Trans.ID 关联, 可以查询到 订单 在 本次 事务 更新后的 栏位 的 值 。
一笔订单 在 订单_Trans 表 中可以对应 多笔 事务记录, 这些 事务记录, 有成功的, 也有不成功的 。
乐观事务 的 关键 在于 最后只能 更新 一张表 的 (一个)栏位 来 决定 事务是否成功,
如果要 更新 多个 表, 那又 回到 传统事务 了 。
所以, 这就看 针对性 的 设计 。
对于 淘宝 天猫 这样的 海量购物, 应该设计为 单项递增数据 的 架构, 也就是 只 insert , 不 update ,
但问题是 如果 一个 交易 里 要 insert 多个 表 呢 ?
也许可以用 乐观事务 A 表 关联 B 表, B 表 关联 C 表, ……
这样只需要 最后 更新 A 表里的一个 生效 栏位 , 就可以 表示 事务 是否成功 。
因为 可以 根据 A 表 关联 到 B 表 , B 表 关联 到 C 表 ,
或者有一个 Trans 表, 通过 Trans 表 的 ID (Trans ID) 来 关联 A, B, C 表 ,
这样可以 查询出 某一个 事务(Trans ID) 在 A, B, C 表 里 insert 的 资料,
然鹅 。
不过 说不定 这种方法 真的 有人在用 喔 ~!
数据完整性 最好 还有由 数据库 来实现, 这才是合理的 。
关键在于,
数据库 应该使用 行锁定 来 实现 数据完整性 。
就是说, 我们应该让 数据库 用 行锁定 来 执行 事务 。
淘宝 天猫 应该 自己 已经对 数据库 做过这种 优化了 。
也就是说, 淘宝 天猫 的 数据库 的 事务 是用 行锁定 的 方式 执行的 ,
当然, 这只是我的 猜测, 啊哈哈哈哈 。
Redis 锁定 + 数据库 行锁定 事务 (行级事务) + 数据库 使用 固态硬盘
这样 来 应对 大并发, 你觉得呢 ?
论 大并发 下的 乐观锁定 Redis锁定 和 新时代事务的更多相关文章
- 高并发场景系列(一) 利用redis实现分布式事务锁,解决高并发环境下减库存
原文:http://blog.csdn.net/heyewu4107/article/details/71009712 高并发场景系列(一) 利用redis实现分布式事务锁,解决高并发环境下减库存 问 ...
- 大并发连接的oracle在Linux下内存不足的问题的分析
大并发连接的oracle在Linux下内存不足的问题的分析 2010-01-28 20:06:21 分类: Oracle 最近一台装有Rhel5.3的40G内存的机器上有一个oracle数据库,数据库 ...
- 大压力下Redis参数调整要点
调整以下参数,可以大幅度改善Redis集群的稳定性: 为何大压力下要这样调整? 最重要的原因之一Redis的主从复制,两者复制共享同一线程,虽然是异步复制的,但因为是单线程,所以也十分有限.如果主从间 ...
- 【转载】.NET中锁6大处理方法 悲观乐观自己掌握
我们为什么需要锁? 在多用户环境中,在同一时间可能会有多个用户更新相同的记录,这就会产生冲突,这个就是著名的并发性问题. 图 1 并行性问题漫画 如何解决并发性问题? 借助正确的锁定策略可以解决并发性 ...
- 《.NET 5.0 背锅案》第7集-大结局:捉拿真凶 StackExchange.Redis.Extensions 归案
第1集:验证 .NET 5.0 正式版 docker 镜像问题 第2集:码中的小窟窿,背后的大坑,发现重要嫌犯 EnyimMemcachedCore 第3集-剧情反转:EnyimMemcachedCo ...
- 大并发server架构 && 大型站点架构演变
server的三条要求: 高性能:对于大量请求,及时高速的响应 高可用:7*24 不间断,出现问题自己主动转移.这叫fail over(故障转移) 伸缩性:使用跨机器的通信(TCP) 另外不论什么网络 ...
- .net core 下使用StackExchange的Redis库访问超时解决
原文:.net core 下使用StackExchange的Redis库访问超时解决 目录 问题:并发稍微多的情况下Redis偶尔返回超时 给出了参考网址? 结论 小备注 引用链接 问题:并发稍微多的 ...
- 处理大并发之五 使用libevent利器bufferevent
转自:http://blog.csdn.net/feitianxuxue/article/details/9386843 处理大并发之五 使用libevent利器bufferevent 首先来翻译一段 ...
- ab测试大并发错误
转载自http://xmarker.blog.163.com/blog/static/226484057201462263815783 apache 自带的ab工具测试,当并发量达到1000多的时候报 ...
随机推荐
- pragma comment的使用 pragma预处理指令详解
pragma comment的使用 pragma预处理指令详解 #pragma comment( comment-type [,"commentstring"] ) 该宏放置一 ...
- Linux平台搭建-----C语言
下面内容是新手上路,各位高手路过勿喷!因为我第一次发布,可能页面设置或者其他做的不好,还请见谅~该文章只是作为我学习C语言的笔记以及记录学习进程的. 零基础学习C语言---搭建Linux平台开发环境 ...
- springsecurity基于数据库验证用户
之前的springsecurity程序都是将数据存放在内存中的,通过 <security:user-service> <security:user name="user&q ...
- 常见无线DOS攻击
记录下自己最近一段时间对无线渗透学习的笔记. 无线DOS就是无线拒绝服务攻击.主要包括以下几种攻击类型:Auth Dos攻击.Deauth Flood攻击.Disassociate攻击及RF干扰攻击等 ...
- vue优势
Vue.js是一个轻巧.高性能.可组件化的MVVM库,同时拥有非常容易上手的API: 我们都知道单页面应用:页面切换快 ,首屏时间稍慢,SEO差 js 渲染 (多页面应用: 首屏时间快 ...
- 2019-04-01-day023-对象实例的反射实例化
学习方法 学练改管测 听别人说 读 input 自己说 自己写 output 解决语法错误 解决逻辑错误 ##内容回顾 ##继承 多态 封装 property classmethod staticme ...
- 【Python】多线程-1
#练习:创建一个线程 from threading import Thread import time def run(a = None, b = None) : print a, b time.sl ...
- EPOCH, BATCH, INTERATION
CIFAR10 数据集有 50000 张训练图片,10000 张测试图片.现在选择 Batch Size = 256 对模型进行训练. 每个 Epoch 要训练的图片数量: 训练集具有的 Batch ...
- 大数据-01-安装Hadoop
环境 服务器:ubuntu-16.04.3-desktop-amd64.iso 创建hadoop用户 sudo useradd -m hadoop -s /bin/bash 本文中会大量使用到sudo ...
- ix 混合索引
raw_datas #DateFrame diff_index_list = [] #行index #多行所有列索引 raw_datas.ix[diff_index_list] #多行一列索引raw_ ...