数据库锁和隔离级别
数据库锁定机制简单来说,就是数据库为了保证数据的一致性,而使各种共享资源在被并发访问变得有序所设计的一种规则。
1.表级锁定(table-level)
表级别的锁定是MySQL各存储引擎中最大颗粒度的锁定机制。该锁定机制最大的特点是实现逻辑非常简单,带来的系统负面影响最小。所以获取锁和释放锁的速度很快。由于表级锁一次会将整个表锁定,所以可以很好的避免困扰我们的死锁问题。【使用表级锁定的主要是MyISAM,MEMORY,CSV等一些非事务性存储引擎。】
2.行级锁定(row-level)
行级锁定最大的特点就是锁定对象的颗粒度很小,也是目前各大数据库管理软件所实现的锁定颗粒度最小的。由于锁定颗粒度很小,所以发生锁定资源争用的概率也最小,能够给予应用程序尽可能大的并发处理能力而提高一些需要高并发应用系统的整体性能。【使用行级锁定的主要是InnoDB存储引擎。】
3.页级锁定(page-level)
页级锁定是MySQL中比较独特的一种锁定级别,在其他数据库管理软件中也并不是太常见。【使用页级锁定的主要是BerkeleyDB存储引擎。】
悲观锁:
数据库总是认为多个数据库并发操作会发生冲突,所以总是要求加锁操作。悲观锁主要表锁、行锁、页锁。
乐观锁:
数据库总是认为多个数据库并发操作不会发生冲突,所以总是不加锁操作。所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让返回用户错误的信 息,让用户决定如何去做。乐观锁的实现方式一般包括使用版本号和时间戳。
表级锁:
读锁锁表,会阻碍其他事务修改表数据。写锁锁表会阻碍其他事务读与写。
页级锁:
就是对页加锁
行级锁:
行锁分为共享锁和排他锁,共享锁:一个事务对一行的共享只读锁。排它锁:一个事务对一行的排他读写锁。
共享锁:
加锁与解锁:当一个事务执行select语句时,数据库系统会为这个事务分配一把共享锁,来锁定被查询的数据。在默认情况下,数据被读取后,数据库系统立即解除共享锁。例如,当一个事务 执行查询“SELECT * FROM accounts”语句时,数据库系统首先锁定第一行,读取之后,解除对第一行的锁定,然后锁定第二行。这样,在一个事务读操作过程中,允许其他事务同时更新 accounts表中未锁定的行。
兼容性:如果数据资源上放置了共享锁,还能再放置共享锁和更新锁。
并发性能:具有良好的并发性能,当数据被放置共享锁后,还可以再放置共享锁或更新锁。所以并发性能很好。
排它锁:
加锁与解锁:当一个事务执行insert、update或delete语句时,数据库系统会自动对SQL语句操纵的数据资源使用独占锁。如果该数据资源已经有其他锁(任何锁)存在时,就无法对其再放置 独占锁了。
兼容性:独占锁不能和其他锁兼容,如果数据资源上已经加了独占锁,就不能再放置其他的锁了。同样,如果数据资源上已经放置了其他锁,那么也就不能再放置独占锁了。
并发性能:最差。只允许一个事务访问锁定的数据,如果其他事务也需要访问该数据,就必须等待。
更新锁:
加锁与解锁:当一个事务执行update语句时,数据库系统会先为事务分配一把更新锁。当读取数据完毕,执行更新操作时,会把更新锁升级为独占锁。
兼容性:更新锁与共享锁是兼容的,也就是说,一个资源可以同时放置更新锁和共享锁,但是最多放置一把更新锁。这样,当多个事务更新相同的数据时,只有一个事务能获得更新锁,然后 再把更新锁升级为独占锁,其他事务必须等到前一个事务结束后,才能获取得更新锁,这就避免了死锁。
并发性能:允许多个事务同时读锁定的资源,但不允许其他事务修改它。
意向锁(意向共享,意向更新):
在判断每一行是否已经被行锁锁定效率比较低下,因此使用意向锁,当发现表上有意向共享锁,说明表中有些行被共享行锁锁住了,因此,事务B申请表的写锁会被阻塞。
===============================》
给SQL语句加锁
共享锁(S):SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE 排他锁(X):SELECT * FROM table_name WHERE ... FOR UPDATE
-------------------------------------------------
--------------------------------------------------
事务隔离级别Isolation Level
1、事务具有ACID特性
原子性(atomicity):一个事务被事务不可分割的最小工作单元,要么全部提交,要么全部失败回滚。
一致性(consistency):数据库总是从一致性状态到另一个一致性状态,它只包含成功事务提交的结果
隔离型(isolation):事务所做的修改在最终提交一起,对其他事务是不可见的
持久性(durability):一旦事务提交,则其所做的修改就会永久保存到数据库中。
并行事务的四大问题:
1.更新丢失:和别的事务读到相同的东西,各自写,自己的写被覆盖了。(谁写的快谁的更新就丢失了)
2.脏读:读到别的事务未提交的数据。(万一回滚,数据就是脏的无效的了)
3.不可重复读:两次读之间有别的事务修改。
4.幻读:两次读之间有别的事务增删。
对应隔离级别
1.READ UNCOMMITTED:读未提交,不处理。
2.READ COMMITTED:读已提交,只读提交的数据,无脏读;
3.REPEATABLE READ:可重复读,加行锁,两次读之间不会有修改,无脏读无重复读;
4.SERIALIZABLE: 串行化,加表锁,全部串行,无所有问题。
1.READ UNCIMMITTED(未提交读)
事务还没提交,而别的事务可以看到他其中修改的数据的后果,也就是脏读。
2.READ COMMITTED(提交读)
首先大多数数据库系统的默认隔离级别是READ COMMITTED,这种隔离级别就是一个事务的开始,只能看到已经完成的事务的结果,正在执行的,是无法被其他事务看到的。这种级别会出现读取旧数据的现象
3.REPEATABLE READ(可重复读)
REPEATABLE READ解决了脏读的问题,该级别保证了每行的记录的结果是一致的,也就是上面说的读了旧数据的问题,但是却无法解决另一个问题,幻行,顾名思义就是突然蹦出来的行数据。指的就是某个事务在读取某个范围的数据,但是另一个事务又向这个范围的数据去插入数据,导致多次读取的时候,数据的行数不一致。
4.SERIALIZABLE(可串行化)
SERIALIZABLE是最高的隔离级别,它通过强制事务串行执行(注意是串行),避免了前面的幻读情况,由于他大量加上锁,导致大量的请求超时,因此性能会比较低下,在需要数据一致性且并发量不需要那么大的时候才可能考虑这个隔离级别。
各个隔离级别的原理:
READ_UNCOMMITED 的原理:
-
事务对当前被读取的数据不加锁;
-
事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加 行级共享锁,直到事务结束才释放。================》
-
-
事务1读取某行记录时,事务2也能对这行记录进行读取、更新;当事务2对该记录进行更新时,事务1再次读取该记录,能读到事务2对该记录的修改版本,即使该修改尚未被提交。
-
事务1更新某行记录时,事务2不能对这行记录做更新,直到事务1结束。
-
-
READ_COMMITED 的原理:
-
事务对当前被读取的数据加 行级共享锁(当读到时才加锁),一旦读完该行,立即释放该行级共享锁;
-
事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加 行级排他锁,直到事务结束才释放。
=====================》
-
事务1读取某行记录时,事务2也能对这行记录进行读取、更新;当事务2对该记录进行更新时,事务1再次读取该记录,读到的只能是事务2对其更新前的版本,要不就是事务2提交后的版本。
-
事务1更新某行记录时,事务2不能对这行记录做更新,直到事务1结束。
REPEATABLE READ 的原理:
-
事务在读取某数据的瞬间(就是开始读取的瞬间),必须先对其加 行级共享锁,直到事务结束才释放;
-
事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加 行级排他锁,直到事务结束才释放。
=======================》
-
事务1读取某行记录时,事务2也能对这行记录进行读取、更新;当事务2对该记录进行更新时,事务1再次读取该记录,读到的仍然是第一次读取的那个版本。
-
事务1更新某行记录时,事务2不能对这行记录做更新,直到事务1结束。
SERIALIZABLE 的原理:
-
事务在读取数据时,必须先对其加 表级共享锁 ,直到事务结束才释放;
-
事务在更新数据时,必须先对其加 表级排他锁 ,直到事务结束才释放。
==================》
-
事务1正在读取A表中的记录时,则事务2也能读取A表,但不能对A表做更新、新增、删除,直到事务1结束。
-
事务1正在更新A表中的记录时,则事务2不能读取A表的任意记录,更不可能对A表做更新、新增、删除,直到事务1结束。
共享锁(S):发生在数据查找之前,多个事务的共享锁之间可以共存
排他锁(X):发生在数据更新之前,排他锁是一个独占锁,与其他锁都不兼容
更新锁(U):发生在更新语句中,更新锁用来查找数据,当查找的数据不是要更新的数据时转化为S锁,当是要更新的数据时转化为X锁
意向锁:发生在较低粒度级别的资源获取之前,表示对该资源下低粒度的资源添加对应的锁,意向锁有分为:意向共享锁(IS) ,意向排他锁(IX),意向更新锁(IU),共享意向排他锁(SIX),共享意向更新锁(SIU),更新意向排他锁(UIX)
=====》
锁
对于Mysql来说实现了两种行级锁:
共享锁:允许事务读一行数据,一般记为S,也称为读锁。
排他锁:允许事务删除或者更新一行数据,一般记为X,也称为写锁。
隔离级别
Mysql的4个隔离级别:
读未提交Read Uncommitted:能读到其他事务还没有提交的数据,这种现象叫做脏读。
读已提交Read Committed:只会读取其他事务已经提交的数据,所以不会产生RC的脏读问题。所以又带来一个问题叫做不可重复读,一个事务中两次一样的SQL查询可能查到的结果不一样。
可重复读Repeatable Read:RR是Mysql的默认隔离级别,一个事务中两次SQL查询总是会查到一样的结果,不存在不可重复读的问题,但是还是会有幻读的问题。
串行Serializable:串行场景没有任何问题,完全串行化的操作,读加读锁,写加写锁。
===========》
幻读指的是同一事务下,不同的时间点,同样的查询,得到不同的行记录的集合。
如果说一个select
执行了两次,但是第二次比第一次多出来行记录,这就是幻读。
所以,对于幻读来说那一定是新增插入的数据!