Redis数据库系列(四)、Redis事务、乐观锁和分布式锁

第四章、Redis事务、乐观锁和分布式锁

什么是事务机制?

4.1、关系型数据库中的事务机制遵循ACID规则

关系型数据库例如MySql、Oracle;
事务的英文是transaction,以现实中的银行交易做示例,关系型数据库事务机制有四大特性:

  1. A(Atomicty)原子性:

    原子性,也就是说在事务里的所有操作,要么都做,要么都不做,事务成功的条件是事务中的所有操作都成功执行,只要有一个操作失败,整个事务失败,将会发生回滚。
    eg:银行转账,张三向李四转100元,分为两个步骤
    (1)、从张三账户取出100元
    (2)、向李四账户存入100元
    事务机制就是要保证,这两个步骤要么同时执行成功,要么同时执行失败;如果说没有事务机制,从张三账户取出100元的同时银行突然断电了,那么李四账户中却没有转入100元,这时候张三账户却少了100元,张三恼死也没底说理,你说这气不气?

  2. C(Consistency)一致性

    一致性也就是我们常说的数据的完整性约束,事务的运行不会改变数据的完整性约束。
    怎么说:例如a+b=10,如果一个事务改变了a,那么b随之也要改变,才能保证a+b的结果不会改变仍然为10,这就叫数据的完整性约束(即一致性),否则事务失败。

  3. I(Isolation)独立性(隔离性)

    事务的独立性,也就是我们常说的隔离性,所谓隔离性就是说并发事务彼此之间不会受到干扰,如果一个事务要访问的数据正在被另外一个事务修改,只要另外一个事务未提交事务,第一个事务正在访问的数据就不会受到干扰。

  4. D(Durability)持久性

    持久性也就是说事务一旦提交,事务中所作的数据操作将会永久保存在数据库中,即使出现宕机现象数据也不会丢失。

4.2、非关系型数据库的事务机制(NoSql)

非关系型数据库例如Hbase、MongodDB、Redis;
这里我们讲解Redis数据库的事务机制

  • redis事务机制定义:

    • redis的事务机制中,可以一次执行多条命令,本质上是将一组命令放入集合队列中,而且这些命令将会被序列化,即按顺序执行并且不会被其他命令插入干扰和中断。
    • 在redis中,事务的作用就是在一个队列中,一次性、顺序性、排他性的执行一系列命令。
  • 事务的执行顺序和周期:

    • 创建事务:使用multi开启一个事务。
    • 加入队列:在开启事务后,每次操作的命令将会插入到队列中,并被序列化,同时这些命令在事务未提交前不会被执行。
    • 提交事务:使用exec提交事务。
  • 有关事务常用的命令

    • multi 开启事务,记录一个事务块的开始,执行后通常会返回OK,这个时候用户可以输入多条操作指令,redis将会把这些指令逐条放入集合队列中,然后序列化这些指令。
    • discard 回滚事务,要在提交事务之前使用,事务中所有的指令操作将会失效。
    • exec 提交事务,按顺序执行事务中的所有操作命令。
    • watch(类似于乐观锁) 监视一个或多个数据, 注意监视数据要在multi开启事务之前,如果在事务未提交之前,正监视的数据被其他用户所修改了,那么回头再提交这个事务,事务中的命令都会失效。
    • unwatch(释放锁)
    • setnx(分布式锁)是(set if not exists)的缩写,也就是说只有key不存在的时候才会设置

    setnx key value
    当且仅当key不存在时,将key的值设置为value;
    若设定的key已经存在,则setnx不做任何操作。

    • epxire (用于设置key的过期时间)
    #后面的second代表多少秒后释放该锁变量
    expire key second
    
    • pexpire(用于设置key的过期时间)
    #后面的miliseconds代表多少毫秒后释放该锁变量
    pexpire key miliseconds
    

4.3、实际操作演示

  • 开启事务和提交事务
    在这里插入图片描述
  • 如果事务过程中所包含的命令语法出现错误,在exec提交事务时,整个事务将会自动回滚;
    在这里插入图片描述
  • 而如果是命令逻辑赋值错误,在exec提交事务时,在这行赋值命令前面的正确命令都会事务提交成功,只有赋值错误这行代码会自动事务回滚。
    在这里插入图片描述
  • 事务回滚,一定要在提交事务之前进行回滚
    在这里插入图片描述
  • 监视(乐观锁)
    watch
  • 客户端1:
    在这里插入图片描述
  • 客户端2(在客户端1未提交事务之前另一个客户执行修改操作):
    在这里插入图片描述
  • 客户端1(此时客户端1提交事务,发现上述事务机制过程全部无效):
    在这里插入图片描述
  • 分布式锁
  • 客户端1:
    在这里插入图片描述
  • 客户端2(在客户端1设置的释放时间未到时,执行修改同一个变量的操作):
    在这里插入图片描述
Redis分布式锁通常使用的是乐观锁策略,它基于Key-Value存储的原子性操作来实现。在执行数据库更新时,其核心逻辑通常包含以下几个步骤: 1. 获取锁:客户端尝试获取锁,通常是设置一个具有过期时间的Key(比如`mylock:123`),并设置值为某个唯一的标识符(如当前时间戳)。 ```python lua_script = """ if redis.call("get", KEYS) == ARGV then -- 锁未被其他线程修改,设置过期时间 return redis.call("setnx", KEYS, ARGV, "EX", ARGV) else -- 锁已被其他线程占用,返回0或nil return 0 end """ result = self.client.evalsha(script_sha, keys=[lock_key], args=[identifier, lock_value, lock_timeout]) ``` 2. 重试机制:如果获取失败(`result`为0或nil),客户端会进入一个循环,不断尝试直到获取到锁为止,或者超时。 3. 更新操作:一旦获得锁,客户端可以在一个事务中进行数据库的更新操作。这个操作通常在一个Lua脚本中执行,以保证原子性。 ```lua lua_script = """ -- 执行你的数据库更新操作 -- 假设操作是 incr counter local updated = redis.call("incr", "counter") if updated > 0 then -- 成功更新,删除锁 return redis.call("del", KEYS) else -- 更新失败,解锁 return 0 end """ result = self.client.evalsha(script_sha, keys=[lock_key], args=[]) ``` 4. 错误处理和解锁:如果数据库更新操作失败(`result`还是0),需要解锁以释放资源。如果更新成功,因为设置了过期时间,锁会在指定时间内自动失效。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一宿君

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值