上一篇我们聊到了mariadb的锁,以及怎么手动加锁和解锁等等,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/13196905.html;今天我们来聊一聊mariadb的事务隔离级别;在前边的随笔中,我们提到到了mysql的存储引擎,常用的有MyISAM和innodb,其中myisam不支持事务,innodb支持事务;所以我们常说的事务是针对innodb存储引擎来说的;所谓事务就是在我们在执行大批量语句时,为了保证数据库的完整性,要么语句全部执行,要么语句全部不执行;所以事务必须满足ACID这是个条件;A表示Atomicity,原子性,也是不可分性;C表示Consistency,一致性;I表示Isolation,隔离性,又称独立性;D表示Durability,持久性;所谓原子性,就是事务中执行的语句要么全部执行,要么全部不执行,如果事务在中途发生错误,那么前面执行过的语句将会回滚到事务前;一致性指的是在执行事务之前和事务执行完成后的数据库状态是完整的;也就是说我们执行的语句都按照我们预想的结果执行了;隔离性指数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。持久性指的是,事务执行完成后,对数据的修改是永久的;
接下来我们来说说怎么在mariadb中开启事务吧
用户手动开启事务用start transaction 或者使用begin语句开启事务;
提示:以上红框中的语句就表示手动开启一个事务;这里需要注意一点,对于mysql来讲,默认在命令行执行的语句都是自动提交事务的;也就是说默认情况下我们在mysql shell中执行的语句它默认也会开启一个事务,但我们语句执行完成后,它会自动把该事务提交;所以我们手动开启一个事务可以使用start transaction 语句 或者begin 或者执行set autocommit=0来关闭自动提交事务;
提示:提交事务用commit 语句,表示结束当前事务;当然结束当前事务也可以用rollback语句,表示回滚到事务之前的状态;回滚以后事务也就结束了;
SAVEPOINT identifier:创建一个保存点,一个事务中可以存在多个保存点,回滚时我们可以指定回滚到某个点上;
MariaDB [first_db]> begin; Query OK, 0 rows affected (0.000 sec) MariaDB [first_db]> select * from test_tb; +------+----------+------+ | id | name | age | +------+----------+------+ | 1 | xiaoming | 44 | | 2 | zhangsan | 16 | +------+----------+------+ 2 rows in set (0.001 sec) MariaDB [first_db]> insert into test_tb values (3,"lisi",23); Query OK, 1 row affected (0.002 sec) MariaDB [first_db]> savepoint one; Query OK, 0 rows affected (0.001 sec) MariaDB [first_db]> insert into test_tb values (4,"wangwu",25); Query OK, 1 row affected (0.001 sec) MariaDB [first_db]> savepoint two; Query OK, 0 rows affected (0.000 sec) MariaDB [first_db]> select * from test_tb; +------+----------+------+ | id | name | age | +------+----------+------+ | 1 | xiaoming | 44 | | 2 | zhangsan | 16 | | 3 | lisi | 23 | | 4 | wangwu | 25 | +------+----------+------+ 4 rows in set (0.001 sec) MariaDB [first_db]> rollback to one; Query OK, 0 rows affected (0.001 sec) MariaDB [first_db]> select * from test_tb; +------+----------+------+ | id | name | age | +------+----------+------+ | 1 | xiaoming | 44 | | 2 | zhangsan | 16 | | 3 | lisi | 23 | +------+----------+------+ 3 rows in set (0.001 sec) MariaDB [first_db]>
提示:存在多个保存点,如果回滚到前边的点以后,后面的保存点就没有了;
接下来看看事务的隔离级别
对于mysql数据库 innodb存储引擎支持的事务隔离级别有4中,READ-UNCOMMITED:读未提交;这表示事务还没有结束,其他用户或进程是可以实时的读到事务中对数据的改变,因为事务还没提交,所以我们认为其他用户或进程读到的数据就是一个不准确的数据,通常我们把这种叫脏读;READ-COMMITTED:读提交;这表示只有事务提交后,其他用户或进程才可以读到事务修改后的数据;这种事务隔离级别就要比前面的隔离级别要高一点,读到的数据也要精准一点;这种隔离级别就是只要事务A提交了,在其他事务里就可以读取到事务A修改后的数据;通常这种我们叫不可重复读,不可重复读表示在其他事务中,读到的数据是根据事务A是否提交有关系;REPEATABLE-READ:可重复读,这表示在A事务中修改后的数据,在B中事务上不可见的,即便A中事务已经提交,B中事务没有提交,那么在B中的事务中读到的还是A事务修改前的数据;只有当事务B结束后才能读到A事务修改后的数据;所以通常我们把这种方式也叫幻读,给人一种幻觉的感觉;SERIALIZABLE:串行化;这种隔离级别是最高的,但同时并发访问也是最差的,它表示事务A和事务B是顺序执行的,什么意思呢,就是说事务B必须要等到事务A完成后才可以执行;
关闭自动提交功能
MariaDB [first_db]> select @@session.autocommit; +----------------------+ | @@session.autocommit | +----------------------+ | 1 | +----------------------+ 1 row in set (0.001 sec) MariaDB [first_db]> set @@session.autocommit=0; Query OK, 0 rows affected (0.001 sec) MariaDB [first_db]> select @@session.autocommit; +----------------------+ | @@session.autocommit | +----------------------+ | 0 | +----------------------+ 1 row in set (0.001 sec) MariaDB [first_db]>
提示:以上语句表示设定当前会话关闭自动提交功能,关闭此会话它将恢复原有设定;
设置事务A的隔离级别为read-uncommitted
提示:事务隔离级别字串要加引号,否则会提示语法错误
设置会话B的隔离级别为read-uncommitted
在事务A中修改表中数据,在会话B中查看表中数据,看看是否在会话B中看到修改后的数据?
MariaDB [first_db]> BEGIN; Query OK, 0 rows affected (0.000 sec) MariaDB [first_db]> select @@session.tx_isolation; +------------------------+ | @@session.tx_isolation | +------------------------+ | REPEATABLE-READ | +------------------------+ 1 row in set (0.001 sec) MariaDB [first_db]> set @@session.tx_isolation='read-uncommitted'; Query OK, 0 rows affected (0.001 sec) MariaDB [first_db]> select @@session.tx_isolation; +------------------------+ | @@session.tx_isolation | +------------------------+ | READ-UNCOMMITTED | +------------------------+ 1 row in set (0.001 sec) MariaDB [first_db]> select * from test_tb; +------+----------+------+ | id | name | age | +------+----------+------+ | 1 | xiaoming | 44 | | 2 | zhangsan | 16 | +------+----------+------+ 2 rows in set (0.001 sec) MariaDB [first_db]> insert into test_tb values(2,"xiaohong",33); Query OK, 1 row affected (0.001 sec) MariaDB [first_db]> select * from test_tb; +------+----------+------+ | id | name | age | +------+----------+------+ | 1 | xiaoming | 44 | | 2 | zhangsan | 16 | | 2 | xiaohong | 33 | +------+----------+------+ 3 rows in set (0.001 sec) MariaDB [first_db]>
提示:在事务A中插入数据在当前事务中是可以正常看到数据的变化;
提示:可以看到在会话B上是可以正常看到事务A中数据的修改;
提示:从上面的信息可以看到,当事务A中的修改操作回滚时,在会话B中是可以查看到回滚后的数据;这也意味这在会话B中读到的数据不是很准确;
设置会话B的事务隔离级别为read-committed,然后在事务A上修改数据,不提交,看看会话B上是否可以看到?
MariaDB [first_db]> start transaction; Query OK, 0 rows affected (0.001 sec) MariaDB [first_db]> select @@session.tx_isolation; +------------------------+ | @@session.tx_isolation | +------------------------+ | READ-UNCOMMITTED | +------------------------+ 1 row in set (0.001 sec) MariaDB [first_db]> set @@session.tx_isolation='read-committed'; Query OK, 0 rows affected (0.001 sec) MariaDB [first_db]> select @@session.tx_isolation; +------------------------+ | @@session.tx_isolation | +------------------------+ | READ-COMMITTED | +------------------------+ 1 row in set (0.001 sec) MariaDB [first_db]> select @@session.autocommit; +----------------------+ | @@session.autocommit | +----------------------+ | 0 | +----------------------+ 1 row in set (0.001 sec) MariaDB [first_db]> select * from test_tb; +------+----------+------+ | id | name | age | +------+----------+------+ | 1 | xiaoming | 44 | | 2 | zhangsan | 16 | +------+----------+------+ 2 rows in set (0.001 sec) MariaDB [first_db]> insert into test_tb values(3,"wangwu",43); Query OK, 1 row affected (0.001 sec) MariaDB [first_db]> select * from test_tb; +------+----------+------+ | id | name | age | +------+----------+------+ | 1 | xiaoming | 44 | | 2 | zhangsan | 16 | | 3 | wangwu | 43 | +------+----------+------+ 3 rows in set (0.001 sec) MariaDB [first_db]>
在会话B中查看test_tb表,看看是否有能看到修改后的数据?
提示:在会话B中可以看到的test_tb表中的数据还是事务A开始前的数据;
提交事务A,看看会话B是否能够看到修改后的数据呢?
提示:可以看到当事务A提交以后,在会话B中就可以看到事务A中修改后的数据;
设置事务A的事务隔离级别为repeatable-read
MariaDB [first_db]> begin; Query OK, 0 rows affected (0.000 sec) MariaDB [first_db]> set @@session.tx_isolation='repeatable-read'; Query OK, 0 rows affected (0.000 sec) MariaDB [first_db]> select @@session.tx_isolation; +------------------------+ | @@session.tx_isolation | +------------------------+ | REPEATABLE-READ | +------------------------+ 1 row in set (0.000 sec) MariaDB [first_db]>
关闭事务B的自动提交功能并设置事务B的隔离级别为repeatable-read
在事务A中修改数据,并提交,看看在事务B中是否能看到修改后的数据?
在事务B中查看test_tb表中的数据,看看是否有变化呢?
提示:可以看到在事务B中无论怎么查看数据都是没有发生变化;
提交事务B在查看数据看看是否有变化?
提示:可以看到当事务B提交以后,再次查看表中数据,就可以看到事务A更改以后的表数据了;
设置会话A的事务隔离级别为serializable
设置会话B的事务隔离级别为serializable
在事务A,事务B中更新表中同一条数据,看看会发生什么?
提示:在事务B中成功修改了第一条数据;
提示:在事务A上就不能修改第一条数据了,那我们在事务A上是否可修改第二条数据呢?
提示:可以看到第二条数据是能够修改的;
提交事务B,看看A事务上是否可以修改第一条数据呢?
提示:提交了事务B以后,在会话B上再次查看test_tb表中的数据,发现执行不了,原因是事务A修改了第二条数据,还未完成事务,所以一直阻塞;
在事务A上修改第一条数据,看看是否可修改?
提示:提交了事务B以后,在事务A上还是不能够修改第一条数据;原因是事务A修改了第二条数据,事务还没有结束,所以第三个事务就无法执行,所以我们在会话B上是不能够查看数据,因为默认情况查看数据也会启动一个事务;
结束事务A,在会话B上看看是否可查询?
提示:回滚了事务A,事务A也就结束了;在事务B中修改的数据,在事务A上做回滚,是回滚不回去的;这说明回滚操作只针对当前事务;
提示:结束了事务A以后,在会话B上就可以正常查看test_tb上的数据了;
以上就是在mariadb数据库上的事务的四种隔离级别的区别;从上面的演示可以看到,串行化的隔离级别最高,但是并发连接也是最差的,因为它必须要等到前一个事务结束后才可以执行后面的事务;其次就是可重读,可重读必须是两个事务都结束以后才可以看到真实修改后的数据;再其次就是不可重读,读提交,这种隔离级别必须是一方事务提交以后其他事务才可以读到真实修改数据;而隔离级别最低就是对未提交,这种隔离级别只要是事务中修改了,其他事务上就能够读到相应的数据;当然这种读到的数据也是最不靠谱的;