cas全称是compare and set,是一种典型的事务操作。

简单的说,事务就是为了存取数据库中同一数据时不破坏操作的隔离性和原子性,从而保证数据的一致性。

一般数据库,比如MySql是如何保证数据一致性的呢,主要是加锁,悲观锁。比如在访问数据库某条数据的时候,会用SELECT FOR UPDATE ,这MySql就会对这条数据进行加锁,直到事务被提交(COMMIT),或者回滚(ROLLBACK)。如果此时,有其他事务对被加锁的数据进行写入,那么该事务将会被阻塞,直到第一个事务完成为止。它的缺点在于:持有锁的事务运行越慢,等待解锁的事务阻塞时间就越长。并且容易产生死锁(前面有篇文章有讲解死锁)!

本文会介绍三种redis实现cas事务的方法,并会解决下面的虚拟问题:
维护一个值,如果这个值小于当前时间,则设置为当前时间;如果这个值大于当前时间,则设置为当前时间+30。简单的单线程环境下代码如下:

# 初始化
r = redis.Redis()
if not r.exists("key_test"):r.set("key_test", 0)def inc():count = int(r.get('key_test')) + 30 #1# 如果值比当前时间小,则设置为当前时间count = max(count, int(time.time())) #2r.set('key_test', count) #3return count

很简单的一段代码,在单线程环境下可以跑的很欢,但显然,是无法移植到多线程或者是多进程环境的(进程A和B同时运行到#1,获取了相同的count值,然后运行#2#3,会导致count值总共只增加了30)。而为了能在多进程环境下运行,我们需要引入一些其他的东西。

py-redis本身自带的事务操作

redis有这么几个和事务相关的命令,multi,exec,watch。通过这几个命令,可以实现‘将多个命令打包,然后一次性、按顺序执行,且不会被终端’。事务会从MULTI开始,执行EXEC后触发事件。另外,我们还需要WATCH,watch可以监视任意数量的键,当在调用EXEC执行事务时,如果任意一个键被修改了,整个事务不会执行。

下边是使用redis本身的事务解决cas问题的代码。

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,
互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
class CasNormal(object):def __init__(self, host, key):self.r = redis.Redis(host)self.key = keyif not self.r.exists(self.key):self.r.set(self.key, 0)def inc(self):with self.r.pipeline() as pipe:while True:try:#监视一个key,如果在执行期间被修改了,会抛出WatchErrorpipe.watch(self.key)next_count = 30 + int(pipe.get(self.key))pipe.multi()if next_count < int(time.time()):next_count = int(time.time())pipe.set(self.key, next_count)pipe.execute()return next_countexcept WatchError:continuefinally:pipe.reset()

代码也不复杂,引入了之前说到的multi,exec,watch,如果对事务操作比较熟悉的同学,可以很容易看出来,这是一个乐观锁的操作(咱们假设没人竞争来着,每次去拿数据的时候都不会上锁,真有人来改了再说。)乐观锁在高并发的情况下会显得很无力,文末的性能对比会显示这个问题。

使用基于redis的悲观锁

悲观锁,就是很悲观的锁,每次拿数据都会假设别人也要拿,先给锁起来,用完再把锁释放掉。redis本身没有实现悲观锁,但我们可以先用redis实现一个悲观锁。

ok,咱们现在有悲观锁了,做起事来也有底气了,根据上边的代码,咱们只要加上@ synchronized注释就能保证同一时间只有一个进程在执行。下边是基于悲观锁的解决方案。

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,
互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
lock_conn = redis.Redis("localhost")class CasLock(object):def __init__(self, host, key):self.r = redis.Redis(host)self.key = keyif not self.r.exists(self.key):self.r.set(self.key, 0)@synchronized(lock_conn, "lock", 10)def inc(self):next_count = 30 + int(self.r.get(self.key))if next_count < int(time.time()):next_count = int(time.time())self.r.set(self.key, next_count)return next_count

代码看上去少多了(因为引入了synchronized…)

基于lua脚本实现

上边两种方法都是用锁来实现的,锁的实现总会出现竞争的问题,区别无非是出现竞争了咋办的问题。使用redis lua脚本的实现,可以直接把这个cas操作当成一个原子操作

我们知道,redis本身的一系列操作,都是原子操作,且redis会按顺序执行所有收到的命令。先看代码

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:857662006 寻找有志同道合的小伙伴,
互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
class CasLua(object):def __init__(self, host, key):self.r = redis.Redis(host)self.key = keyif not self.r.exists(self.key):self.r.set(self.key, 0)self._lua = self.r.register_script("""local next_count = redis.call('get',KEYS[1]) + ARGV[1]ARGV[2] = tonumber(ARGV[2])if next_count < ARGV[2] thennext_count = ARGV[2]endredis.call('set',KEYS[1],next_count)return tostring(next_count)""")def inc(self):return int(self._lua([self.key], [30, int(time.time())]))

这里先注册了这个脚本,后边可以直接去使用他。关于redis lua脚本的文章有不少,感兴趣的可以去搜搜看,这边就不赘述了。

性能对比

这边的测试只是一个非常简单的测试(不过还是能看出效果来的),测试换机就是自己的开发机,数字看个大小就行了。

分别测了三种操作在单线程,五个线程,十个线程,五十个线程情况下,进行1000次操作各自的表现,时间如下

     optimistic Lock pessimistic lock  lua
1thread       0.43       0.71 0.35
5thread       5.80       3.10 0.62
10thread      17.80       5.60 1.30
50thread      245.00       29.60 6.50

依次是redis本身事务实现的乐观锁,基于redis实现的悲观锁以及lua实现。

在比较悲观锁和乐观锁之前,需要先说明一点,这边的测试对乐观锁不是很公平,乐观锁本身就是假设不会有很多的并发的。在单线程情况下,悲观锁要差一些。单线程下,不存在竞争关系,悲观锁耗时长仅因为是多了一次redis的网络交互。随着线程的增加,悲观锁的性能逐渐变好,毕竟悲观锁本身就是为了解决这种高并发高竞争的环境而诞生的。在50线程的时候,乐观锁的实现单次操作的时间要0.245秒,非常恐怖,如果是生产环境,几乎都不能用了。

至于lua的性能,快的不可思议,几乎就是线性增加。(50线程的情况下,平均的1000次完成时间是6.5s,换言之,6.5秒内执行了50 * 1000次cas操作)。

以上测试都是本地redis,本地测试,如果redis是远端的,网络交互时间会增加,lua优势会更加明显。

python实现redis三种cas事务操作相关推荐

  1. python redis事务_python实现redis三种cas事务操作

    cas全称是compare and set,是一种典型的事务操作. 简单的说,事务就是为了存取数据库中同一数据时不破坏操作的隔离性和原子性,从而保证数据的一致性. 一般数据库,比如MySql是如何保证 ...

  2. Redis三种特殊数据类型

    Redis三种特殊数据类型 geospatial 地理位置 朋友的定位, 附近的人, 打车的距离计算 Redis的Geo在Redis3.2版本就推出了.这个功能可以推算地理位置信息, 两地之间的距离, ...

  3. Spring的4种事务管理(1种编程式事务+三种声明事务)

    2019独角兽企业重金招聘Python工程师标准>>> Spring的4种事务管理(1种编程式事务+三种声明事务) 一.Spring事务的介绍 二.编程式事务xml的配置 注入后直接 ...

  4. Redis三种特殊数据类型——Geospatial地理空间

    Redis三种特殊数据类型:bitmaps位图.hyperloglog基数统计 和 geospatial 地理空间 一.介绍 Redis GEO 用于存储地理位置信息,并对存储的信息进行操作,该功能在 ...

  5. pca算法python代码_三种方法实现PCA算法(Python)

    主成分分析,即Principal Component Analysis(PCA),是多元统计中的重要内容,也广泛应用于机器学习和其它领域.它的主要作用是对高维数据进行降维.PCA把原先的n个特征用数目 ...

  6. Python二叉树的三种深度优先遍历

    Python二叉树的三种深度优先遍历 一.广度优先遍历和深度优先遍历 对二叉树进行遍历(traversal)是指依次对树中每个节点进行访问,在遍历的过程中实现需要的业务. 对树的遍历方式有广度优先遍历 ...

  7. 执行 Python 程序的三种方式及Python 的 IDE —— `PyCharm`

    执行 Python 程序的三种方式 3.1. 解释器 python / python3 Python 的解释器 # 使用 python 2.x 解释器 $ python xxx.py# 使用 pyth ...

  8. python和c++哪个好-Scratch和Python与C++三种编程语言选哪个好

    Scratch和Python与C++三种编程语言选哪个好? 目前少儿编程培训机构主要提供的有三种主流课程,分别是Scratch.Python和C++,面对这三种课程家长该如何选择呢,到底哪种课程适合孩 ...

  9. python定时爬虫三种方法

    python定时爬虫三种方法 第一种 import timefrom scrapy import cmdlinedef doSth():# 把爬虫程序放在这个类里 zhilian_spider 是爬虫 ...

最新文章

  1. C++ 的语言杂谈(一)--C++不是新手友好的
  2. 在Eclipse中写第一个hibernate小例子
  3. spark集群启动正常,但是提交任务后只有一个节点(slave/worker)参与集群计算
  4. 函数式编程基础_在收件箱中免费学习函数式编程的基础
  5. c语言 三个小球排排坐,关颖三个孩子排排坐 太萌啦
  6. 深度综述|基因与疾病关系研究的百年进展
  7. 设计师,程序员,当心字体侵权
  8. 迪士尼超级IP版图日趋完整
  9. html 内容写入数据库中,FoxPro数据库写入html文件中
  10. 1128UI自动化测试经验分享-显式等待(二)expected_conditions模块、visibility_of_element_located(locator)
  11. 多肽TAT接枝/功能肽RGDC修饰荧光碳量子点/碳量子点修饰多肽LyP-1的制备研究
  12. 为什么HashMap默认初始容量为2次幂?不是2次幂会怎样?讲讲 HashMap 扰动函数?
  13. 用微分和差分方程描述的因果LIT系统
  14. 方舟服务器显示等待发布,《明日方舟》开服既炸服的这波操作《方舟生存进化》永远也学不会...
  15. echarts 画中国地图
  16. 秒杀系统----热点/秒杀商品如何处理?
  17. reacr富文本编辑器
  18. SAP携手“大数据之都” 共推大数据创新
  19. MEM/MBA数学基础(01)预备知识
  20. 数学:确定性的丧失---第八章 不合逻辑的发展:天堂之门

热门文章

  1. Asp.net正则获取html内容
  2. RHEL6入门系列之二十七,源码安装及软件安装综合实例
  3. 让OA选型与实施不再苦口难言
  4. [转]另一个SqlParameterCollection 中已包含 SqlParameter[解决方案]
  5. JDBC+Servlet+JSP整合开发之22.JSP简介
  6. LDAP 查询基本知识
  7. lg gram 笔记本 linux,lg gram 15笔记本使用雨林木风u盘安装win7系统教程?
  8. 切割图形_重庆Q3245R锅炉板加工几何图形2021新闻
  9. User Exits,Customer Exits,BADI and BTE基本概念
  10. SAP批次级别的意义及启用操作