前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >猿思考系列6——事务也就那么回事儿

猿思考系列6——事务也就那么回事儿

作者头像
山旮旯的胖子
发布2020-07-28 16:51:50
2570
发布2020-07-28 16:51:50
举报
文章被收录于专栏:猿人工厂猿人工厂

看完上一个章节,相信你已经充分的掌握代理的套路,猿人工厂君也知道,内容对于新手而言,理解起来还是比较很吃力的,文中提到的其他方式,大家可以去尝试实现,猿人工厂君就不一一赘述了。今天我们将开启一个新的方向,让大家思考一些新的问题。不过上一章节涉及编译原理、类加载机制和一点点jvm的知识,很重要,请务必掌握其中的过程和概念。

数据库事务:一般来讲是指数据库访问逻辑中一个最小的不可再分的工作单元;是一组对数据库的相对完整的逻辑单元操作。一个数据库事务,可以是一条SQL语句,也可以是一组SQL语句。

我们来看一个例子,比如,账户A向账户B转账100元,那么账户A必须减少100元,而账户B必须增加100元。这个操作是不可能分开的,是一个原子操作。账户A必须减少100元而账户B必须增加100元,不能出现A减少100元,B没有增加100元,也不能出现A没有减少100元,B增加了100元。操作必须是同时成功的或者同时失败的,必须是一致的。A和B的操作,是不能受到其他事务的干扰的从而影响结果的正确执行,并发的事务之间是需要隔离起来的,而且这个操作进行之后,A账户,和B账户的操作结果不可改变,必须把操作数据记录到磁盘,这是一个持久状态。

以上就是数据库事务必须具备的四大特性。原子性(atomicity)、一致性(consistency)、隔离性(isolation)、持久性(durability),俗称ACID四大特性或四大原则。

关于MYSQL事务的操作有几个语句你也要了解一下:

开启事务:starttransaction;(开启显示提交事务)

提交事务:commit;(提交事务,执行后,数据固化到磁盘,不可改变)

回滚事务:rollback;(回滚事务,撤销之前的DML语句操作)

在很多人的观念里,提到数据事务就是ACID四大原则,但是这四大原则是由谁来保障的呢?答案当然是数据库了。比如MYSQL数据库,你用MYISAM引擎的表搞搞事务试试?所以,要做数据库事务得有一个大前提,数据库必须是支持事务的。

如果使用的是INNODB引擎的表,默认情况下是开启了事务自动提交的,在不使用 start transation语句的情况下,任何一个insert update语句都是自动提交的,另外,insert update语句都是一个原子级操作噢,总不能写一条数据,或者修改一条数据还存在,只修改一半的情况吧?这一点先了解下,以后的学习中自由妙用。

额,这个问题看上去有点难,我们先看看原子性——要么执行,要么全部不执行。操作多条语句,思考起来太复杂了,来简单点而的。比如我们要修改用户ID为1的电话好嘛为13888888888:

update travel_user set travel_user_phone=’13888888888’where travel_user_id=1;

我们先考虑执行失败怎么办?执行失败了,自然需要保证数据恢复原样。那么要干的第一件事情,自然是记录原始数据了,有了原始记录,在之后的修改过程中,发生了任何问题,都可以恢复原样。那么第二件事情,自然是修改记录了,如果修改失败了,根据原始记录恢复数据就好了。第三件事情,是将修改的记录写到磁盘了,如果失败,有原始记录,恢复就好,如果成功,操作就完成了,事务也成功完成了。

想想看,这个原始记录需要记录在哪里?白纸黑字的东西才会让人放心,自然是文件了。Mysql提供了一个叫redo log的家伙来做这个事情。

MYSQL当然不会这么傻啦,肯定先写到内存嘛,内存操作就快了,内存写差不多了,单独起一个线程来异步把内存里的数据往磁盘上写就好了。当然,数据在内存中放时间长了,万一机房断电,数据就没有了,这个风险嘛,交给用户去配置,让他自己决定多长时间刷新到磁盘持久就好了。当然,正向操作要记录,反向操作的也要记录啦,比如记录了一条insert,必然记录一条delete用于数据恢复,就叫undo log好了。

当然考虑到其他问题,比如集群下的数据同步,MYSQL还提供了一个叫binlog的东西用于记录数据的逻辑操作——执行的SQL语句。这样你修改主节点的数据,只需要主节点把binlog的语句发到其他节点执行就好了噢。

那么新的问题又来了,redo log 的内容必须是和binlog是一致的,要不就乱套了噢。怎么来解决呢?

这个好办,先写redo log,如果写失败了,那我们就认为这次事务有问题,回滚,不再写binlog。如果redo log写成功了,写binlog写一半失败了,事务回滚嘛,然后删除无效的binlog就好了(不删除,被同步就麻烦了)。如果写redo log和binlog都成功了,事务完成,加个标记表示成功嘛。这个过程还有个学名——“二阶段提交”。

prepare :redo log刷新到磁盘,事务进入 prepare 状态。

commit::binlog刷新到磁盘,事务进入 commit 状态,并打上log_xid标记。

在讨论这个话题之前,我们来看几个问题,假如有两个事务并发执行可能发生什么情况呢?一个事务把另一个事务的数据覆盖了,这个叫丢失更新问题。一个事务A读取了一条d1,一个事务B写入一个数据,但是未提交,事务A再次查询,读到未提交的数据,这叫“脏读”。一个事务A读取了一条d1,一个事务B修改了d1,并提交事务,事务A再次读取d1,发现两条记录不一致,这就是虚读。一个事务A做了一次查询,事务B写入了数据d2,并提交事务,事务A再做了一次查询(语句可以不同),发现查询结果包含了d2,这就叫幻读。虚读和幻读都叫“不可重复读”。

为了解决这些问题数据库定义了四种隔离级别:

读未提交:read uncommitted,事务A未提交的数据事务B也能读取到,允许脏读,但是不允许发生丢失更新的问题。

读已提交:read committed,事务A已提交的数据事务B才能读取到,不允许脏读,允许发生不可重复读。

可重复读:repeatable read,事务A提交之后的数据,事务B读取不到,但是,事务B可重复读,允许幻读,但不允许虚读,即同一条记录的情况。MYSQL的默认级别就是repeatable read。

串行化:serializable,事务排队执行,一个事务执行完才能执行下一个,事务排队执行,性能可想而知,几乎无用武之地。

那MYSQL是怎么来保证事务的隔离级别的呢?

其实MYSQL在设计上比较鸡贼啦,每次开启事务都会给事务编个号,然后每一行记录都包含了三个隐藏列,DATA_TRX_ID记录最后一个事务的ID,DB_ROLL_PTR记录当前记录的redo log 信息,DB_ROW_ID用于标记新插入数据的行ID。

这下好了增删改查操作按下面的来就行了:

select:

读取创建版本小于或等于当前事务版本号,并且删除版本为空或大于当前事务版本的记录。用于保证在读取之前记录都是存在的。

insert:

将当前事务的版本号保存至行的DATA_TRX_ID

update:

新插入一行,并以当前事务版本号作为DATA_TRX_ID,同时将原记录行的DATA_TRX_ID设置为当前事务版本号

delete:

将当前事务版本号保存至行的DATA_TRX_ID。

MYSQL会把已开始未提交的事务保存在一个叫做trx_sys的事务链表中。同时还搞了一个叫做ReadView的东西。ReadView包含了几个很重要的信息。ReadView{low_trx_id,up_trx_id, trx_ids},low_trx_id表示务链表中最大的事务id编号,up_trx_id表事务链表中最小的事务id编号,trx_ids表示所有事务链表中事务的id集合。ReadView在什么时候创建呢?这个就和隔离级别有关了。如果隔离级别是READ COMMITTED,那么每次读取数据前都生成一个ReadView,如果隔离级别是REPEATABLE READ,那么在第一次读取数据时生成一个ReadView。

那么判断事务是否可见这个问题就好办了:

所有数据行上DATA_TRX_ID小于up_trx_id的记录,说明修改该行的事务在当前事务开启之前都已经提交完成,所以对当前事务来说,都是可见的。而对于DATA_TRX_ID大于low_trx_id的记录,说明修改该行记录的事务在当前事务之后,所以对于当前事务来说是不可见的。

至于位于(up_trx_id, low_trx_id)中间的事务是否可见,这个需要根据不同的事务隔离级别来确定。对于READ COMMITTED的事务隔离级别来说,对于事务执行过程中,已经提交的事务的数据,对当前事务是可见的;而对于REPEATABLE READ隔离级别来说,事务启动时,已经开始的事务链表中的事务的所有修改都是不可见的,所以在REPEATABLE READ级别下,low_trx_id基本保持与up_trx_id相同的值即可。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-04-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 猿人工厂 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 SQL Server
腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档