Redis 学习教程·五 —— Redis 事务

Redis 的基本事务操作

Redis 事务

Redis 单条命令是保持原子性的,但是Redis 的事务没有原子性!!!

Redis 事务的本质: 一组命令的集合

一个事务中的所有命令都会被序列化,在事务执行的过程中,会按照顺序执行

----- 队列 set set set 执行 ---

事务的特点:

  • 一次性
  • 顺序性
  • 排他性
  • 执行一些列的命令

Redis 的事务没有隔离级别的概念

Redis 的事务:

  • 开启事务(multi
  • 命令入队(......
  • 执行事务(exec
  • 放弃事务(discard
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> multi  # 开启事务
OK
# 命令入队
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> exec  # 执行事务
1) OK
2) OK
3) "v2"
4) OK
127.0.0.1:6379> multi  # 开启事务
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> discard  # 取消事务
OK
127.0.0.1:6379> get k4  # 事务队列中的命令都不会执行
(nil)

常见异常

  • 编译型异常:

    代码有问题,命令有错,事务中的所有命令都不会被执行

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> getset k3  # 错误命令
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379> set k4 v4 
QUEUED
127.0.0.1:6379> set k5 v5
QUEUED
127.0.0.1:6379> exec  # 执行错误出错
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k5  # 所有命令都没有被执行
(nil)
  • 运行时异常:

    如果事务队列中存在语法性问题(e.g. 1/0),那么执行命令的时候,其他命令是可以正常执行的

127.0.0.1:6379> set k1 "v1"
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr k1  # 会执行失败
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> get k3
QUEUED
127.0.0.1:6379> exec
1) (error) ERR value is not an integer or out of range  # 虽然第一条命令报错了,但是依旧执行成功了
2) OK
3) OK
4) "v3"
127.0.0.1:6379> get k2
"v2"
127.0.0.1:6379> get k3
"v3"

Redis 事务与 MySQL 事务对比

MySQL 中事务的四大特性

MySQL 中事务有四大特性:ACID

  • 原子性(Atomicity)
    原子性是最基本的特性,一个事务内所有操作就是最小的一个操作单元,要么全部成功,要么全部失败

  • 一致性(Consistency)
    一个事务可以封装状态改变(除非是只读的);事务必须始终保持系统处于一致的状态,不管在任何给定的时间,并发事务有多少
    如果一个操作触发辅助操作(级联、触发器),这些辅助操作也必须成功,否则该操作失败
    如果系统是由多个节点组成的,一致性规定所有的变化必须传播到所有节点(多主复制);如果从站节点是异步更新的,那么就打破了一致性规则,系统成为 “最终一致性”
    一个事务是数据状态的切换,当事务是并发多个时,系统也必须如同串行事务一样操作
    大多数数据库管理系统选择(默认情况)是放宽一致性,以达到更好的并发性

  • 隔离性(Isolation)
    事务的隔离性,基于原子性和一致性,事务可以有多个原子包的形式并发执行,但是每个事务之间互不干扰

  • 持久性(Durability)
    当一个事务提交后,数据库的状态就永远地发生了改变;即使事务提交后,数据库发生异常,也不会因为异常导致事务提交失败

不同的事务命令

  • MySQL
    Begin : 显式地开启一个事务
    Commit : 提交事务,将对数据库进行的所有修改变成永久性
    Rollback : 结束用户的事务,并撤销现在正在进行的未提交的修改

  • redis
    Multi : 标记事务的开始
    Exec : 执行事务的 commands 队列
    Discard : 结束事务,并清除 commands 队列

不同的默认状态

  • MySQL
    MySQL 会默认开启事务,且缺省设置是自动提交,即每成功执行 sql,一个事务就会马上 commit,所以不能 rollback

  • redis
    redis 默认不会开启事务,即 command 会立刻执行,而不会排队,并不支持 rollback

不同的使用方式

  • MySQL
    用 Begin、Rollback、Commit 显式开启并控制一个 新的 事务 Transaction
    执行命令 set autocommit=0 ,用来禁止当前会话自动 commit,并控制默认开启事务

  • redis
    用 Multi、Exec、Discard,显式开启控制一个 Transaction

不同的实现原理

  • MySQL
    MySQL 实现事务是基于 undo/redo 日志
    undo 记录修改前状态,rollback 基于 undo 日志实现
    redo 记录修改后状态,commit 基于 redo 日志实现

  • redis
    redis 实现事务是基于 commands 队列
    如果没有开启事务,command 将会被立即执行并返回执行结果,并直接写入磁盘
    如果事务开启,command 不会被立即执行,而是排入队列,并返回排队状态;调用 exec 才会执行 commands 队列

redo 日志是 innodb 专有的,所以 innodb 支持事务
在 MySQL 中,无论是否开启事务,sql 都会被立即执行并返回执行结果,只是事务开启后执行后的状态记录在 redo 日志中,执行 commit 之后,数据才会被写入磁盘

redis 事务的错误

redis 的事务期间,可能会遇到两种命令错误:

一、在调用 exec 命令之前出现错误(command 排队失败)

例如,命令可能存在语法错误(参数数量错误、命令名称错误、、、),或者内存不足(如果服务器使用 maxmemory 指令做了内存限制)

二、在调用 exec 命令之后出现错误

例如,使用错误的值对某个 key 执行操作(如,对 String 值调用 List 操作)
exec 命令执行后发生的错误并不会被特殊对待:即使事务中的某些命令执行失败,其他命令仍会被正常执行

因为 redis 命令在事务执行时可能会失败,但是仍继续执行剩余命令而不是 rollback(事务回滚),所以 redis 自然也不会支持事务回滚

由于不必支持事务回滚,redis 内部简洁且更加高效

悲观锁和乐观锁

悲观锁

  • 很悲观,认为什么时候都会出现问题,无论对数据做什么操作都会加锁

乐观锁

  • 很乐观,认为什么时候都不会出现问题,所以不会对数据上锁
  • 但是更新数据的时候会去判断一下,在此期间是否有人修改过这个数据
  • 获取 version
  • 更新的时候比较 version

正常执行

# 正常执行成功
127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 20
QUEUED
127.0.0.1:6379> incrby out 20
QUEUED
127.0.0.1:6379> exec
1) (integer) 80
2) (integer) 20

测试多线程修改值,使用 watch 可以当做 redis 的乐观锁操作

127.0.0.1:6379> watch money # 监视 money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 10
QUEUED
127.0.0.1:6379> incrby out 10
QUEUED
127.0.0.1:6379> exec # 执行前,另一个线程,修改了我们的值,这个时候,就会导致事务执行失败
(nil)
# 如果事务执行失败,就先解锁
127.0.0.1:6379> unwatch  # 解锁之前的监视
OK
127.0.0.1:6379> watch money  # 获取最新的值,再次监视
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby money 10
QUEUED
127.0.0.1:6379> incrby out 10
QUEUED
127.0.0.1:6379> exec  # 比对监视的值是否发生变化,如果没有发生变化 ,那么可以执行成功,否则执行失败
1) (integer) 990
2) (integer) 30

Jedis

Jedis 是 Redis 官方推荐的 Java 链接开发工具!使用 Java 操作 Redis 中间件!如果要使用 Java 操作 Redis,那么一定要对 Jedis 十分熟悉

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值