首先说一下数据库事务的四大特性html
事务的四大特性是ACID(不是"酸"....)mysql
原子性指的是事务要么彻底执行,要么彻底不执行.算法
事务完成时,数据必须处于一致的状态.若事务执行途中出错,会回滚到以前的事务没有执行前的状态,这样数据就处于一致的状态.若事务出错后没有回滚,部分修改的内容写入到了数据库中,这时数据就是不一致的状态.sql
同时处理多个事务时,一个事务的执行不能被另外一个事务所干扰,事务的内部操做与其余并发事务隔离.数据库
事务提交后,对数据的修改是永久性的.segmentfault
Mysql的锁其实能够按不少种形式分类:微信
这里主要讨论S锁,X锁,乐观锁与悲观锁.session
S锁与X锁是InnoDB引擎实现的两种标准行锁机制.查看默认引擎可以使用并发
show variables like '%storage_engine%';
做者的mysql版本为8.0.17,结果以下:post
先建好测试库与测试表,很简单,表就两个字段.
create database test; use test; create table a ( id int primary key auto_increment, money int );
S锁也叫共享锁,读锁,数据只能被读取不能被修改.
玩一下,上锁!
lock table a read;
而后.....
只能读不能改,删,也不能增.
X锁也叫排他锁,写锁,一个事务对表加锁后,其余事务就不能对其进行加锁与增删查改操做.
设置手动提交,开启事务,上X锁.
set autocmmmit=0; start transaction; lock table a write;
在开启另外一个事务,使用select语句.
set autocommit=0; start transaction; select * from a;
这里是阻塞select操做,由于一直都没释放X锁.
一样也不能再加锁,也是阻塞中.
回到原来那个加锁的事务,嗯,什么事也没有,正常读写.
释放锁后:
unlock table;
在另外一个事务中能够看到中断时间.
乐观锁就是老是假设是最好的状况,每次去操做的时候都不会上锁,但在更新时会判断有没有其余操做去更新这个数据,是一种宽松的加锁机制.
mysql自己没有提供乐观锁的支持,须要本身来实现,经常使用的方法有版本控制和时间戳控制两种.
举个例子,假设此时version=1,A进行操做,更新数据后version=2,与此同时B也进行操做,更新数据后version=2,A先完成操做,率先将数据库中的version设置为2,此时B提交,B的version与数据库中的version同样,不接受B的提交.
还有一种实现方法叫CAS算法,这个做者不怎么了解,有兴趣能够自行搜索.
悲观锁就是老是假设最坏的状况,在整个数据处理状态中数据处于锁定状态,悲观锁的实现每每依靠数据库的锁机制.每次在拿到数据前都会上锁.
mysql在调用一些语句时会上悲观锁,如(先关闭自动提交,开启事务):
set autocommit=0; start transaction;
两个事务都这样操做,而后其中一个事务输入:
select * from a where xxx for update;
在另外一事务也这样输入:
这时语句会被阻塞,直到上锁的那个事务commit(解开悲观锁).
在另外一事务中能够看到这个事务被阻塞了2.81s.
*** lock in share mode.
也会加上悲观锁.
脏读是指一个事务读取到了另外一事务未提交的数据,形成select先后数据不一致.
好比事务A修改了一些数据,但没有提交,此时事务B却读取了,这时事务B就造成了脏读,通常事务A的后续操做是回滚,事务B读取到了临时数值.
事务A | 事务B |
---|---|
开始事务 | 开始事务 |
更新X,旧值X=1,新值X=2 | |
读取X,X=2(脏读) | |
回滚X=1 | |
结束事务(X=1) | 结束事务 |
幻读是指并非指同一个事务执行两次相同的select语句获得的结果不一样,而是指select时不存在某记录,但准备插入时发现此记录已存在,没法插入,这就产生了幻读.
事务A | 事务B |
---|---|
开始事务 | 开始事务 |
select某个数据为空,准备插入一个新数据 | |
插入一个新数据 | |
提交,结束事务 | |
插入数据,发现插入失败,因为事务B已插入相同数据 | |
结束事务 |
不可重复读指一个事务读取到了另外一事务已提交的数据,形成select先后数据不一致.
好比事务A修改了一些数据而且提交了,此时事务B却读取了,这时事务B就造成了不可重复读.
事务A | 事务B |
---|---|
开始事务 | 开始事务 |
读取X=1 | 读取X=1 |
更新X=2 | |
提交,结束事务 | |
读取X=2 | |
结束事务 |
第一类丢失更新就是两个事务同时更新一个数据,一个事务更新完毕并提交后,另外一个事务回滚,形成提交的更新丢失.
事务A | 事务B |
---|---|
开始事务 | 开始事务 |
读取X=1 | 读取X=1 |
修改X=2 | 修改X=3 |
提交,结束事务 | |
回滚 | |
结束事务(X=1) | X=1,X本应为提交的3 |
第二类丢失更新就是两个事务同时更新一个数据,先更新的事务提交的数据会被后更新的事务提交的数据覆盖,即先更新的事务提交的数据丢失.
事务A | 事务B |
---|---|
开始事务 | 开始事务 |
读取X=1 | 读取X=1 |
更新X=2 | |
提交事务,X=2,结束 | |
更新X=3 | |
提交事务,X=3,事务A的更新丢失,结束 |
封锁协议就是在用X锁或S锁时制定的一些规则,好比锁的持续时间,锁的加锁时间等.不一样的封锁协议对应不一样的隔离级别.事务的隔离级别一共有4种,由低到高分别是Read uncommitted,Read committed,Repeatable read,Serializable,分别对应的相应的封锁协议等级.
一级封锁协议对应的是Read uncommitted隔离级别,Read uncommitted,读未提交,一个事务能够读取另外一个事务未提交的数据,这是最低的级别.一级封锁协议本质上是在事务修改数据以前加上X锁,直到事务结束后才释放,事务结束包括正常结束(commit)与非正常结束(rollback).
**一级封锁协议不会形成更新丢失,但可能引起脏读,幻读,不可重复读.
设置手动提交与事务隔离等级为read uncommited,并开启事务(注意要先设置事务等级再开启事务).**
set autocommit=0; set session transaction isolation level read uncommitted; start transaction;
(中间有一行打多了一个t能够忽略.....)
在一个事务中修改表中的值,不提交,另外一个事务能够select到未提交的值.
出现了脏读.
在一个事务中插入一条数据,提交.
另外一事务中select时没有,准备insert,可是insert时却提示已经存在.引起幻读.
未操做提交前:
另外一事务修改并提交:
再次读:
引起不可重复读.
二级封锁协议本质上在一级协议的基础上(在修改数据时加X锁),在读数据时加上S锁,读完后当即释放S锁,能够避免脏读.但有可能出现不可重复读与幻读.二级封锁协议对应的是Read committed与Repeatable Read隔离级别.
先设置隔离等级
set session transaction isolation level read committed;
Read committed,读提交,读提交能够避免脏读,但可能出现幻读与不可重复读.
开启一个事务并更新值,在这个事务中money=100(更新后)
另外一事务中money为未更新前的值,这就避免了脏读.
注意,事实上脏读在read committed隔离级别下是不被容许的,可是mysql不会阻塞查询,而是返回未修改以前数据的备份,这种机制叫MVCC机制(多版本并发控制).
在一个事务中插入数据并提交.
另外一事务中不能插入"不存在"的数据,出现幻读.
事务修改并提交前:
事务修改并提交:
出现不可重复读.
Repeatable read比Read committed严格一点,是Mysql的默认级别,读取过程更多地受到MVCC影响,可防止不可重复读与脏读,但仍有可能出现幻读.
在一个事务中修改数据,不提交.
另外一事务中两次select的结果都不变,没有出现脏读.
一个事务修改数据并提交.
另外一事务中select的结果没有发生改变,即没有出现不可重复读.
同理,一个事务插入一条数据并提交.
另外一个事务插入时出现幻读.
三级封锁协议,在一级封锁协议的基础上(修改时加X锁),读数据时加上S锁(与二级相似),可是直到事务结束后才释放S锁,能够避免幻读,脏读与不可重复读.三级封锁协议对应的隔离级别是Serializable.
先设置Serializable隔离级别
set session transaction isolation level serializable
设置事务隔离等级后开启事务并update,发现堵塞.从而避免了脏读.
插入时直接阻塞,避免了幻读.
在脏读的例子中能够知道,update会被堵塞,都不能提交事务,所以也避免了不可重复读.
事务必须分为两个阶段对数据进行加锁与解锁,两端锁协议叫2PL(不是2PC),全部的加锁都在解锁以前进行.
加锁会在更新或者
select *** for update *** lock in share mode
时进行
解锁在事务结束时进行,事务结束包括rollback与commit.
最后,如下是做者的微信公众号,里面有更多精彩文章,欢迎关注.一块儿学习,一块儿成长.
参考连接
1:ACID1
2:ACID2
3:mysql的锁1
4:乐观锁与悲观锁1
5:乐观锁与悲观锁2
6:乐观锁与悲观锁3
9:数据库封锁协议
10:mysql事务隔离机制1
11:mysql事务隔离机制2
12:mysql幻读
14:mysql两段锁1
15:mysql两段锁2