Database(六) mysql的事务和实现

一:简介

1.事务特性:ACID
   原子性:Atomicity:一个事务必须被视为不可分割的最小单元,事务中的所有操作,要么全部提交成功,要么全部失败回滚; 
  一致性:Consistency:事务必须从一个一致性的状态变换到另一个一致性的状态,即事务执行之前和执行之后都必须处于一致性的状态;可分为强一致性和弱一致性;
  隔离性:Isolation:当多用户并发访问数据库时,数据可以根据不同的隔离级别达到隔离的要求;最高的要求是实现不同事务间,完全不影响;
  持久性:Durability:一个事务一旦被提交了,那么对数据库的改变就是永久性的,即使数据库系统遇到故障,也不会丢失提交事务的操作;
2.事务的提交和回滚
 
 2.1事务的开始和提交
      在未开启autocommit的情况下,事务的开始是使用start transaction,用来声明开启一个事务,事务的提交是commit,事务的动作在中间进行,如下面:

start transaction;

……  #一条或多条sql语句

commit;

2.2 事务的回滚
   如果sql语句执行出现问题,会调用rollback,回滚所有已经执行成功的sql语句。当然,也可以在事务中直接使用rollback语句进行回滚。
2.3 自动提交
  mysql中默认采用的是自动提交(autocommit)模式,即如果没有显示的声明start transaction,则由引擎自动自动声明一个事务,并在完成后默认提交;
  查询是否是自动提交,可以用 show variables like 'autocommit';
  在关闭了autocommit的情况下,存在一些特殊的命令,会马上强制执行commit提交事务;如DDL语句(create table/drop table/alter/table)、lock tables语句等等。不过,常用的select、insert、update和delete命令,都不会强制提交事务。

二:事务原子性的实现

 1.定义
   
原子性是指一个事务是一个不可分割的工作单位,要么全做。要么全不做,当其中的一个sql语句执行失败,则已执行的语句也必须全部回滚,数据库回退到事务前的状态;
2.实现原理:undo log
     
undo log又称为回滚日志,是Mysql的日志的一种;Mysql作为开源数据库中的佼佼者,小巧、效率高、可靠性好,高可靠性就是靠这些日志实现的,包括二进制日志binlog、错误日志error log、慢日志slow log、重做日志redo log等,其中undo log是实现事务原子性和隔离性的基础;
       在说明原子性原理之前,首先介绍一下MySQL的事务日志。MySQL的日志有很多种,如二进制日志、错误日志、查询日志、慢查询日志等,此外InnoDB存储引擎还提供了两种事务日志:redo log(重做日志)和undo log(回滚日志)。其中redo log用于保证事务持久性;undo log则是事务原子性和隔离性实现的基础。在隔离性的时候,讲了undo链在MVCC中的作用,这里主要说下原子性;
      在事务实现原子性的时候,如果成功则提交修改,如果失败,则需要将先前的操作回滚,回滚靠的就是undo log;当事务对数据进行修改的时候,会产生undo log,如果事务执行失败或调用了rollback,导致事务回滚,则可以利用undo log的信息将数据回滚到修改之前的样子;
     undo log属于逻辑日志,它记录的是sql执行相关的信息。当发生回滚时,InnoDB会根据undo log的内容做与之前相反的工作:对于每个insert,回滚时会执行delete;对于每个delete,回滚时会执行insert;对于每个update,回滚时会执行一个相反的update,把数据改回去。

三:事务持久性的实现

1.概念
      持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作;即持久性主要关心的问题在于事务提交之后,数据如何落盘,正常情况下,落盘自然没有问题,那么在发生故障的情况下,如何落盘就是重点,比如宕机、磁盘失效等
2.Buffer:Innod buffer 
   Innodb作为Mysql的存储引擎,数据是放在磁盘上的,但因为每次读取都进行磁盘ID,效率很低;为了提高读写的效率,Innodb提供了一块连续的buffer,Buffer中包含了磁盘中部分数据的映射,作为数据库的缓冲,从DB中读取时,会先从Buffer中读取,向DB中写的时候,会先写入Buffer,Buffer中的数据会定期刷新到磁盘中(刷新脏数据);
   引入的问题:Buffer的使用大大提高了读写数据的效率,但是当Mysql宕机,而此时Buffer中修改的数据没有刷新到磁盘的时候,就会导致数据的丢失,事务的持久性没法保证;
3.redo log
   redo log又称为重做日志,用来引入解决这个问题,它记录了事务在数据页上做了哪些修改;当数据修改时,除了修改Buffer中的数据,还会再redo log中记录这次操作,当事务提交时,会调用fsync对redo log进行刷盘;
   当Mysql宕机,重启时就可以读取redo log中的数据,对数据库进行恢复;redo log是采用预写的形式,即所有修改先写入日志,再更新到Buffer,保证数据不会因Mysql宕机而丢失;当然这也不是万无一失的,因为fsync也是通过Buffer进行的,也存在风险;   
      既然redo log也要写入磁盘,不是多此一举吗,其实它比直接将Buffer Pool中修改的数据写入磁盘(即刷脏)要快,主要有以下两方面的原因:
     1.刷脏是随机IO,每次修改的数据位置随机,redo log是追加操作,属于顺序IO;
     2.刷脏是以数据页(Page)为单位的,MySQL默认页大小是16KB,一个Page上一个小修改都要整页写入;而redo log中只包含真正需要写入的部分,无效IO大大减少。
4.磁盘、数据库的数组组织结构
    数据库、OS和磁盘读写的基本单位都是块,也称为block,数据库的块一般为8K、16K,而OS的一般为4K,磁盘IO块更小,Linux内核要求 IO block <= OS block;
    block是磁盘IO的逻辑操作单位,磁盘的物理单位为扇区sector,一般为512字节,一般block对应一个或几个扇区;
   总体上 DB block > OS block >= IO block > 磁盘 sector,且他们是整数倍的关系。
   导致的问题:
   
由于Innodb的page size一般为16KB,所以一般是磁盘sector的几倍,所以可能在极端的情况下,只写入一部分的page,即partial page write部分页写入的问题,
    解决:
     
当Mysql将脏数据flush到data file的时候,先使用memcopy将脏数据复制到内存中的doublewrite buffer,之后通过doublewrite buffer再分2次,每次写入到1MB的共享表空间,然后马上调用fsync函数,刷新到磁盘,避免缓冲带来的问题;doublewrite是顺序写;开销不大。

四:数据恢复 Crash Recovery(持久性的必备条件)

1. binlog
  binlog又称为二进制日志,它记录了所有对表和数据进行修改的语句,如出select、show等查询的DDL和DML语句,以事件的形式记录,比如记录操作的语句;binlog的主要目的就是复制和恢复;复制主要是指主从或者是 集群的复制,留待后面再讲,这里主要讲数据的恢复,尤其是数据库Crash之后的数据恢复;
2.binlog会不会丢数据?
   2.1 参数
sync_binlog
       它表示每sync_binlog个事务,Mysql会调用一次fsync操作将binlog刷新到磁盘中其中0表示完全由OS控制,在Mysql 5.7.7版本之前默认为0,在之后默认的值为1,即每次事务都需要刷新到磁盘中;当sync_binlog=0,或者大于1的时候,这个时候表示牺牲一定的一致性,来获取更好的性能;
   2.2 innodb_flush_log_at_trx_commit
    它也是影响redo log刷新到硬盘的时机的参数:
    为0时: 由mysql的main_thread每秒将存储引擎log buffer中的redo日志写入到log file,并调用文件系统的sync操作,将日志刷新到磁盘。
    为1时:每次事务提交时,将存储引擎log buffer中的redo日志写入到log file,并调用文件系统的sync操作,将日志刷新到磁盘。
    为2时:每次事务提交时,将存储引擎log buffer中的redo日志写入到log file,并由存储引擎的main_thread 每秒将日志刷新到磁盘。

3.binlog的格式
  支持3种格式:Mysql 5.7.7之前,默认是statement,之后为row
     1)statement:基于SQL语句的复制( statement-based replication);优点:只记录SQL语句,简单;缺点是:由于只记录语句,会出现slave和master执行结果不一致的情况出现;
     2)row:基于行的复制( row-based replication),不记录SQL语句,只记录数据被修改后的结果,row的日志会非常清楚记录每一行数据修改的细节;优点:不会出现主备因执行顺序导致的不一致的问题,缺点:会产生大量的日志内容;
     3)mixed:混合模式的复制( mixed-based replication),一般的语句修改使用statement,对弈一些statement无法完成主从复制的操作,则由row来记录,Mysql会根据执行的每一条具体的sql语句,来区分对待记录的日志形式;
4. Crash recovery的过程
   数据库宕机之后恢复,分2种情况,一是redo log已经写入磁盘,但是数据的更新没有刷入磁盘;二是binlog已经记入,但是redo log没有刷盘成功;当然也有两者共同存在的情况,所以恢复动作分为2步:redo log恢复和binlog和undo log合作恢复;
4.1 技术简介
   1. LSN,log sequence number,日志序列号,在redo log中用来记录log随着时间变化的唯一标志;PS:每个数据页也有LSN;
   2.checkpoint技术:在一些日志和文件写入的场景,checkpoint挺常见的,比如kafka的日志中也有,该技术是为了解决几个问题:1.隔段时间,批量写入,并标志进度,缩短数据恢复时间,也加快写入速度;2.缓冲池不够大时,将脏页刷回磁盘;3.redo log不可用时,刷新脏页;
  3.redo恢复机制:因为checkpoint的存在,所以当数据库需要恢复时,checkpoint之前的已经写入了,所以只需恢复checkpoint之后的数据;
  4.redo log和binlog的一致性的保证:以update举例
     1)Mysql server 收到update请求,并转发 innodb存储引擎;
     2)innodb update操作成功,redo log 写盘,Innodb事务进入 prepare状态;
     3)binlog写盘,Innodb事务进入commit状态,并写入redo log 的commit日志;
  5.利用binlog和undo log进行恢复
     binlog和redo log是一个两段式提交的协议;但是理论上,在忽然宕机的情况下,还是会出现binlog写入,但是undo log没有写入成功的场景;这种场景就需要利用binlog来恢复redo log,然后利用redo log去恢复;
4.2 redo log恢复
    
恢复时,Innodb会通过redo log找到最近的checkpoint的位置,然后根据checkpoint的LSN找到需要重做的日志;
   因为redo log是事务对数据页做的修改,所以引擎会解析相应的redo log加载到内存中,读取相关的页进行恢复;恢复之后,在redo log和磁盘不一致的数据得到了恢复;4.3 binlog恢复
   当binlog已经成功写入,但是redo log未写入,导致的不一致的问题(可能是主备不一致,至少是事务的修改没有落盘),需要binlog和undo log合作来恢复;步骤为:
   1.根据binlog,获取到所有可能没有提交事务的xid列表;
   2.根据undo log中的信息,构造所有未提交事务的链表,;
   3.最后判断事务是否可以提交,判断依据是:
     凡是xid在通过binlog发构建的xid列表中存在的事务,都需要被提交,这些事务是在提交时,被写入binlog的;
    那些剩下的,需要被回滚,即这个事务没有提交成功,最后也没有写成binlog;
5.总结
   事务的一致性涉及的点很多,包括主备、集群、Mysql server和存储引擎,存储引擎和磁盘、redo log和binlog等等;这里仅从单机的角度,描述了单机Mysql的一致性,即在异常状态下数据的恢复,从而保证数据的一致性;

五:事务一致性的实现

1. 概念
     一致性是指事务执行结束后,数据的完整性约束没有被破坏,事务执行前后都是合法的数据状态;数据库的完整性约束包括但不限于:实体完整性(主键唯一)、列完整性(字段类型大小合法)、外键约束、用户自定义(如转账前后,两账户的余额总和不变);原子性、持久性和隔离性,都是为了保证数据库状态的一致性;
2.种类
     从逻辑层的角度来划分,数据一致性包括应用层面的一致性、主备数据库的一致性,还有单机mysql内部的数据一致性,这里主要讨论的是单机mysql内部的数据一致性;
     从一致性的强弱,又分为强一致性、弱一致性、最终一致性;

六:事务隔离性的实现

   上篇在介绍Innodb的锁的时候,介绍了事务的隔离性,隔离性关注的是不同的事务之间的相互影响,即事务内部的操作个其他事务是隔离的,并发执行的各个事务之间不能互相干扰;
    其中锁机制保证隔离性,MVCC保证读操作的隔离性;其中要再说明的是MVCC和间隙锁解决了幻读问题,但是并没有实现Serializable;

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,589评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,615评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,933评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,976评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,999评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,775评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,474评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,359评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,854评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,007评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,146评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,826评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,484评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,029评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,153评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,420评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,107评论 2 356