事务
事务就是一组原子性的sql查询,一个独立的工作单元,事务内的语句只要有一条不成功则所有就执行失败,要么全部执行成功,要么全部执行失败
例子:转账
A转给B转帐100,3个步骤 支票表(checking) 储蓄表(savings)
1.检查A余额是否高于100
2.A余额减去100
3.B增加200
3个步骤必须打包成 一个事务,任何一个步骤失败,则转账失败
#使用start transaction开启一个事务,然后使用commit提交事务将修改的数据持久保留,要么使用rollback撤销所有修改
start transaction;
select balance from checking where customer_id = 1111;
update checking set balance = balance - 100.00 where customer_id = 1111;
update savings set balance = balance - 100.00 where customer_id = 1111;
commit;
单纯的事务概念并不是全部:
如果执第四条语句的时候服务器崩溃了用户可能损失100
如果执行第三条语句和第四语句之间的时候,另一个进程要删除支票账户的所有余额,在不知道这个逻辑下B白捡了100
除非系统经过严格的ACID测试,ACID表示原子性(atomicity)一致性(consistency)隔离性(isolation)持久性(durability)一个运行良好的事务必须具备这些标准特征
原子性(atomicity)
一个事务必须被视为一个不可分割的最小单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来说不能只执行其中一部分操作,这就是事务的原子性
一致性(consistency)
数据库总是从一个一致性的状态转换到另一个一致性的状态,一致性确保了,即使在第三条四条之间时系统崩溃,支票账户也不会损失100,因为事务没有提交,所以事务修改的数据并不会保存到数据库
隔离性(isolation)
通常来说,一个事务所做的修改在最终提交之前,对其他事务是不可见的,当执行第三局,第四句还未开始,此时另一个账户汇总程序进行开始运行,则他会看到支票账户的余额并没有减去100
持久性(durability)
一旦事务提交,则其所作的修改就会永久保存到数据库中,此时及时服务器崩溃修改的数据也不会丢失,持久性是模糊的概念,因为实际上持久性也分很多不同的级别,有些持久性策略能够提供非常欠打的安全保障,而且不可能
隔离级别
sql定义4种隔离界别,每一种级别规定了一个事务中的修改,那些事务内和事务间是可见的,那些是不可见的较低级别的隔离通常可以执行更高的并发,系统开销低
READ UNCOMMITTED(未提交读)
这个隔离级别,事务中的修改,即使没有提交,对其他事务也都是可见的,事务可以读取未提交的数据,这也被称为脏读(Dirty Read )这个级别会导致很多问题,从性能来说,READ UNCOMMITTED不会比其他的级别好太多,但是缺乏其他其别的好处。
READ COMMITTED(提交读)
大多数的默认隔离级别都是READ COMMITTED(mysql不是),READ COMMITTED满足前面提到的隔离性的简单定义,一个事务开始时,只能看见一体机奥的事务所做的修改,换句话说,一个事务开始时,只能看见已经提交的事务所作的修改,一个事务从开始直到提交前,所作的任何修改对其他十五都是不可见的,这个级别有时候也叫做不可重复读(nonrepeatable read)因为两次执行同样的查询,可能得到不一样的结果
REPEATABLE READ(可重复读)
REPEATABLE READ解决了脏读的问题,该级别保证了在同一个事务中多次读取同样的记录的结果是一致的,但是理论上,可重复读隔离级别还是无法解决另外一个幻读(Phantom READ)问题(当某个事物在读取某个范围内的记录时,另外一个十五又在该范围内插入了新的数据,将之前的事务再次读取该范围的记录时会产生(Phantom READ),InnoDB和XtraDB存储引擎通过多版本并发控制(MVCC multiversion Concurrency Control)解决了幻读的问题)可重复读时mysql默认事务隔离级别
SERIALIZABLE(可串行化)
SERIALIZABLE是最高的隔离级别,它通过强制事务串行执行,避免了钱前面说的幻读的问题。简单来说,SERIALIZABLE会在读取的每一行数据上都加锁,所以可能导致大量的超时时间和锁争用的问题,实际应用中也很少用到这个隔离级别,只是非常需要确保数据的一致性而且,可以接受没有并发的情况下,才考虑这个级别
隔离级别 | 脏读可能性 | 不可重复度可能性 | 幻读可能性 | 加读锁 |
---|---|---|---|---|
READ UNCOMMITTED | yes | yes | yes | no |
READ COMMITTED | no | yes | yes | no |
REPEATABLE READ | no | no | yes | no |
SERIALIZABLE | no | no | no | yes |
死锁
死锁是指两个或者多个事务在同一资源互相占用,并请求锁定对方占用的资源,从而导致恶性循环现象,当多个事务试图以不同的顺序锁定资源时,就可能会产生死锁,多个事务同时锁定同一个资源时,也会产生死锁,
看以下现象:
//事务1;
start transaction;
update st set so=1 where name = yzq and data='2020-09-08';
update st set so=2 where name = zq and data='2020-09-08';
commit;
//事务2;
start transaction;
update st set si=3 where name = zq and data='2020-09-08';
update st set si=4 where name=yzq and data='2020-09-08';
commit;
两条事务都执行第一条update,更新了一条数据,准备执行第二条语句的时候 发现改行已经别对方锁定,然后两个事务都在等待对方释放锁,同事有持有对方的需要的锁,这时候就会陷入死循环
死锁解决方法:
数据库实现了各种死锁检测和死锁超时机制,InnoDB可以检查到死锁的循环依赖,并立即返回一个错误,死锁会导致慢查询
当查询的时间达到锁等待超时的设后放弃锁请求,InnoDB目前处理方式,将持有最少行级排他锁的事务进行回滚
锁的行为和顺序和存储引擎相关,以同样的顺序执行语句,有些存储引擎会产生死锁,死锁的产生有双重原因,有些是因为真正的数据冲突,有些完全是由存储引擎的实现方式导致
死锁发生之后,只有部分或者完全回归其中一个事务才能打破死锁,大多数情况下只需要重新执行因死锁回滚的事务即可
事务日志:
特点:可以帮助提高事务的效率,使用事务日志存储引擎修改表数据时只需要修改其内存拷贝,再把修改行为记录到持久在硬盘的事务日志中,事务日志采用的追加方式
写日志的操作是磁盘上一块区域内的顺序I/O,不像随机I/O需要在磁盘的多个地方移动磁头,所以采用事务日志方式相对来说比较快的多,事务日志持久后,内存修改的数据在后台可以慢慢的刷回磁盘,修改数据需要写两次磁盘,预写式日志(write-Ahead logging)(相当于缓存,把日志写入到缓存,之后利用缓存写入到磁盘)
mysql提供两种事务型的存储引擎:InnoDB,NDB Cluster
另外还有一些第三方存储引擎也支持事务