锁:S锁和X锁:共享锁和排它锁,也叫读锁和写锁
(1)、当读取一条数据时,先给这条记录添加S锁,其他事务也可以对这条记录添加S锁,但不能添加X锁
(2)、当修改一条数据时,先给这条记录添加X锁,其他事务不能添加 任何 锁
数据库的隔离级别及加锁(前面三个是行锁,串行化是表锁):
读未提交:查询不加锁,写的时候加S锁、事务结束释放;
读已提交:读时加S锁、读完即释放,写时加X锁、事务结束释放锁;
可重复读:读时加S锁、事务完成释放锁,写时加X锁、事务结束释放锁;
串行化:读整个表S锁,写 整个表X锁;
数据库四种事务隔离级别:
Read uncommitted(读未提交)
如果一个事务已经开始写数据,则另外一个事务不允许同时进行写操作,但允许其他事务读此行数据,该隔离级别可以通过“排他写锁”,但是不排斥读线程读数据。这样就避免了更新丢失,却可能出现脏读,也就是说事务B读取到了事务A未提交的数据
Read committed(读提交)
一个事务读数据允许其他事务读写这条数据,一个事务写数据将会禁止其他事务访问该数据,该隔离级别避免了脏读,但是可能出现不可重复读。事务A事先读取了数据,事务B紧接着更新了数据,并提交了事务,而事务A再次读取该数据时,数据已经发生了改变。
Repeatable read(可重复读)
可重复读是指在一个事务内,多次读同一个数据,读取的数据是一样的,因此称为是可重复读隔离级别,读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务(包括了读写),这样避免了不可重复读和脏读。
读数据分为快照读 和 当前读,出现幻读的原因是使用了 当前读 参考:七. 幻读是什么,幻读有什么问题_Tyella的博客-CSDN博客_幻读
Serializable(可序化)
提供严格的事务隔离,它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行,如果仅仅通过“行级锁”是无法实现序列化的,必须通过其他机制保证新插入的数据不会被执行查询操作的事务访问到。序列化是最高的事务隔离级别,同时代价也是最高的,性能很低,一般很少使用,在该级别下,事务顺序执行,不仅可以避免脏读、不可重复读,还避免了幻读
脏读:一个事务修改了数据还没有提交,另一个事务读取了这条没提交的数据;
不可重复读:A事务中查数据,B事务更新数据并提交,A再查数据和第一次查的数据不一样,主要是针对 update;
幻读:幻读是针对新插入的数据,可重复读隔离级别下避免(1)的幻读,,主要针对insert和delete
(1)、事务1读数据,事务2插入数据,事务1再次读数据,读的结果不变(快照读);
(2)、事务1读数据,事务2插入数据,事务1更新数据后再次读数据,读的结果变了(当前读);
读已提交的幻读:
左边先查询一次,右边再插入数据并提交、左边再次查询会将右边新插入的数据也查出来
可重复读的幻读:
尝试1、左边事务执行第一次查询,右边事务插入数据并提交,左边执行第二次查询,和第一次查询结果一样,未出现幻读;
尝试2、左边事务执行第一次查询,右边事务插入数据并提交,左边事务执行更新操作,左边执行第二次查询,和第一次查询结果不一样,出现幻读;
出现幻读的原因是 尝试1使用的快照读,尝试2中 执行update语句后再次查询是用的当前读
避免幻读可以使用间隙锁,这种方式只针对主键范围查询,在查询条件范围内的数据都被锁定,如下:在id范围是1-5区间内被锁定,无法插入ID在这个范围内的数据,
数据库的默认隔离级别:
mysql默认隔离级别 可重复读
oracle默认隔离级别 读已提交
undo log 与 隔离级别:
对数据库的每一笔操作在undo log中都有记录,RC(读提交)每次读取数据都会生成一个readView,每次读取的都是最新的已提交数据;RR(可重复读)在一个事务内第一次读取生成readView,后面再读的时候就不会再生成readView(只有查询没有其他操作的),所以在一个事务内每次读的内容是一样的;
脏写:A事务和B事务同时对一条数据更新,A事务查询出后未更新、B已更新提交,A再根据之前查询的数据做更新会导致数据异常; 对于事务并发导致脏写问题可以在表中添加版本号字段,每次操作对比版本号,如果不一致则说明已被其他事务修改;
spring的事务隔离级别比数据库隔离级别多一种 default(默认使用数据库的隔离级别),在spring事务隔离级别和数据库隔离级别冲突时以spring的隔离级别为准;spring设置隔离级别如下:
mysql中设置事务隔离级别(不同的mysql版本参数名不一样有的是tx_isolation,我这里是transaction_isolation):
set transaction_isolation='read-uncommitted' 读未提交
set transaction_isolation='read-committed' 读已提交
set transaction_isolation='repeatable-read' 可重复读