数据库的事务有原子性,一致性,隔离性,持久性。
事务的隔离性由隔离级别体现,事务有四种隔离级别。
1,READ UNCOMMITTED(未提交读)
事务可以读取其他事务未提交的修改。
2,READ COMMITTED(已提交读)
事务读取的信息是其他事务已提交的。
3:REPEATABLE READ(可重复读)
与不可重复读相对,事务中不存在读取同一份数据不一样的情况。READ UNCOMMITTED和READ COMMITTED级别下存在不可重复读的情况,当事务还未提交,其他事务修改数据并且提交,此时该事务再查询同一份数据时,数据发生改变。mysql的默认事务隔离级别。
4:SERIALIZABLE(串行化)
事务的读取,修改,添加都是通过加锁保证事务的执行次序。
MariaDB> SELECT @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| SERIALIZABLE |
+----------------
1:设置隔离级别为READ UNCOMMITTED
打开会话窗口一,设置事务隔离界别为“未提交读”。
MariaDB> set session transaction isolation level READ UNCOMMITTED;
再打开一个窗口二
## 开启事务
begin;
## 查询信息
MariaDB> select account from zz_users limit 1;
+---------+
| account |
+---------+
| |
+---------+
1 rows in set (0.02 sec)
## 修改信息
MariaDB> update zz_users set account="lantian" where id=1;
Query OK, 1 rows affected (0.02 sec)
此时修改了数据,事务还未提交
回到原来窗口:
## 开启事务
begin;
## 查询另一个会话事务修改的数据
MariaDB> select account from zz_users where id=1;
+---------+
| account |
+---------+
| lantian |
+---------+
1 rows in set (0.02 sec)
此时查询出了其他事务没有提交的修改,此时二号窗口回滚。
## 重新执行查询
MariaDB> select account from zz_users where id=1;
+---------+
| account |
+---------+
| |
+---------+
1 rows in set (0.01 sec)
会话一读取了未生效的数据,也就是垃圾数据,对于会话一这就是脏读。
2:设置隔离级别为READ COMMITTED
MariaDB> set session transaction isolation level READ COMMITTED;
会话二重复上面的操作,会话一分别在会话二提交前和提交后查询数据:
## 会话二未提交
MariaDB> select account from zz_users where id=1;
+---------+
| account |
+---------+
| |
+---------+
1 rows in set (0.01 sec)
## 会话二已提交
MariaDB> select account from zz_users where id=1;
+---------+
| account |
+---------+
| lantian |
+---------+
1 rows in set (0.01 sec)
会话一读取的信息是其他事务提交的数据,此时脏读不存在。不可重复读会存在,
删除id为1 的数据,两个会话重新开启事务。
## 会话一查询id为2的数据
MariaDB> select account,id from zz_users where id=2;
+----------+----+
| account | id |
+----------+----+
| 88888888 | 2 |
+----------+----+
1 rows in set (0.01 sec)
## 会话二插入一条数据
MariaDB> insert into zz_users (uuid, account, id) values ("uuid74298d2a3938ccb2d28067fe5817db77","666666", 1);
Query OK, 1 rows affected (0.01 sec)
MariaDB> select account,id from zz_users where id=1;
+---------+----+
| account | id |
+---------+----+
| 666666 | 1 |
+---------+----+
1 rows in set (0.01 sec)
MariaDB> commit;
Query OK, 0 rows affected (0.02 sec)
## 会话一重新查询数据发生变化
MariaDB> select account,id from zz_users where id=2;
+----------+----+
| account | id |
+----------+----+
| 77777777 | 2 |
+----------+----+
1 rows in set (0.01 sec)
同一份数据在同一个事务两次读取不一致,就是不可重复读。
3:事务级别设置为REPEATABLE READ:
## 会话一查询数据
MariaDB> select account,id from zz_users where id=2;
+----------+----+
| account | id |
+----------+----+
| 77777777 | 2 |
+----------+----+
1 rows in set (0.01 sec)
## 会话二更新数据操作
MariaDB> update zz_users set account="88888888" where id=2;
Query OK, 1 rows affected (0.01 sec)
MariaDB> commit;
Query OK, 0 rows affected (0.01 sec)
## 会话一再次查询数据
MariaDB> select account,id from zz_users where id=2;
+----------+----+
| account | id |
+----------+----+
| 77777777 | 2 |
+----------+----+
1 rows in set (0.01 sec)
## 提交事务
MariaDB> commit;
Query OK, 0 rows affected (0.01 sec)
## 再次查询
MariaDB> select account,id from zz_users where id=2;
+----------+----+
| account | id |
+----------+----+
| 88888888 | 2 |
+----------+----+
1 rows in set (0.02 sec)
此时事务为结束之前,多次读取数据都是一致的。不可重复读不存在,但是幻读会存在。
两个会话重新开启事务:
## 会话一查询id为1,2的数据。此时只存在id为2 的数据
MariaDB> select account,id from zz_users where id in (1,2);
+----------+----+
| account | id |
+----------+----+
| 88888888 | 2 |
+----------+----+
1 rows in set (0.02 sec)
## 会话2插入一条数据id为1
MariaDB> insert into zz_users (uuid, account, id) values ("uuid74298d2a3938ccb2d28067fe5817db79","666666", 1);
Query OK, 1 rows affected (0.01 sec)
## 查询
MariaDB> select account,id from zz_users where id in (1,2);
+----------+----+
| account | id |
+----------+----+
| 666666 | 1 |
| 88888888 | 2 |
+----------+----+
2 rows in set (0.02 sec)
## 提交数据
MariaDB> commit;
Query OK, 0 rows affected (0.01 sec)
### 会话一再次查询id为1,2的数据
MariaDB> select account,id from zz_users where id in (1,2);
+----------+----+
| account | id |
+----------+----+
| 88888888 | 2 |
+----------+----+
1 rows in set (0.01 sec)
### 没有id为1的数据,但是此时插入数据时
MariaDB> insert into zz_users (uuid, account, id) values ("uuid74298d2a3938ccb2d28067fe5817db79","666666", 1);
Duplicate entry '1' for key 'PRIMARY'
此时没有出现幻读,读取数据还是以前查找的数据,只是插入的时候回报重复id的错误,网上其他资料RR级别是会出现幻读的,但是实验没有,可能MariaDB最新版本解决了RR级别幻读的问题。
4:事务级别设置为REPEATABLE SERIALIZABLE:
重复上面的操作,先在会话一查询:
### 会话一查询数据
MariaDB> select account,id from zz_users where id in (1,2);
+----------+----+
| account | id |
+----------+----+
| 88888888 | 2 |
+----------+----+
1 rows in set (0.03 sec)
### 此时会话二插入数据,此时会话二进入等待状态,等到会话1提交后才会继续执行。
MariaDB> insert into zz_users (uuid, account, id)
values ("uuid74298d2a3938ccb2d28067fe5817db79","666666", 1);
总结:事务的隔离性是数据库mvcc和锁的共同作用下实现。SERIALIZABLE级别下开销最大,会对所有查询加共享锁,即使数据不存在。达到命令一条一条执行。