隔离级别
数据库事务有不同的隔离级别,不同的隔离级别对锁的使用是不同的,锁的应用最终导致不同事务的隔离级别。
实现隔离级别的方式就是加锁
隔离级别的分类
1.读未提交 Read Uncommitted(在本次事务中可以读到其他事务中没有提交的数据 - 脏数据)
2.读已提交 Read Committed (只能读到其他事务提交过的数据。如果在当前事务中,其他事务有提交,则两次读取结果不同)
3.可重复读 Repeatable Read (默认,保证了事务中每次读取结果都相同,而不管其他事物是否已经提交。会出现幻读)
4.序列化 Serializable (隔离级别中最严格的,开启一个 serializable 事务,那么其他事务对数据表的写操作都会被挂起)
实验:
1.创建一张t1表
CREATE TABLE t1(
id INT NOT NULL AUTO_INCREMENT,
ACCOUNT FLOAT NOT NULL,
PRIMARY KEY(id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
2.设置事务隔离级别
设置 innodb 的事务级别方法是:set 作用域 transaction isolation level 事务隔离级别
mysql> insert into t1 values (1,1);
Query OK, 1 row affected (0.01 sec)
设置session变量
mysql> set session transaction isolation level read committed;
实验过程
1.read uncommitted(读取未提交数据)#
A 用户
mysql> insert into t1 values (1,1);
Query OK, 1 row affected (0.01 sec)
mysql> set session transaction isolation level read uncommitted;
Query OK, 0 rows affected (0.00 sec)
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from t1;
+----+---------+
| id | ACCOUNT |
+----+---------+
| 1 | 1 |
+----+---------+
1 row in set (0.00 sec)
B 用户
mysql> set session transaction isolation level read uncommitted;
Query OK, 0 rows affected (0.00 sec)
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> update t1 set account=account+200 where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
A用户查看
mysql> select * from t1;
+----+---------+
| id | ACCOUNT |
+----+---------+
| 1 | 201 |
+----+---------+
1 row in set (0.00 sec)
B用户查看
mysql> select * from t1;
+----+---------+
| id | ACCOUNT |
+----+---------+
| 1 | 201 |
+----+---------+
1 row in set (0.00 sec)
结论一:我们将事务的隔离级别设置为读未提交,所以 A 用户能够读到 B 用户没有提交的数据
存在的问题:那么这么做有什么问题吗?
那就是我们在一个事务中可以随随便便读取到其他事务未提交的数据,这还是比较麻烦的,我们叫脏读。实际上我们的数据改变了吗?答案是否定的,因为只有事务 commit 后才会更新到数据库。
2.read committed(可以读取其他事务提交的数据)—- 大多数数据库默认的隔离级别
将 B 用户的隔离级别设置为 read committed
set session transaction isolation level read committed;
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> update t1 set account = account-200 where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from t1;
+----+---------+
| id | ACCOUNT |
+----+---------+
| 1 | -599 |
+----+---------+
1 row in set (0.00 sec)
A用户
mysql> select * from t1;
+----+---------+
| id | ACCOUNT |
+----+---------+
| 1 | -599 |
+----+---------+
1 row in set (0.00 sec)
B用户
mysql> select * from t1;
+----+---------+
| id | ACCOUNT |
+----+---------+
| 1 | -399 |
+----+---------+
1 row in set (0.00 sec)
结论二:我们将当前会话的隔离级别设置为 read committed 的时候,当前会话只能读取到其他事务提交的数据,未提交的数据读不到。
存在的问题:那就是我们在会话 B 同一个事务中,读取到两次不同的结果。这就造成了不可重复读,就是两次读取的结果不同。这种现象叫不可重复读。
3.repeatable read(可重读)—-MySQL 默认的隔离级别
设置 B 中的隔离级别为 repeatable read
set session transaction isolation level repeatable read;
start transaction;
select * from t1;
在 A 中添加一条数据
发现插入不进去
结论三:当我们将当前会话的隔离级别设置为 repeatable read 的时候,当前会话可以重复读,就是每次读取的结果集都相同,而不管其他事务有没有提交。
出现的问题:
一个事务中读取的数据一致(可重复读),数据已经发生改变,但是我还是要保持一致。但是,出现了用户 B 面对的问题,这种现象叫幻读。
4.serializable(串行化)
在 B 中先开启 serializable 事务
set session transaction isolation level serializable;
start transaction;
select * from t1;
然后在 A 中写数据,会出现超时,如果这时 B commmit, 那么 A 中会执行成功
结论四:当我们将当前会话的隔离级别设置为 serializable 的时候,其他会话对该表的写操作将被挂起。可以看到,这是隔离级别中最严格的,但是这样做势必对性能造成影响。所以在实际的选用上,我们要根据当前具体的情况选用合适的。
总结:
读未提交:别人修改数据的事务尚未提交,在我的事务中也能读到.
读已提交:别人修改数据的事务已经提交,在我的事务中才能读到.
可重复读:别人修改数据的事务已经提交,在我的事务中也读不到.
串行:我的事务尚未提交,别人就别想改数据.
这四种隔离级别,从上到下,并行能力依次降低,安全性一次提高
版权声明:本文为博主原创文章,未经博主允许不得转载。
MYSQL
- 上一篇:Mysql四种引擎方式
- 下一篇:OceanBase 数据库