隔离性是当多个用户并发访问数据库时,好比同时操做同一张表时,数据库为每个用户开启的事务,不能被其余事务的操做所干扰,多个并发事务之间要相互隔离css
读未提交
:隔离级别最低的一种事务级别。在这种隔离级别下,会引起脏读、不可重复读和幻读。mysql
读已提交
读到的都是别人提交后的值。这种隔离级别下,会引起不可重复读和幻读,但避免了脏读。sql
可重复读
这种隔离级别下,会引起幻读,但避免了脏读、不可重复读。数据库
串行化
是最严格的隔离级别。在Serializable隔离级别下,全部事务按照次序依次执行。脏读、不可重复读、幻读都不会出现。session
SHOW VARIABLES LIKE 'tx_isolation';
并发
查看全局的事务隔离级别app
SHOW GLOBAL VARIABLES LIKE 'tx_isolation';
性能
使用系统变量查询spa
SELECT @@global.tx_isolation;
SELECT @@session.tx_isolation;
SELECT @@tx_isolation;
日志
MySql经常使用命令
查询隔离级别select @@tx_isolation;
设置手动提交set autocommit=0 ;
查看当前事务自动提交模式select @@autocommit;
设置隔离级别set tx_isolation = 'READ-COMMITTED';
查询表的状态show table status like 'test1';
修改表的存储引擎alter table test1 engine = INNODB
查看是否开启日志show variables like 'log_bin';
查看日志状态show master status;
SET [GLOBAL | SESSION] TRANSACTION ISOLATION LEVEL
{
REPEATABLE READ
| READ COMMITTED
| READ UNCOMMITTED
| SERIALIZABLE
}
GLOBAL
:设置全局的事务隔离级别SESSION
:设置当前session的事务隔离级别,若是语句没有指定GLOBAL或SESSION,默认值为SESSION
SET GLOBAL tx_isolation='REPEATABLE-READ';
SET SESSION tx_isolation='SERIALIZABLE';
在多个事务并发作数据库操做的时候,若是没有有效的避免机制,就会出现种种问题。大致上有如下问题:
脏读
指一个事务读取了另一个事务未提交的数据。
具体看后文案例介绍
不可重复读
指在一个事务内读取表中的某一行数据,屡次读取结果不一样。
不可重复读和脏读的区别是,脏读是读取前一事务未提交的脏数据,不可重复读是从新读取了前一事务已提交的数据。
具体看后文案例介绍
幻读(虚读)
指在一个事务内读取到了别的事务插入的数据,致使先后读取不一致。
如下都是采用mysql数据库
下面实际操做中使用到的一些并发控制语句,可看上面的操做介绍
做为演示:product表(产品表)
产品ID 产品名称 产品价格 产品数量 .
productId | productName | productPrice | productCount |
---|---|---|---|
1 | xiaomi | 1999 | 100 |
带着上面的问题咱们来看一下,事务在没有隔离性的状况下,会引起哪些问题?
同时打开两个窗口模拟2个用户并发访问数据库
查询事务隔离级别
SELECT @@tx_isolation;
设置隔离级别为未提交读:
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
注意:须要同时修改两个窗口的事务隔离级别
如下咱们以两位用户抢小米手机为例
时间轴 | 事务A | 事务B |
---|---|---|
T1 | start transaction; | |
T2 | select p.productName,p.productCount from product p where p.productId=1;(productCount =100) | |
T3 | start transaction; | |
T4 | select p.productName,p.productCount from product p where p.productId=1;(productCount =100) | |
T5 | update product set productCount = 99 where productId = 1; | |
T6 | select p.productName,p.productCount from product p where p.productId=1;(productCount =99) | |
T7 | ROLLBACK; | |
T8 | select p.productName,p.productCount from product p where p.productId=1;(productCount =100) |
T1—— A用户开启事务,start transaction;
T2—— A用户查询当前小米手机剩余数量,select p.productName,p.productCount from product p where p.productId=1;此时数量显示为100。
T3——B用户开启事务,start transaction;
T4——B用户查询当前小米手机剩余数量,select p.productName,p.productCount from product p where p.productId=1;此时数量显示为100。
T5—— B用户购买了一台小米手机,update product set productCount = 99 where productId = 1; 此时只修改数据并未提交事务。
T6—— A用户刷新页面,select p.productName,p.productCount from product p where p.productId=1;此时数量显示为99。
T7—— B用户购买失败,回滚事务。
T8—— A用户查询当前小米手机剩余数量,select p.productName,p.productCount from product p where p.productId=1;此时数量显示为100。
事务A读取了未提交的数据,事务B的回滚,致使了事务A的数据不一致,致使了事务A的脏读
!
查询事务隔离级别
SELECT @@tx_isolation;
更改数据库隔离级别,设置隔离级别为提交读:
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
注意:须要同时修改两个窗口的事务隔离级别
时间轴 事务A 事务B T1 start transaction; T2 select p.productName,p.productCount from product p where p.productId=1;(productCount =100) T3 start transaction; T4 select p.productName,p.productCount from product p where p.productId=1;(productCount =100) T5 update product set productCount = 99 where productId = 1; T7 select p.productName,p.productCount from product p where p.productId=1;(productCount =100) T6 commit; T8 select p.productName,p.productCount from product p where p.productId=1;(productCount =99)
这里就再也不对流程作过多赘述。
小结:
能够看到避免了
脏读
现象,可是却出现了,一个事务尚未结束,就发生了不可重复读问题,即事务A来讲 productCount从 100->100->99。但这个过程当中事务并未提交结束。
3.3事务隔离级别设置为Repeatable Read(mysql默认级别)
查询事务隔离级别
SELECT @@tx_isolation;
更改数据库隔离级别,设置隔离级别为可重复读:
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
注意:须要同时修改两个窗口的事务隔离级别
时间轴 事务A 事务B T1 start transaction; T2 select p.productName,p.productCount from product p where p.productId=1;(productCount =100) T3 start transaction; T4 select p.productName,p.productCount from product p where p.productId=1;(productCount =100) T5 update product set productCount = 99 where productId = 1; T7 select p.productName,p.productCount from product p where p.productId=1;(productCount =100) T6 commit; T8 select p.productName,p.productCount from product p where p.productId=1;(productCount =100)
这里就再也不对流程作过多赘述。
小结:
能够看到
可重复读
隔离级别避免了脏读
,不可重复读
的问题,可是出现了幻读
现象。事务A查询到的小米数量等于100,可是事务B修改了数量为99,可是事务A读取到的值仍是100。当事务A去减1等于99时,是错误的,此时应该是99-1=98才对。接下来咱们再提升一个事务隔离级别。3.4事务隔离级别设置为Serializable
查询事务隔离级别
SELECT @@tx_isolation;
更改数据库隔离级别,设置隔离级别为串行化:
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
时间轴 事务A 事务B --- --- --- T1 start transaction; T2 start transaction; T2 select p.productName,p.productCount from product p where p.productId=1;(productCount =100); T4 update product set productCount = 99 where productId = 1;(等待中..)
这里就再也不对流程作过多赘述。
小结:
在咱们Serializable隔离级别中,咱们能够看到事务B去作修改动做时卡主了,不能向下执行。这是由于:给事务A的select操做上了锁,因此事务B去修改值的话,就会被卡主。只有当事务A操做执行完毕,才会执行事务B的操做。这样就避免了上述三个问题了。
问题自己
回到问题的自己,其实咱们并不须要将事务提到这么高。
问题的自己就是,当咱们读完了的时候,就要在上面加锁。咱们不但愿别人可以去读它。由于别人读到了count,就会修改count的值,并写进去。因此咱们在select 操做的时候,加上for update。这时候就会把这行操做给锁掉了。那么另一我的也进行相同的操做,也表示select 出来的count须要进行update,须要锁住。
- select p.productName,p.productCount from product p where p.productId=1 for update;
- 在实际开发过程当中,这样的加锁行为,是很是的耗系统性能的
本章节主要介绍了数据库中事务的ADID特性中的
隔离性
,在没有隔离的状况下会发生什么问题,相信你们经过本章,对数据库事务中的隔离性
有了必定的了解,下篇文章咱们将介绍数据库中的悲观锁与乐观锁
。