但愿这篇文章可以阐述清楚跟数据库相关的四个概念:事务、数据库读现象、隔离级别、锁机制数据库
先来看下百度百科对数据库事务的定义:并发
做为单个逻辑单元执行一系列操做,要么彻底执行,要么彻底不执行。事务处理能够确保除非事务性单元内的全部操做都成功完成,不然不会永久更新面向数据的资源。性能
事务有四个属性,称为ACID属性:spa
一、原子性(Atomicity):事务是一个原子单位,要么所有执行,要么所有不执行。3d
二、一致性(Consistent):事务的开始和结束,数据都必须保持一致状态。对象
三、隔离性(isolation):数据库系统提供隔离机制,保证并发事务之间是互相不干扰的。也就意味着事务处理过程当中的中间状态对其余的事务是透明的。blog
四、持久性(Durable):事务完成以后,对数据的修改是永久性的,即便出现系统故障也可以保持事务
事务是一系列SQL语句的集合,若是没有事务,会出现什么问题?或者说SQL只能一条一条的单个执行,会出现什么问题?ci
这个很简单,若是没有事务,咱们平时生活中的银行转帐就没法操做。资源
ACID属性里面有一个是隔离级别,即并发事务之间互相不干扰。互相不干扰只是一个终极状态,且须要消耗巨大的性能。在咱们实际应用过程当中,是存在很大的灰度空间的:隔离级别有程度的区分。因此若是隔离程度控制的比较弱的话,就会产生脏读、不可重复读以及幻读的现象。
事务T1修改某个字段的值,而后事务T2读取该值,此后T1撤销了对该字段的更新,或者更新成另外的值才commit到数据库中,这样T2读取的数据是无效的或者错误的。致使T2依据脏数据所作的操做也是错误的。
---------思聪同窗中午去食堂吃饭,看到窗边的座位被如花同窗占有了,思聪认为这个座位已经被占有了,就转身去找其余的座位。不料,如花同窗起身离开了。事实是:如花并非吃饭,而是临时坐在那里等她的约会对象,只是临时小坐一会,并无真正“commit”。
在数据库访问中,一个事务范围内的两次相同的查询却返回了不一样的数据。
事务T1读取某一数据,事务T2读取并修改了该数据,T1为了对读取值进行验证而从新读取,却发现获得了不一样的结果。
---------思聪同窗中午去食堂吃饭,看到窗边的座位是空的,便屁颠屁颠的跑去打饭,回来后却发现这个座位被如花同窗抢去了。
幻读解决了不可重复读的问题,即在同一个事务范围内,两次相同的查询结果是相同的。可是能够新增表中的数据记录。
幻读是指事务T1对表中的数据进行修改,假设修改涉及了表中所有的数据行,同时第二个事务也修改这个表中的数据,这种修改是向表中插入一条新的数据。后面就会出现操做了T1事务的用户发现表中还有没有修改的数据行,仿佛出现了幻觉同样。
--------思聪同窗中午去食堂吃饭,看到窗边的座位是空的,便屁颠屁颠的跑去打饭,回来后窗边的座位仍是空的,便很高兴坐上去准备开始吃饭,这时候却发现如花同窗搬了一个小板凳坐在旁边狼吞虎咽,思聪顿时没有了胃口。
若是须要解决脏读、不可重复读、幻读等这些数据库读现象,就必须相应提升事务的隔离级别。可是数据库的隔离级别越高,对应的并发能力就越弱,性能也就相应的越差,因此咱们还需根据具体的应用场景去权衡。
事务的最低隔离级别,在这种隔离级别下,一个事务能够读取另一个事务未提交的数据。
数据库锁实现原理:
事务T在读数据的时候并未对数据进行加锁,事务T在修改数据的时候对数据增长行级共享锁
T1在读取数据时,T2能够对相同数据进行读取、修改。由于T1没有进行任何锁操做;当T2对记录进行修改时,T1再次读取数据能够读取到T2修改后的数据。由于T2对数据进行修改只增长了行级共享锁,T1能够再增长共享读锁进行数据读取(尽管T2没有提交事务)
如上所述,这种隔离级别,会致使脏读现象
在一个事务修改数据过程当中,若是事务没有进行提交,其余事务不能读取该数据
数据库锁实现原理:
事务T在读取数据时增长行级共享锁,读取一旦结束,当即释放;事务T在修改数据时增长行级排他锁,直到事务结束才释放。
T1在读取数据的过程当中,T2也能够对相同数据进行读取,可是不能进行修改(T1增长的是共享锁,T2也能够增长共享锁,可是不能增长排他锁)。T1读取结束后,会当即释放共享锁,这时T2能够增长排他锁,对数据进行修改,而此时T1既不能对数据进行读取也不能进行修改,直到T2事务结束。
如上所述,这种隔离级别,解决了脏读问题,可是不能解决不可重复读现象。
事务T在数据读取时,必须增长行级共享锁,直到事务结束;事务T在修改数据过程当中,必须增长行级排他锁,直到数据结束。
数据库锁实现原理:
T1在读取数据的过程当中,T2也能够对相同数据进行读取,可是不能进行修改(T1增长的是共享锁,T2也能够增长共享锁,可是不能增长排他锁)。直到T1事务结束后,才会释放共享锁,这时T2才能够增长排他锁,对数据进行修改。
如上所述,这种隔离级别,解决了不可重复读现象,可是这种隔离级别解决不了幻读的问题:
T1进行查询,读取了10条记录,并对十条记录增长了行级锁,此时T2是没法对这10行数据进行修改操做的,可是因为没有表级锁,它能够增长一条知足T1查询条件的记录。随后T1在进行查询时,会发现虽然10条记录没有改变,可是忽然多了一条记录。
产生幻读是因为没有进行范围查询时没有增长范围锁。
数据库锁实现原理:
事务T在读取数据时,必须先增长表级共享锁,直到事务结束才释放;事务T在修改数据时,必须先增长表级排他锁,直到事务结束才释放。
T1在读取A表时,增长了表级共享锁,此时T2也能够读取A表,可是不能进行任何数据的修改,直到T1事务结束。随后T2能够增长对A表的表级排他锁,此时T1不能读取A表中的任何数据,更不能进行修改。
如上所述,可序列化解决了脏读、不可重复读、幻读等读现象,可是隔离级别愈来愈高的同时,在并发性上也就愈来愈低。
默认状况下,MYSQL是自动提交的,也就意味着平时咱们执行一条update语句时,MYSQL是自动帮咱们提交的,尽快咱们没有显示执行commit命令。可是这种只适用于单条SQL的执行。
若是咱们想要同时执行多条SQL,而且执行过程当中有SQL执行异常,须要回滚前面已经成功执行的SQL或者最终想回滚所有,则必须显示的使用事务。
一、开始一项事务:start tr ansaction或者begin;
二、提交事务:commit;
三、回滚事务:rollback;
四、事务提交以后的操做:chain;
五、事务回滚以后的操做:release;
六、修改当前链接的提交方式:set autocommit;若是设置了set autocommit=0,则设置以后全部的事务都须要显式的经过命令来进行提交或者回滚。
客户端A 开启事务,并更新数据
此时事务尚未提交,开启客户端B,并进行查询,此时的数据仍是未更新前的
客户端A进行事务提交,而后客户端B查询,此时是最新的数据
若是在提交的时候使用commit and chain,那么在提交后当即开始一个新的事务
A提交事务后,B再进行查询
锁表期间,用start transaction 命令开始一个新事务,则会隐式的执行unlock tables
A对表进行写锁操做
此时B进行查询:因为被A锁表,因此查询被阻塞
A开启一个事务
因为A开启事务,隐式的释放了写锁,因此B的查询再也不被阻塞
事务中能够经过定义SAVEPOINT,指定回滚事务的一个部分
A开启事务并insert一条记录,并设置savepoint
B进行查询,查询到的是开启事务前的数据
A又插入一条数据,而后回滚到savepoint
B进行查询