0


关于 watched variable changed 异常的详解

文章目录

在编写测试案例的演示脚本时,使用的线程池与 redis 事务管理的相关知识,运行代码的时候出现了 " watched variable changed " 的异常抛出。

❤️‍🔥 演示案例的示例脚本

  • 代码演示的案例内容如下:

# coding:utf-8import redis
import random
from redis_db import redis_Pool         # 导入 redis 连接池from concurrent.futures import ThreadPoolExecutor       # 导入 concurrent.futures 模块的 线程池类 ThreadPoolExecutor"""
生成随机用户的id
"""
s =set()# 设置空集合,将其赋值给变量 s"""
定义一个死循环,向集合中添加 1000个 不重复的用户id;
因为使用的是 random 模块,不能保证生成的用户 id 是不重复的,所以 while 循环是不能够循环 1000 次的;
所以 while 循环的结束条件是 s 的长度为 1000 的时候,小于 1000 的情况下,是需要继续向 s 中添加 用户id 的
"""whileTrue:iflen(s)==1000:break
    num = random.randint(10000,100000)
    s.add(num)# 将随机生成的 用户id 添加到 s 中去"""
获得 redis 连接
"""
con = redis.Redis(
    connection_pool=redis_Pool
)"""
利用 try...except...finally 捕获异常
1、需要判断 redis 中是否包含有秒杀用到的数据,这段程序在调试的过程中肯定会运行不止一次,如果已存在秒杀数据,势必会影响下一次运行。
2、如果第一次运行的结果,秒杀成功的记录没有被删除,第二次再次运行的时候又会将秒杀成功的记录再次添加,这就非常不合理了。
3、所以每一次运行脚本之前,就需要将与 秒杀案例 相关的 redis 的记录全部删掉
"""try:
    con.delete("kill_total","kill_num","kill_flag","kill_user")# 删掉 redis 的秒杀案例相关的记录
    con.set("kill_total",50)# 写入 秒杀商品总数量 kill_total,数量 50
    con.set("kill_num",0)# 写入 秒杀活动成功抢购的数量 kill_num,初始数量 0
    con.set("kill_flag",1)# 写入 秒杀活动 的状态 kill_num,参数值 1 ;这里给多少随意,只是用来判断活动的标识。
    con.expire("kill_flag",600)# 写入 秒杀活动 的状态的过期时间,十分钟,600秒"""kill_user 不用创建,在后面用到的时候像 kill_user 添加记录时会自动创建"""except Exception as e:# 捕获异常并打印输出print(e)finally:# 回收 redis 连接del con


"""
定义线程池,传入 缓存的线程池数量 200
"""
executor = ThreadPoolExecutor(200)"""
创建线程池任务 - 秒杀
1、参与秒杀时,需要重点关注秒杀的数据
2、利用 redis 事务去执行 秒杀任务 数据的记录 - pipeline()
"""defbuy():"""获取 redis 链接(区别于第 31 行的 con 连接);创建 redis 事务 - pipeline()"""
    connection = redis.Redis(
        connection_pool=redis_Pool
    )"""定义 pipline """
    pipline = connection.pipeline()"""利用 try...except...fi nally 捕获异常;并最终回收链接。"""try:"""通过 connection 判断 '秒杀' 是否处于有效状态;如果 'kill_flag' 为 0 的话,则秒杀任务就不存在,没有任何实际意义"""if connection.exists("kill_flag")==1:"""在秒杀活动生效的情况下,观察 成功抢购数 与 成功抢购的用户ID 的两个数据"""
            pipline.watch("kill_num","kill_user")"""获取 秒杀商品 的总数,需要转码与转换 int 数据类型"""
            total =int(pipline.get("kill_total").decode("utf-8"))"""获取 秒杀活动成功抢购 的数量,需要转码与转换 int 数据类型"""
            num =int(pipline.get("kill_num").decode("utf-8"))"""
            当 秒杀活动成功抢购 的数量 小于 秒杀商品 的总数时[num < tota],秒杀活动还未结束,可以继续参与秒杀;
            当 有效时,将 kill_num 的数量加 1 ,并将 user_id 写入 kill_user 记录 
            """if num < total:"""利用 pipline 的 multi() 函数,开启 redis 的事务"""
                pipline.multi()"""利用 pipline 的 incr() 函数,将 'kill_num' 记录 +1 """
                pipline.incr("kill_num")"""利用 s 集合 的 pop() 函数删除并获取 random 随机生成的 用户id ,赋值给 user_id 变量"""
                user_id = s.pop()"""利用 pipline 的 rpush() 函数,将秒杀到商品的用户的 'user_id' 传入给 kill_user记录 """
                pipline.rpush("kill_user", user_id)"""利用 pipline 的 execute() 函数,提交 redis 的事务"""
                pipline.execute()except Exception as error:print(error)finally:"""判断 pipline 是否存在,存在的话,回收 pipline 事务"""if"pipline"indir():
            pipline.reset()del connection


"""
利用 for 循环,模拟 1000 名用户参与抢购秒杀活动;
将 bug 函数作为线程池任务函数,传给线程池来执行。
"""for i inrange(0,1000):
    executor.submit(buy)print("秒杀已经结束")

  • 在运行之后会出现如下的异常捕获:
Watched variable changed.
Watched variable changed.
Watched variable changed.
Watched variable changed.

❤️‍🔥 异常捕获分析

  • Watched variable changed. 的提示信息为 观察的变量已更改。
  • 这是因为我们在使用 pipline 针对 redis 事务中的某些重要数据进行观察的时候,数据发生了改变所造成的。
  • 因为这个模拟 “抢购秒杀” 的演示案例,在运行的时候,有其他客户端抢到了 秒杀商品 修改了 kill_num 的值, 那么当前客户端的事务就会失败。 程序需要做的, 就是不断重试这个操作, 直到没有发生碰撞为止。
  • 这种形式的锁被称作乐观锁, 它是一种非常强大的锁机制。 并且因为大多数情况下, 不同的客户端会访问不同的键, 碰撞的情况一般都很少, 所以通常并不需要进行重试。

  • Watched variable changed:watch 执行之后, execute执行之前, 有其他客户端修改了 watch 中指定的key的值, 程序的事务就会抛出异常 redis.WatchError。
  • 还有我们演示的案例是一个商品秒杀的模拟案例,使用的是线程池的多线程技术,线程池的任务是并发执行的,当一个线程执行完毕后或因阻塞性任务而让出CPU的使用权后,会马上执行另一个的线程,这种执行原则是无序的,当一个线程执行完毕后会返回对应线程的执行结果。
  • 正因为并发执行线程池中的任务,所以在 redis 中事务管理需要对目标数据进行 "监视" ,即:"Watched variable changed",从而有效的避免其他线程对目标数据进行“非法”修改。
  • 抛出该异常是正常的,在程序中捕获到了该异常并进行了输出,因此控制台会输出该内容,是没影响的。
标签: redis 缓存 数据库

本文转载自: https://blog.csdn.net/weixin_42250835/article/details/127189687
版权归原作者 不渴望力量的哈士奇 所有, 如有侵权,请联系我们删除。

“关于 watched variable changed 异常的详解”的评论:

还没有评论