五.Redis事务与乐观锁
本文最后更新于:2022年1月13日 下午
Redis事务
Redis事务本质:一组命令集合。一个事务中的所有命令都会被序列化,在事务执行过程中,会按照顺序执行。
1 |
|
Redis事务:
- 开启事务(multi)
- 命令入队列(一系列命令)
- 执行事务(exec)
所有的命令在事务中,并没有直接执行,而是只会在执行命令发起的时候才会执行。
1 |
|
Redis中的两种错误:(同java基本一致)
- 编译型异常(代码有问题,命令语法错误):事务中的所有命令都不会被执行!(入队失败)
- 运行时异常(分母为0,数组越界等):如果事务队列中的某一条命令存在运行时异常,那么在执行事务时,其他命令是可以正常执行的,错误的命令会抛出异常。
1 |
|
Redis事务并没有隔离级别的概念
重点:Redis中单条命令是原子性的,但事务并不保证原子性(acid没有全部实现,与关系型数据库不同),一旦事务中(命令队列中)有一条命令执行失败(运行时异常),并不影响整个事务的执行,而mysql的事务具有原子性,在一个事务中,多条命令,一旦有一条执行失败,其他的也无法成功执行。
一次性,顺序性,排他性地执行一系列命令
- exec命令执行之前,多个命令被放入队列缓存中
- exec命令执行后,缓存队列中的命令顺序执行,一旦有一个错误(运行时异常),不影响其他命令的执行
- 在事务执行过程中,其他客户端提交的命令请求不会插入到当前的缓存命令队列中,这里只是保证在执行过程中,在将命令添加进入队列的过程中,是不会保证不被其他客户端命令插入的
- 不支持回滚,没有实现发生错误直接回滚的功能,redis的事务更像一个命令打包的功能
为什么不支持回滚?官方回答:
1 |
|
Redis实现乐观锁
- 悲观锁:见名知意,很悲观,担心数据会被修改,对读写操作都上锁,自己用完数据后,就会进行解锁。那么其它线程进行操作时必须等待,效率相对较低。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁、表锁、读锁、写锁等。都是在操作之前先上锁让别人无法操作该数据。
- 乐观锁:顾名思义,很乐观,每次取数据的时候并不担心自己的数据会被修改。数据库实现乐观锁,通常使用的是version(数据版本),来表示数据的。每次取数据的时候,连同数据版本version一起取出。当读取出数据,对数据进行更改时,会将数版本version 加一,然后提交到数据库,此时比较数据版本version,如果提交的数据版本version大于当前数据库对应记录的数据版本version,那么提交成功。否则小于等于都会提交失败。需要重新从数据库取数据。
- 乐观锁适用场景:频繁读取数据的场景,因为读取数据并不会上锁。但是当有大量数据写入的时候,会频繁的提交不成功,会重新读取数据,再提交。
- 悲观锁适用场景:频繁写入数据的场景,因为不管是读还是写 都会上锁,如果大量写入数据,为了数据安全上锁是有必要的,相反乐观锁就会大量的读取提交操作。但是当有大量数据读出的时候,效率低下。
Redis实现乐观锁:
Redis通过使用watch命令来实现乐观锁,watch命令用于监视一个或多个key,如果在事务执行之前,这个(或这些)key被其他命令所改动,那么这个事务将被打断。
大多数是基于数据版本(version)的记录机制实现的。即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个”version”字段来实现读取出数据时,将此版本号一同读出,之后更新时,对此版本号加1。此时,将提交数据的版本号与数据库表对应记录的当前版本号进行比对,如果提交的数据版本号大于数据库当前版本号,则予以更新,否则认为是过期数据。
“ Redis使用WATCH命令实现事务的“检查再设置”(CAS)行为。作为WATCH命令的参数的键会受到Redis的监控,Redis能够检测到它们的变化。在执行EXEC命令之前,如果Redis检测到至少有一个键被修改了,那么整个事务便会中止运行,然后EXEC命令会返回一个Null值,提醒用户事务运行失败。”
如果在添加watch之后,事务正常exec完成,会自动释放锁(监视),其他的情况,需要手动执行unwatch命令释放监视。
watch的key是对整个连接有效的,事务也一样(但事务被执行后就不存在了),如果连接断开,监视和事务都会被自动清除,discard和unwatch命令也会清除连接中的所有监视。
操作实例:
1 |
|
本文作者: ziyikee
本文链接: https://ziyikee.fun/2021/11/16/%E4%BA%94-Redis%E4%BA%8B%E5%8A%A1/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!