本篇文章给大家带来的内容是关于mysql和redis事务的比较(图文),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。
简言:一般来说,事务是必须满足4个条件(ACID)::原子性(Atomicity,或称不可分割性)、一致性(Consistency)、隔离性(Isolation,又称独立性)、持久性(Durability)。
从标题来看,既然都是事务,那之间有什么区别?来一一解开,先从两个数据库说去。
MySQL 属于
关系型数据库, Redis 属于
非关系型数据库,两者对事务有着不同的解释。
(相关推荐:MySQL教程,Redis教程)
Redis
[1] Redis 事务可以一次执行多个命令, 并且带有以下两个重要的保证:
批量操作在发送 EXEC 命令前被放入队列缓存。 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。一个事务从开始到执行会经历以下三个阶段:
开始事务。 命令入队。 执行事务。单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的。
事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。
操作错误
看着有点儿绕口,那就实际执行一下 看一下结果。
127.0.0.1:6379> multi OK 127.0.0.1:6379> set tr_1 233 QUEUED 127.0.0.1:6379> lpush tr_1 666 QUEUED 127.0.0.1:6379> set tr_2 888 QUEUED 127.0.0.1:6379> exec 1) OK 2) (error) WRONGTYPE Operation against a key holding the wrong kind of value 3) OK
在上面的事务中,设置了一个 key 为
tr_1的字符串数据,然后又通过
lpush来添加元素,这很明显是错误的操作方式,当我们提交事务候出现了一个
操作错误,这时候我们来看看
tr_1的值是什么。
127.0.0.1:6379> get tr_1 "233"
通过
get命令来的
tr_1内容还是 233 ,并没有变,那再看一下其他的。
127.0.0.1:6379> keys * 1) "tr_2" 2) "tr_1" 127.0.0.1:6379> get tr_2 "888" 127.0.0.1:6379>
这里可以看到
tr_2存在,并打印了值,这时候我们发现,即使出现了
操作错误,但是错误并没有致使执行停止,错误之后的语句也执行了并成功执行,似乎符合上面提到的 中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。
语法错误
NO~,这时候还有另外一种情况
语法错误
127.0.0.1:6379> multi OK 127.0.0.1:6379> set tr_1 233 QUEUED 127.0.0.1:6379> lpush tr_1 666 QUEUED 127.0.0.1:6379> set (error) ERR wrong number of arguments for 'set' command 127.0.0.1:6379> set 233 (error) ERR wrong number of arguments for 'set' command 127.0.0.1:6379> set tr_2 888 QUEUED 127.0.0.1:6379> exec (error) EXECABORT Transaction discarded because of previous errors. 127.0.0.1:6379> keys * (empty list or set)
当我们执行到
set时没有给任何参数,第二次执行时故意少给了一个参数。可以看到报了
语法错误,最后提交事务,也告诉了我们事务因为错误被丢失了,接着用
keys *检索发现确实如此。
文档释义
这里可以官方文档中提到的
关于 Redis 暂时看到这里 接下来看到 MySQL
MySQL
众所周知,MySQL 只有
InnoDB 引擎支持 事务,在启用 MySQL 事务之前需要先
停掉自动提交
测试表结构 user
| 列 | 类型 | 注释 |
|---|---|---|
| id | int(11) 自动增量 | 主键ID |
| money | int(11) [0] | 金钱 |
| title | varchar(500) NULL | 称呼 |
在这里来模拟一个转账的操作:
A给
B转
100元。
步骤解析
A+100 元,
B-100元,即两步虽然很简单,简单走一下流程。

可以看到,没有问题,那么我们从中人为的制造一些问题呢?
操作错误
| 列 | 类型 | 注释 |
| id | int(11) 自动增量 | |
| money | int(11) unsigned [0] |
|
| title | varchar(500) NULL |
这里我们把
money字段变成了无符号,即不能小于 0,并且,调整数据库中的数据如下。
`SELECT * FROM `user` LIMIT 50` (0.000 秒)
| 修改 | id | money | title |
|---|---|---|---|
| 编辑 | 1 | 10000 | A |
| 编辑 | 2 | 0 | B |
接着执行下面的 SQL
select version();
SET AUTOCOMMIT=0;
begin;
select * from user where title in ('A','B') for update;
update user set money = money + 1000 where title = 'A';
update user set money = money - 1000 where title = 'B';
select * from user where title in ('A','B');
commit;
问题出现了,这里报出了错误,但是可以看到 前面的 SQL 已经是已执行的了,结果已经发生了变化,从这里看,似乎和 Redis 的处理差不多,除了错误之后语句继续执行。但是 值的注意的是, 在我们实际开发中,这种情况程序会直接抛出异常,以供我们在 catch 块中执行 rollback ,以回滚操作确保数据完整,即使是单独使用 MySQL 命令行 我们也可以用存储过程来对异常进行回滚。
语法错误
刚刚看到 Redis 当遇到
语法错误时会自动丢弃事务,阻止提交,那 MySQL 呢?

答案:
不会,MySQL 在顺序执行时,如果未对异常进行处理,总会将成功执行的的提交,而不会触发自动终止,但是我们可以在程序执行时进行放弃提交。
Redis 为什么没有回滚?
Redis 的官方文档给出了这样的解释
总结
| 数据库 自动回滚条件 | 操作错误 | 语法错误 |
|---|---|---|
| MySQL | ✖ | ✖ |
| Redis | ✖ | ✔ |
但是 MySQL 支持手动回滚,实际开发过程中可以自行手动对已提交的操作进行回滚操作,更加友好。
