1.MYSQL(一)---基础架构(查询)
2.MYSQL(二)---日志系统(更新)
3.MYSQL(三)---事务隔离
事务,和数据库打交道事务是不可缺少的,为什么要用事务,最简单的案列就是转账的案例,A向B转账100元,这个过程需要查询、加减、更新等,这个过程需要保证是一起的,如果中间出现错误,A的100转给了多个人,这就是不能发生的事情。而事务就是要保证这个过程,要么完成,就全部完成,否则就全部失败。保证数据的完整性
在MySQL中,自带的MyISAM是不支持事务的,所以我们学习都是一INnoDB为准。
事务的四大特性ACID
原子性(Atomicity):事务中的所有操作作为一个整体像原子一样不可分割,要么全部成功,要么全部失败。
一致性(Consistency):事务的执行结果必须使数据库从一个一致性状态到另一个一致性状态。一致性状态是指:1.系统的状态满足数据的完整性约束(主码,参照完整性,check约束等) 2.系统的状态反应数据库本应描述的现实世界的真实状态,比如转账前后两个账户的金额总和应该保持不变。
隔离性(Isolation):并发执行的事务不会相互影响,其对数据库的影响和它们串行执行时一样。比如多个用户同时往一个账户转账,最后账户的结果应该和他们按先后次序转账的结果一样。
持久性(Durability):事务一旦提交,其对数据库的更新就是持久的。任何事务或系统故障都不会导致数据丢失。
其中在开始举例中,就是展示数据的原子性。
我们在使用数据库的过程中,导致数据出错主要来自两个方面:1.并发控制2.数据库故障
其中ACID中,ACI是为了并发控制,CID则是为了保证数据库故障时候数据的不丢失(这就需要上文中的binlog恢复数据)。
并发问题导致:脏读(dirty read)、不可重复读(non-repeatable read)、幻读(phantom read)这就需要从隔离级别来讲。
事务的隔离和隔离级别
我们需要知道,在实现功能的时候,我们控制的越严密,那么效率就会相对下降。这是不可避免的,所以在事务的隔离中,提供了几种,需要我们自己去选择,平衡数据和效率之间的关系。
SQL标准的事务隔离级别包括:读未提交(read uncommitted)、读已提交(read committed)、可重复读(repeatable read)和串行化(serializable )。
- 读未提交:在事务没有提交的时候,它的数据更改其能被其他看到
- 读已提交:在事务进行提交后,它的变更才能被其他看到
- 可重复读:在这个事务进行的过程中,所看到的数据和事务启动得时候是一致的。(这个过程,读未提交也看不到)
-
串行化:对于一条数据,读写会分别加“读锁”和“写锁”,当出现读写锁冲突时,会等待一个事务执行完,再执行另一个。
这个过程可以通过例子进行简单实现:
在这个过程中,数据库里面会创建一个视图,访问的时候已视图得逻辑为准。 读未提交直接返回记录得最新值,没有视图得概念,读已提交是在开始执行SQL时候创建得。可重复读是在事务启动得时候创建,整个事务存在期间都在用这个视图,串行化是用锁机制来进行隔离的。
事务的咖喱级别必定是和性能息息相关,具体使用哪个视图,肯定是由自己进行选择。
可重复读场景:假设你在管理一个个人银行账户表。一个表存了每个月月底的余额,一个表存了账单明细。这时候你要做数据校对,也就是判断上个月的余额和当前余额的差额,是否与本月的账单明细一致。你一定希望在校对过程中,即使有用户发生了一笔新的交易,也不影响你的校对结果。
事务隔离的实现
以“可重复读”为例
在MySQL中,实际上每个记录在更新的时候都会记录一条回滚操作,记录上的最新值,都乐意通过回滚操作,得到前一个状态的值。
系统在记录值得空间会不会一直变大?答案是,系统会自动在不需要的时候进行删除。
同时,我们在使用的过程中,尽量避免长事务。
长事务意味着系统里面会存在着很老的事务视图,由于这些事务随时可能访问数据库中得任何数据,所以在事务提交之前,数据库里面它可能用到的会馆记录都必须保留,就会导致大量存储空间。
事务的启动方式
如上所述,长事务会存在着一些风险,使用的过程中是尽量避免,但是有时我们无意使用到这些长事务。MySQL事务启动有如下:
1.显式启动事务语句,begin或者start transation。配套的提交语句是commit,回滚语句是rollback。
2.set autocommit=0.这个命令会将这个线程自动提交关闭,以为之执行select语句就会启动事务了,而且不会自动提交。这个事务会持续到主动执行commit或者rollback,或者断开链接。
有些客户端在连接的过程中会自动执行 set autocommit=0的命令,这就导致在以后的查询都在事务中,如果是长连接,就会导致长事务。
但是有时候就会担心,主动提交的话就会多一次交互,对于频繁使用的业务,第二种方式在开始不会主动执行一次'begin',减少了交互次数。如果有这个顾虑,还是使用commit work and chain语法。
在autocomit=1的情况下,用begin显示启动事务,如果执行commit则提交事务。如果执行commit work and chain,则是提交事务并主动启动下一个事务,这样省去可再次执行begin的开销。同时带来的好处就是从程序开发的角度明确的知道每个语句是否处在事务中。
可以在informaion_schema库中得innodb_trx表中查询长事务,
比如下面这个语句,用于查找持续时间超过60s的事务。
select * from information_schema.innodb_trx where TIME_TO_SEC(timediff(now(),trx_started))>60