数据库的四大隔离级别:
read uncommitted; 数据库不做任何隔离性控制,数据库具有脏读、不可重复读和虚读(幻读)问题。
read committed; 数据库可以防止脏读问题,具有不可重复读和虚读(幻读)问题
repeatable read; 数据库可以防止脏读、不可重复读,具有虚读(幻读)问题
serializable; 数据库将会处在串行化,可以防止所有隔离性的问题,但是性能非常低下。
从安全性上考虑:
serializable > repeatable read > read committed > read uncommitted
从效率上考虑:
read uncommitted > read committed >repeatable read > serializable
数据库的使用者应该根据自己的业务需求选择一个在能够防止想要防止的问题的基础上性能最高的一个隔离级别。
mysql的默认隔离级别repeatable read
select @@tx_isolation; 查询隔离级别
set session/global transaction isolation level xxxxxxx; 修改隔离级别
**如果写session改变的是当前客户端和数据库进行交互时使用的隔离级别
**如果写global当前客户端和数据库进行交互时的隔离级别不变,改变的是数据库默认的隔离级别,影响新开的客户端和数据库交互时采用的默认的隔离的级别。
数据库中的锁:
共享锁:在非Serializable隔离级别下,查询不加任何锁,在Serializable隔离级别下查询加共享锁。
共享锁和共享锁可以共存,共享锁和排他锁不能共存。
排他锁:在任何隔离级别下进行增删改都会加排他锁
排他锁和任意锁都不能共存。
两个并发的修改: 无论任何隔离级别 增删改都加排他 两个排他不能共存 可以保证两个并发的修改一定隔离开
两个并发的查询: 无论什么情况都可以并发查询
非Serializable 非Serializable 两个都不加锁 可以共存,并发查询
Serializable 非Serializable 一个加共享锁 另一个不加锁 可以共存,并发查询
Serializable Serializable 两个都加共享锁 可以共存,并发查询
一个修改,一个查询:serializable的工作机制:
一个查询加了共享锁 ,其他修改试图加排他,由于共享和排他不能共存,只能等待,自然就不会造成隔离性问题。
五、更新丢失问题
更新丢失问题:两个并发的线程基于同一个查询结果进行修改,后提交的事务忽略了先提交的事务对数据库的影响,产生更新丢失问题。
例子:重复充值的案例
例子:小芳的例子
解决方案:
将数据库设置位Serializable隔离级别就可以原生的防止更新丢失问题,但是Serializable性能低下,一般不会将数据库的隔离级别设置位Serializable
在非Serializable隔离级别下:
悲观锁:悲观锁悲观认为每次查询都会造成更新丢失,所以在查询时手动加排他锁,从而防止其他并发事务的查询/修改,从而防止更新丢失问题。
select * from user for update;
乐观锁:乐观锁乐观的认为,每次更新都不会造成更新丢失,正常的查询和更新,只有检测到发生了更新丢失,才进行补救。乐观锁通常通过数据库中增加一个版本字段来工作。
如果查询比较多,更新比较少,用乐观锁。如果更新比较多,而查询比较少用悲观锁。