我们都知道,如果要实现事务,需要整体保证 ACID(A-原子性|C-持久性|I-隔离性|D-一致性) ,其中一致性是目标,原子性、持久性和隔离性都是手段,所以这里对比一下 MySQL 和 Redis 在事务实现上的区别,当然严格意义上来说,Redis 由于不满足原子性,不能算真正意义上实现了事务。
原子性
MySQL - 原子性
MySQL 的原子性是通过 undolog 保证的,undolog 是 MySQL 的回滚日志,保存的是数据的历史版本,通过历史版本让数据在任何时候都可以回滚到某一个事务开始之前的状态,所以如果事务执行失败了,可以通过 undolog 来保证到原子性。
Redis - 原子性
Redis 没有实现原子性,Redis 的官方文档是这么解释的,所以严格意义上来说,Redis 没有实现事务,因为不能回滚。
It’s important to note that even when a command fails, all the other commands in the queue are processed – Redis will not stop the processing of commands.
持久性
MySQL - 持久性
MySQL 的持久性是通过 redolog 保证的,redolog 是 MySQL 的前滚日志,记录数据修改的操作日志,通过操作日志可以保证数据库的数据不丢失。
Redis - 持久性
Redis 严格意义上来说,也没有保证到持久性,这是因为它的持久化策略中不管是 RDB 还是 AOF 都是异步执行的,这样还是有可能会造成数据丢失。
不过 MySQL 除非使用双1
的配置,也就是 sync_binlog 和 innodb_flush_log_at_trx_commit 的配置都是 1,否则也是不满足持久性的;因为如果主机发生异常重启/掉电,还是有可能会造成数据丢失。所以这里可以认为 Redis 部分实现了持久性。
隔离性
MySQL - 隔离性
MySQL 里面定义了四种标准的隔离级别,通过 MVCC(Multi-Version Concurrency Control,多版本并发控制) 来实现个不同隔离级别的隔离,简单来说,通过数据的版本来控制数据的可见性。
读未提交 - Read Uncommitted
可能会读到其他事务未提交的数据,也就是脏读
。
读已提交 - Read Committed
读取的数据可能会前后不一致的情况,因为读取的数据可能在事务执行的过程中被修改了。
可重复读 - Repeatable Read
事务一旦开始,事务过程中读取的数据就不能被修改。
但是依然可能会发生幻读
,因为可重复读虽然保护了读取的数据,但是其他数据插入后依然可能满足当前的查询条件,造成幻读
。
序列化 - Serializable
系统中的所有事务都串行执行。虽然可以避免所有数据不一致的情况,但是性能下降明显,一般不建议使用。
Redis - 隔离性
Redis 的事务在执行的过程中,就不会处理其它命令,而是等所有命令都执行完后,再处理其它命令。所以只要执行了 multi,就会阻塞其他操作。因此 Redis 事务是满足隔离性的。
总结
最后我们可以用一个表格来总结 Redis 和 MySQL 在事务实现上的区别。
MySQL | Redis | |
A - 原子性 | undo log | ❌ |
C - 持久性 | redo log | RDB + AOF |
I - 隔离性 | MVVC | 命令单进程+单线程执行 |