[.com原稿]关于学习这件事,宁愿花点时间系统的学习,也不要东拉西扯敲锤子。他们都说最好的学习方法是系统的学习。希望阅读本文后,您对MySQL有一定的了解。数据库版本为8.0。图片来自Pexels什么是事物事物是一个独立的工作单元,其中所有操作要么全部成功,要么全部失败。也就是说,如果有一条语句因为崩溃或其他原因执行失败,则不会再次执行未执行的语句,回滚已执行的语句。这个过程称为交易。例子:最近在写一个论坛系统。当发布的话题被其他用户举报时,后台会对举报内容进行审核。一旦审核为非法话题,就会进行删除话题的操作,但不仅要删除话题,还要删除话题下的帖子和浏览量。所有关于这个主题的信息都需要清理。删除过程如下。利用上面的概念,下面四个过程都必须成功,否则事务回滚返回删除失败。假设执行完第三步后SQL执行失败,则回滚第一步和第二步,不执行第四步。事物的四大特性事物的四大特性:原子性ConsistencyIsolationPersistence①原子性事物中的所有操作要么全部成功要么全部失败,不会有部分成功部分失败的情况。这个概念也是事物的核心特征,事物的概念本身就是使用原子性来定义的。原子性的实现是基于回滚日志(undolog)。当事物需要回滚时,会调用回滚日志进行SQL语句回滚操作,实现数据恢复。②ConsistencyConsistency,字面意思就是一致性!无论在数据库中执行什么操作,都是从一种一致性转移到另一种一致性。当事务结束时,没有违反数据库的完整性约束。当你了解了事物的四大特性后,你会发现其最终目的是为了保证数据的一致性。在学习事物的过程中,大家看到最多的案例就是迁移。假设用户A和用户B的余额合计为1000,那么无论怎么转账,两人的余额自始至终都只有1000。③隔离保证事物的执行尽可能不受其他事物的影响。这是可以自己设置的隔离级别。innodb中默认的隔离级别是可重复读(RepeatableRead)。这种隔离级别可能造成的问题就是幻读,但是使用间隙锁可以解决幻读问题。学习了隔离之后,要知道原子性和持久化是针对单个事物的,而隔离是针对事物之间的关系的。④持久性持久性是指一个事物提交后,数据的状态是永久的,不会因为系统崩溃而丢失。事务持久化是基于重做日志实现的。事物的并发会出现的问题①脏读读取的数据还没有被另一个事物提交过。以上表为例,事务A读取topicvisits时,读取了事务B未提交的150条数据。如果事务B失败回滚,修改后的值还是会回到100。但是事物A获取的数据是修改后的数据,这是有问题的。②不能重复读取相同的数据,返回的结果不一致。上表中,事物A先后获取主题访问次数时,返回的数据不一致。也就是说,在事物A执行过程中,如果访问量被其他事物修改,那么事物A的查询结果就是不可靠的。脏读和不可重复读的区别:脏读读取的是另一个事务没有提交的数据,而不可重复读读取的是另一个事务已经提交的数据。③幻读东西是根据范围查询的,两次返回的结果不同。以上表为例。在统计访问量在100-200的topic时,第一次找到100个,第二次找到101个。④脏读读的是另一个事务没有提交的数据,而不可重复读读的是另一个事务已经提交的数据。幻读和不可重复读都是读取另一个已经提交的事务(这与脏读不同)。数据作为一个整体(例如数据的数量)。针对以上三个问题,产生了四个隔离级别。第二节讲解了一个简单的隔离概念,但实际隔离非常复杂。MySQL中定义了四种隔离级别,分别是:ReadUncommitted:两个东西同时运行,一个东西修改了数据,但是没有提交,另一个东西可以读取未提交的数据。这种情况称为脏读。承诺读(Readcommitted):在一个事物被提交之前,其他事物所做的任何操作都是不可见的。此隔离级别也称为不可重复读取。因为会有两次相同的查询,返回的数据可能会得到不同的结果。RepeatableRead:这个隔离级别解决了脏读的问题,但是仍然存在幻读。这个隔离级别是MySQL的innodb引擎中的默认级别。MySQL使用间隙锁来解决幻读问题。Serializable:这个级别最高,强制事情串行执行,解决了可重复读的幻读问题。对于隔离级别,级别越高,并发越低,级别越低,会造成脏读、不可重复读、幻读。因此,可重复读(RepeatableRead)作为MySQL中的默认级别。默认级别如何解决和处理相应的问题?所以这个问题是个棘手的问题,我会在下一篇MVCC文章中介绍。事务日志以及如何处理事务异常Innodb中有两种事务日志,回滚日志和重做日志。先来看看这两个日志的存放位置吧!MySQL的版本号是8.0。Linux下的MySQL事务日志存放在/var/lib/mysql中:从上图可以看到ib_logfile和undo_两个文件:ib_logfile是重做日志,undo_是回滚日志。估计到这里小伙伴会对这个回滚日志有点疑惑。那是因为MySQL5.6默认的回滚日志并没有保存在独立的表空间中,而是保存在ibdata文件中。MySQL5.6开始支持独立表空间存储,但需要自行配置。在MySQL8.0中,回滚日志的独立空间个数由参数innodb_undo_tablespaces设置,该参数的范围为0-128。默认值为0,表示不开启独立回滚日志,回滚日志存放在ibdata文件中。该参数在数据库初始化时指定,实例创建后不可更改。如果设置的innodb_undo_tablespaces值大于创建的实例数,会启动失败。①重做日志(redolog)(持久化实现原理)事物的持久化是通过重做日志实现的。事务提交后,并没有直接修改数据库中的数据,而是先保证相关操作记录在redolog中。数据库会根据相应的机制将内存中的脏页数据刷新到磁盘中。上图是一个简单的重做日志写入过程。上图中提到了两个比较陌生的概念,Bufferpool和redologbuffer,它们都是Innodb存储引擎内存区域的一部分。重做日志文件位于磁盘上。也就是说,当有DML(插入、更新、删除)操作时,数据会先写入Bufferpool,然后再写入redologbuffer。redologbuffer会根据flush机制写入redolog。该机制的设置参数为innodb_flush_log_at_trx_commit,参数分别为0、1、2。上图为redolog的写入策略:当该参数的值为0时,事务提交后,数据会存放在redologbuffer中,然后每隔一段时间将数据写入磁盘文件第二。当该参数的值为1时,事务提交后,redologbuffer必须从内存中刷新到磁盘文件中。只要事务提交成功,重做日志就一定在磁盘上。当该参数值为2时,事务提交后,将redologbuffer日志写入磁盘文件对应的oscachecache中,而不是直接写入磁盘文件,oscache中的数据会在之后写入1秒入盘文件。②服务器异常停止时如何处理事物(写事物过程)写事物过程如下:当参数为0时,前一秒的日志保存在logbuffer中,也就是内存中。如果机器死机,可能会丢失1秒的交易数据。当参数为1时,数据库对IO的要求非常高。如果底层硬件提供的IOPS比较差,MySQL数据库的并发很快就会因为硬件IO问题而无法提升。当参数为2时,数据直接写入os缓存,属于操作系统。如果操作系统部分损坏或断电,1秒内的交易数据将丢失。将此策略与第一个策略进行比较。安全多了,对IO的要求也没有那么高。总结:关于性能:0>2>1关于安全:1>2>0根据以上结论,在MySQL数据库中,flushingpolicy的默认值是1,保证事务后数据永不丢失已提交。③回滚日志(undolog)(原子实现原理)undolog保证了事物的原子性。回滚日志没有重做日志复杂。当事物修改数据库时,Innodb引擎不仅会记录redolog日志,还会记录undolog日志。如果事情失败了,或者执行了回滚,为了保证事情的原子性,回滚操作必须使用undolog日志。回滚日志的存储形式如下:在undolog日志文件中,事务中使用的每一次insert对应一次delete,每一次update也对应一条相反的update语句。注意:系统崩溃或数据库进程被直接杀死。当用户再次启动数据库进程时,可以通过查询回滚日志立即回滚未完成的事务过程。这也要求回滚日志必须先于数据持久化到磁盘,这也是先写日志后数据库的主要原因。回滚日志不仅可以保证事物的原子性,也是实现mvcc的重要因素。以上就是对关于事物的两大日志,redologs和rollbacklogs的理解。锁机制锁是MySQL非常重要的一部分,锁对MySQL数据访问的并发性起着决定性的作用。因此,锁的内容和细节非常繁琐。本节只是对Innodb锁的一般安排。MySQL中有三种锁,分别是行锁、表锁和页锁。首先需要明确这三种锁属于哪个存储引擎:行锁:Innodb存储引擎表锁:Myisam,MEMORY存储引擎页锁:BDB存储引擎①行锁行锁分为共享锁,独占锁,也称为读锁和写锁,是Innodb存储引擎的默认锁。共享锁(S):假设一个东西给数据A加了共享锁(S),那么这个东西只能读A的数据,其他东西只能给数据A加共享锁(S),不能加排他锁(X),直到这个东西释放数据A的共享锁(S)。这样可以保证其他东西也可以读取A的数据,但是在这个东西释放共享锁(S)之前不能对A进行任何修改A的资料。独占锁(X):假设一个事物给数据A加了一个独占锁(X),只有这个事物被允许读取和修改数据A。在这个事物释放之前,其他任何事物都不能对数据A加任何类型的锁锁定数据A.独占锁阻止其他事物获取同一数据上的共享锁(S)、独占锁(X),直到独占锁(X)被释放。特点如下:只对单个数据加锁,代价高,速度慢,导致死锁锁粒度最小,锁冲突概率越低,并发度越高。还记得上面说的东西的并发带来的问题吗,脏读,不可重复读,幻读。学习到这里,你应该明白可重复读(RepeatableRead)是如何解决脏读和不可重读的。解决脏读和不可重复读的方法很简单。写入前加排它锁(X),事务结束后释放。读前加共享锁(S),事务结束后释放。②表锁表锁分为表共享读锁和表排他写锁,也称为读锁和写锁,是Myisam存储引擎的默认锁。表共享读锁:对于同一条数据,可以同时读,互不影响,但不允许写操作。表独占写锁:当写操作没有完成时,所有的读写都会被阻塞。特点如下:锁整表代价小。加锁速度很快,不会出现死锁。锁粒度最大。锁冲突的概率越大,并发越小。.③如何加表锁:隐式加锁:默认是自动加锁和释放锁,select加读锁,update,insert,delete加写锁。手动加锁:locktabletableNameread;(加读锁),locktabletableName写(加写锁)。手动解锁:unlocktabletableName(释放单个表),unlocktable(释放所有表)。行锁:隐式加锁:默认是自动加锁和释放锁,只有select不会加锁,update、insert、delete会加排他锁。手动添加共享锁:在共享模式下从用户锁中选择id名称。手动添加独占锁:选择idnameformuserforupdate。Unlock:正常的commit、rollback、kill进程。小结本文主要对事物的关键知识点进行解读,并对内容进行总结。事物四大特性的实现原理:原子性:使用事务日志的undolog实现隔离:使用mvcc实现(幻读问题除外)持久性:使用事务日志的重做日志(redolog)来实现达到一致性:是事物追求的最终目标。原子性、隔离性、持久化都是为了保证数据库的一致性。并发问题的区别:脏读和不可重复读的区别:脏读是没有提交的读,不可重复读的数据是提交事务的数据。幻读和不可重复读的区别:都是读取提交的事务数据(区别于脏读),幻读针对的是一批数据,比如数字。不可重复读取是针对单条数据的。事务日志:重做日志(redolog):实现事务的持久化。它不是在提交事务后直接修改数据库,而是确保每个事务操作都被读取并写入重做日志。下单策略有3种(详见4-1节)。回滚日志(undolog):实现事物的原子性,对于DML操作,会有相反DML操作的记录。作者:卡卡编辑:陶嘉龙投稿:如有意投稿或求报道,请加编辑微信gordonlonglong【原创稿件,合作站转载请注明原作者和出处.com】
