mysql查看隔离级别 查看mysql数据库隔离级别
mysql主库更新后,从库都读到值了,主库还有可能读到旧值吗
min_trx_id:活跃事务中的最小事务id今天的文章,其实来自真实的面试题,而且还比较有趣,所以忍不住分享出来。
mysql查看隔离级别 查看mysql数据库隔离级别
mysql查看隔离级别 查看mysql数据库隔离级别
直接开始吧。
我们知道,mysql数据库,为了得到更高性能,一般会读写分离,主库用于写作,比如用于执行insert,update作,从库用于读,也就是最常见的select作。像下面这个图这样。
虽然主库一般用于写作,但也是能读的。那么今天的问题来了。
主库更新后,主库都读到值了,从库还有可能读到旧值吗?主库更新后,从库都读到值了,主库还有可能读到旧值吗?
毕竟面试官都这么问了,那当然是有可能的,那至于是为啥,以及怎么做到的,今天我们来好好聊聊。
正常的主从更新流程
比如我在主库和从库都有张user表,此时有以下两条数据。
正常情况下,我们往主库执行写作,比如更新一条数据,执行
updateusersetage=50whereid=1;代码虽然这是一个单条写作,但本质上可以理解为单条语句的事务。等同于下面这样
begin;updateusersetage=50whereid=1;commit;代码这个事务如果执行成功了,数据会先写入到主库的binlog文件中,然后再刷入磁盘。
binlog文件是mysql的server层日志,记录了用户对数据库有哪些变更作,比如建数据库表加字段,对某些行的增删改等。
它的位置可以通过下面的查询语句看到。
mysqlshowvariableslike%log_bin%;-----------------------------------------------------------------------|Variable_name|Value|-----------------------------------------------------------------------|log_bin|ON||log_bin_basename|/var/lib/mysql/mysql-sle-bin||log_bin_index|/var/lib/mysql/mysql-sle-bin.index||log_bin_trust_function_creators|OFF||log_bin_use_v1_row_nts|OFF||sql_log_bin|ON|-----------------------------------------------------------------------6rowsinset(0.04sec)代码其中binlog在/var/lib/mysql/下,命名会类似mysql-bin.00000x。感兴趣的可以到这个目录下直接查看文件内容长什么样子。
如果两个mysql配置好了主从的关系,那么他们之间会建立一个tcp长连接,主要用于传输同步数据。
除此之外,主库还会再起一个binlogdump线程将binlog文件的变更发给从库。
可以在主库中通过showfullprocesslist;查询到binlogdump线程的存在。
以上,主库的工作就结束了,我们说说从库的。
从库在收到binlog后,会有一个io线程负责把收到的数据写入到relaylog(中继日志)中。
然后再有一个sql线程,来读取relaylog的内容,然后对从库执行sql语句作,从结果上来看就是将主库执行过的写作,在从库上也重放一遍,这样主从数据就一致了。
是不是感觉relaylog有些多余?
为什么要先写一遍relaylog然后再写从库,直接将数据写入到从库不好吗?
在这里relaylog的作用就类似于一个中间层,主库是多线程并发写的,从库的sql线程是单线程串行执行的,所以这两边的生产和消费速度肯定不同。当主库发的binlog消息过多时,从库的relaylog可以起到暂存主库数据的作用,接着从库的sql线程再慢慢消费这些relaylog数据,这样既不会限制主库发消息的速度,也不会给从库造成过大压力。
可以通过在从库中执行showfullprocesslist;确认io线程和sql线程的存在。
因此总结起来,主从同步的步骤就是
1.执行更新sql语句。
2.主库写成功时,binlog会更新。
3.主库binlogdump线程将binlog的更新部分发给从库
4.从库io线程收到binlog更新部分,然后写入到relaylog中
5.从库sql线程读取relaylog内容,重放执行sql,主从一致。
到这里,我们可以开始回答文章开头的个问题。
主库更新后,主库都读到值了,从库还有可能读到旧值吗?
这是可能的,上面提到的主从同步的5个步骤里,第3到第5步骤,都需要时间去执行,而这些步骤的执行时间总和,就是我们常说的主从延迟。
当更新一行数据后,立马去读主库,主库的数据肯定是值,这点没什么好说的,但如果此时主从延迟过大,这时候读从库,同步可能还没完成,因此读到的就是旧值。
在实际的开发当中,主从延迟也非常常见,当数据库压力稍微大点,主从延迟就能到100ms甚至1s以上。
具体的主从延迟时间可以在从库中执行showslestatusG;来查看,其中里面的Seconds_Behind_Master则是主从延迟的时间,单位是秒。
mysqlshowslestatusG;1.rowSle_IO_State:WaitingfortosendntMaster_Host:172.17.0.2Master_User:sleConnect_Retry:30Master_Log_File:mysql-bin.000002Read_Master_Log_Pos:756Relay_Log_File:edu-mysql-relay-bin.000004Relay_Log_Pos:969Relay_Master_Log_File:mysql-bin.000002Sle_IO_Running:YesSle_SQL_Running:YesSeconds_Behind_Master:2代码所以如果你有写数据后就立马要读数据的场景,要是此时读的是从库,很有可能会读到更新前的旧数据,如果你对数据一致性有较高要求,这种时候建议读主库。
主库更新后,从库都读到值了,主库还有可能读到旧值吗?
那另一个问题就来了,如果从库都读到值了,那说明主库肯定已经更新完成了,那此时读主库是不是只能读到值呢?
还真不是的,待会我给大家复现下,但在这之前我们了解一些前置知识点。
mysql的四种隔离级别
这个是面试八股文老股了。mysql有四种隔离级别,分别是读未提交(Readuncommitted),读提交(Readcommitted),可重复读(Repeatableread)和串行化(Serializable)。在不同的隔离级别下,并发读写效果会不太一样。
mysqlselect@@tx_isolation;-----------------|@@tx_isolation|-----------------|REPEATABLE-READ|-----------------1rowinset(0.01sec)代码也可以通过下面的语句去修改隔离级别。
SETGLOBALTRANSACTIONISOLATIONLEVELREPEATABLE-READ;代码下面用一个case来让大家直观点地理解这四个隔离级别的区别。
设我们有两个线程同时对某行数据A(A=1)进行以下作。
我们执行事务都像上面这样,begin可以开启事务,commit会提交事务,上面两个线程,各执行一个事务,且此时是并发执行。
线程1会将某行的A这个字段从1更新为2。
线程2啥也不干,就读A。重点关注2线程的三次读A的行为,它们会根据隔离级别的不同,读到不同的值。
第1次查询所有和 101 、108 号学生同年出生的 no 、name 、birthday 列。读A:
如果是读未提交,那么会读到2,顾名思义,就算线程1未提交,线程2也能读到的值。如果是读提交或者可重复读,那读到的都是1,读提交只认事务提交后的数据,而可重复读只要线程2的事务内没有执行对A的更新sql语句,那读A的数据就会一直不变。
第2次读A:时机正好在线程1提交了事务之后
如果是读未提交,前面都读到2了,现在读到的还是2,这个没啥好说的。如果是读提交,那读到的都是2了,因为线程1的事务提交了,读提交只认提交后的数据,所以此时线程2能读到数据。如果是可重复读那就还是1,理由跟上面一样。
第3次读A:时机正好在线程2提交了事务之后
如果是读未提交或读已经提交,结果跟前面一样,还是2。如果是可重复读,那就变成了2,因为线程2前面的事务结束了,在同一个事务内A的值重复多次读都是一致的,但当事务结束了之后,新的查询不再需要受限于上一次开事务时的值。
上面的情况没有将串行化纳入讨论范围,只讨论了读未提交,读提交和可重复读这三个隔离级别,因为在这三个隔离级别下都有可能出现两个事务并发执行的场景,而在串行化的隔离级别中则不会出现,多个事务只会一个挨着一个依次串行执行,比如线程1的事务执行完了之后,线程2的事务才执行,因此不会产生并发查询更新的问题。
有了这个知识背景之后,我们就可以回到第二个问题里了。
数据库原始状态如下,此时主从都一样。
设当前的数据库事务隔离级别是可重复读,现在主库有A,B两个线程,同时执行begin,开启事务。
此时主库的线程2,先读一次id=1的数据,发现age=72,由于当前事务隔离级别是可重复读,那么只要线程2在事务内不做更新作的话,那么不管重复读多少次,age都是72。在这之后主库的线程1将age更新为100且执行commit提交了事务。
主库线程1的事务提交成功之后binlog就会顺利产生,然后同步给从库。此时从库去查询就能查到值age=100。回过头来,此时主库的线程2因为还没提交事务,所以一直读到的都是旧值age=72。但如果这时候线程2执行commit提交了事务,那么再查询,就能拿到值age=100了。
所以从结论上来说,出现了从库都读到值了,主库却读到了旧值的情况。
好了这道题到这里就结束了。
意不意外?
这道面试题,通过一个问题,将主从同步,事务隔离级别等知识点都串起来了。
还是有点意思的。
如果文章对你有帮助,欢迎.....
算了。别说了,一起在知识的海洋里呛水吧
如何理解数据库事务隔离级别及读、不可重复读、幻读
我们都知道用事务是为了保证数据库的完整性,保证成批的 SQL 语句要么全部执行,要么全部不执行。本篇文章给大家带来的内容是关于如何理解数据库事务隔离级别及读、不可重复读、幻读,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。
一、数据库事务正确执行的四个基本要素1.1ACID原则。
??ACID原则是数据库事务正常执行的四个基本要素,分别指原子性、一致性、性及持久性。
??事务的一致性(Consistency)是指事务的运行并不改变数据库中数据的一致性。例如,完整性约束了a+b=10,一个事务改变了a,那么b也应该随之改变。或者说,A给B转账300元钱,那么A的账户就必须是减少300元钱,B的账户就必须是增加300元钱,不能说是增加或减少了如200元钱等,这里符合事务的原子性,但是不符合事务的一致性。往实际业务中没有这么简单,往是类似买东西扣库存这类的逻辑,主表里有库存,库存表里有库存,SKU表里还有,然后就因为设计缺陷,就算加了事务还是出现了超卖、SKU库存对不上总库存的问题,这个就是一致性不满足的了。
??性(Isolation):事务的性也有称作隔离性,是指两个以上的事务不会出现交错执行的状态,因为这样可能会导致数据不一致。
??持久性(Durability):一旦事务提交或者回滚,这个状态都要持久化到数据库中,不考虑隔离性会出现的读问题。
1.2读、不可重复读,幻读。
??读(Dirty read):在一个事务中读取到另一个事务没有提交的数据。例如,当一个事务正在访问数据,并且对select @@tx_isolation;数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。
??不可重复读(NonRepeatable Read):既不能读到相同的数据内容。是指在一个事务内,多次读同一数据,在这个事务还没有结束时,另外一个事务也访问该同一数据并且修改,那么,在个事务中的两次读数据之间,由于第二个事务的修改,个事务两次读到的的数据可能是不一样的。
??幻读(Phantom Read):在一个事务中,两次查询的结果不一致(针对的insert作) 。是指当事务不是执行时发生的一种现象,例如个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后作个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。
二、数据库事务隔离级别??数据库事务的隔离级别有4个,由低到高依次为Read uncommitted(读未提交)、Read committed(读提交) 、Repeatable read(可重复读)、Serializable(序列化),这四个级别可以逐个解决读 、不可重复读 、幻读这几类问题。
??公司发工资了,把5000元打到singo的账号上,但是该事务并未提交,而singo正好去查看账户,发现工资到账5000元整,非常高兴。可是不幸的是,发现发给singo的工资金应该是2000元,于是迅速回滚了事务(将5000元回滚),修改金额后(修改为2000元),将事务提交,singo实际的工资只有 2000元,singo空欢喜一场。
??出现上述情况,即我们所说的读 ,两个并发的事务,“事务A:给singo发工资”,“事务B:singo查询工资账户”,事务B读取了事务A尚未提交的数据。
??当隔离级别设置为Read uncommitted(读未提交)时,就可能出现读,如果我们此时将隔离级别提升为Read committed(读已提交),便可避免读。
2.2 Read committed(读已提交)
??singo拿着工资卡去消费,系统读取到卡里确实有2000元,而此时她的老婆也正好在网上转账,把singo工资卡的2000元转到另一账户,并在 singo之前提交了事务,当singo扣款时,系统检查到singo的工资卡已经没有钱,扣款失败,singo十分纳闷,明明卡里有钱,到底是啥情况呢?
??出现上述情况,即我们所说的不可重复读 ,两个并发的事务,“事务A:singo消费”、“事务B:singo的老婆网上转账”,事务A事先读取了数据,事务B紧接了更新了数据,并提交了事务,而事务A再次读取该数据时,数据已经发生了改变。
??当隔离级别设置为Read committed(读已提交)时,避免了读,但是可能会造成不可重复读(既不能读到相同的数据内容)。
??大多数数据库的默认级别就是Read committed(读已提交),比如Sql , Oracle,此时如果将隔离级别提升为Repeatable read(可重复读),可以避免读和不可重复读的发生。
??当隔离级别设置为Repeatable read(可重复读)时,可以避免不可重复读。当singo拿着工资卡去消费时,一旦系统开始读取工资卡信息(即事务开始),singo的老婆就不可能对该记录进行修改,也就是singo的老婆不能在此时转账。
??(这里两个博客举得例子不一样,请各位看官指明原因)或者说,有A、B两个会话,分别开启两个事务,然后A向B转了500元钱,A 提交事务,B再去查看,发现依旧是原钱数,B只能结束当前事务,在开启一个新事务,才能查询到数据的变化,这样便避免了不可重复读。如果我们设置了Seriizable(序列化),就相当于锁表,某一时间内只允许一个事务访问该表。
??虽然Repeatable read避免了不可重复读,但还有可能出现幻读 。
??比如singo的老婆工作在银行部门,她时常通过银行内部系统查看singo的消费记录。有一天,她正在查询到singo当月的总消费金额 (select sum(amount) from transaction where month = 本月)为80元,而singo此时正好在外面胡吃海塞后在收银台买单,消费1000元,即新增了一条1000元的消费记录(insert transaction ? ),并提交了事务,随后singo的老婆将singo当月消费的明细打印到A4纸上,却发现消费总额为1080元,singo的老婆很诧异,以为出 现了幻觉,幻读就这样产生了。
??注:Mysql的默认隔离级别就是Repeatable read。
2.4 Serializable(序列化)
??Serializable(序列化)是的事务隔离级别,同时代价也花费,性能很低,一般很少使用,在该级别下,事务顺序执行,不仅可以避免读、不可重复读,还避免了幻读。
三、总结3.1 隔离级别与对应可能产生的问题表
隔离级别读(Dirty read)不可重复读(NonRepeatable Read)幻读(Phantom Read)读未提交(Read uncommitted)可能可能可能读已提交(Read committed)不可能可能可能可重复读(Repeatable read)不可能不可能可能序列化(Serializable)不可能不可能不可能
mysql:事务隔离级别之读未提交情况下删除数据测试案例
4.视频讲解的是mysql数据库中事务的隔离级别之未举一个例子:提交读案例测试,在删除数据的时候,事务隔离级别为读未提交时候的现象。可以发现,当在这种隔离级别下,删除数据同时也会影响到其他事务。
怎么查看数据库隔离级别
实际上,在这张订单表中,product_name 只依赖于 product_id ,customer_name 只依赖于 customer_id。也就是说,product_name 和 customer_id 是没用关系的,customer_name 和 product_id 也是没有关系的。1.查看当前会话隔离级别
在事务b下面执行如下两条语句:2.查看系统当前隔离级别
select @@global.tx_isolation;
3.设置当前会话隔离级别
set session transaction isolatin ll repeatable read;
set global transaction isolation ll repeatable read;
5.命令行,开始事务时
set autocommit=off 或者 start transaction
关于隔离级别的理解
1.read uncommitted
2.read committed
读取提交的数据。但是,可能多次读取的数据结果不一致(不可重复读,幻读)。用读写的观点就是:读取的行数据,可以写。
3.repeatable read(MySQL默认隔离级别)
可以重复读取,但有幻读。读写观点:读取的数据行不可写,但是可以往表中新增数据。在MySQL中,其他事务新增的数据,看不到,不会产生幻读。采用多版本并发控制(MVCC)机制解决幻读问题。
4.serializable
事务隔离的四个级别是什么?
结论是可行的,因为 tmp_table 存在键,且事务a的insert语句只是锁住了id为3的行。所以其他事务获取其他行的共享锁是可行的 。读者可以自行测试,这里就不做演示了。事务隔离的四个级别是未提交读(Read Uncommitted)、提交读(Read Committed)、可重复读(Repeable Read)、可串行化(Serializable)。
1、未提交读(Read Uncommitted):事务可以读取未提交的数据,也称作读(Dirty Re可以看到未提交的数据(读),举个例子:别人说的话你都相信了,但是可能他只是说说,并不实际做。ad)。一般很少使用。
2、提交读(Read Committed):是大都是DBMS(如:Oracle,SQL)默认事务隔离。执行两次同意的查询却有不同的结果,也叫不可重复读。
3、可重复读(Repeable Read):是MySQL默认事务隔离级别。能确保同一事务多次读取同一数据的结果是一致的。可以解决读的问题,但理论上无法解决幻读(Phantom Read)的问题。
4、可串行化(Serializable):是的隔离级别。强制事务串行执行,会在读取的每一行数据上加锁,这样虽然能避免幻读的问题,但也可能导致大量的超时和锁争用的问题。很少会应用到这种级别,只有在非常需要确保数据的一致性且可以接受没有并发的应用场景下才会考虑。
事务隔离级别特点比较
从事务隔离级别的定义上可以看出,Serializable级别隔离性,但是其效率也,因为其要求所有作相同记录的事务都串行的执行。
对于MySql而言,其默认事务级别是Repeatable read,虽然在定义上讲,这种隔离级别无法解决幻读的问题,但是MySql使用了一种Next key-lock的算法来实现Repeatable read,这种算法是能够解决幻读问题的。
关于Next key-lock算法,在进行查询时,其不仅会将当前的作记录锁住,也会将查询所涉及到的范围锁住。
也就是说,其他事务如果想要在当前事务查询的范围内进行数据作,那么其是会被阻塞的,因而MySql在Repeatable read隔离级别下就已经具备了Serializable隔离级别的事务隔离性。
以上内容参考:
MYSQL的事务隔离级别,MVCC,readView和版本链小结
是不一样的, 测试一 我们实际上用的就是checkout上的事务,并没有用到 purchase 的事务,从图上也能看出来。MVCC(Mutil-Version Concurrency Control),就是多版本并发控制。这种并发控制的方法,主要应用在RC和RR隔离级别的事务当中,利用执行select作时,访问记录版本链,使得不同事物的读写,写读可以并发执行,提高系统性能。
REQUIRES_NEW: 不管是否存在事务,业务方法总会为自己发起一个新的事务。如果方法已经运行在一个事务中,则原有事务会被挂起,新的事务会被创建,直到方法执行结束,新事务才算结束,原先的事务才会恢复执行。Innodb 有两个隐藏字段 trx_id(事务id)和roll_pointer(回滚指针)。
transaction id :
innoDB里面每个事务有一个的事务ID,叫作transaction id,它是在事务开始的时候向InnoDB的事务系统申请的,是按申请顺序严格递增的。
roll_pointer :
指向上一事务版本的指针。
版本链 :
是一个单链表结构,对于同一行数据,每一个事务对其进行更新的时候都会产生一个新的版本,就会存储在这个链表当中。
一个存储事务id的列表。
reaiew的几个参数:
m_ids:表示活跃事务id列表
max_trx_id:已创建的事务id
creator_trx_id:当前的事务id。
reaiew的生成时机:
RC隔离级别:每次读取数据前,都生成一个reaiew;
RR隔离级别:在次读取数据前,生成一个reaiew;
使用场景:
[ 创建事务 ] 当我创建一个新的事务需要读取一行数据, 我会查询活跃的事务列表; 设我当前的事务id是200, 当前活跃的事务id没有我的200, 因此需要去拷贝一个的不活跃事务并在版本链插入一个新200; mysql会去对比版本链和readView, 设版本链数据为[1,50,100,150], 活跃列表为[100,150], 说明100和150都是未提交的活跃事务, 再向前一个50不在活跃事务列表说明事务50已经提交, 所以事务200拷贝事务50并插入版本链, 且将200追加到readView活跃列表的一个元素
[ 使用事务 ] 当我再次进行200号事务的查询或修改, 我需要读版本链的数据, 因为上一次作已经在版本链做了200号, 因此我读的数据都是200号的数据, 这样就隔离了其他未提交的事务; 我的全部增删查改都在200号版本链上进行
[ readView实现事务隔离级别 ]以上两点都是基于隔离级别"读已提交"来进行说明的; 当mysql设置为"可重复读"时, 不同事务仍然是保存在版本链的不同上, 只不过新的事务创建的时候拷贝了当下的readView列表, 只要新事物不提交就一直使用这个拷贝的活跃列表; 设此时100号数据提交了, 我在新事务执行了select 会去查活跃列表发现100号事务还是未提交状态, 因此读取到的还是50号事务提交的记录。
原子性,一致性,隔离性,持久性。
未提交读(read uncommitted)、提交读(read committed)、可重复读(repeatable read)、序列化读(serializable)
mysql a事物在写 b事物能读吗
语法
我们先来看看事务的语法。比较浮躁,大家往往只在乎如何解决问题,而不去考虑问题的本质到底是什么。
所以我决定先来介绍事务的语法:
1. 开启事务start transaction,可以简写为 begin2. 然后记录之后需要执行的一组sql
3. 提交commit
4. 如果所有的sql都执行成功,则提交,将sql的执行结果持久化到数据表内。
5. 回滚rollback
6. 如果存在失败的sql,则需要回滚,将sql的执行结果,退回到事务开始之时7. 无论回滚还是提交,都会关闭事务!需要再次开启,才能使用。
8. 还有一点需要注意,就是事务只针对当前连接。
下面我们来进行演示:
使用个链接A,开启事务后,执行一条update语句。
结果成功,数据已经变成修改之后!
此时我们可读,不可写。像ja中的锁,写数据必须等待另一个事务结束。没有提交。
再从其他连接B来查看,发现数据为更改:
此时如果连接A选择提交,也就是commit作。则连接B的数据也会发生变化。
而如果连接A选择回滚,也就是rollback作。则连接A再次查询则发现数据还原。
基本原理
语法说完了,浮躁的人也不用继续看下去了。下面简单说一下事务的基本原理吧。
提交,就会将结果持久化,不提交就不会。
如果我们不开启事务,只执行一条sql,马上就会持久化数据,可以看出,普通的执行就是立即提交。
这是因为MySQL默认对sql语句的执行是自动提交的。
也就是说,开启事务,实际上就是关闭了自动提交的功能,改成了commit手动提交!
我们可以通过简单的对是否自动提交加以设置,完成开启事务的目的!
自动提交的特征是保存在服务的一个autocommit的变量内。可以进行修改:
还需要注意一点,就是事务类似于外键约束,只被innodb引擎支持。
特点
下面来说说事务的特点ACID。也就是原子性,一致性,隔离性和持久性。
原子性:事务是不可分割的。
一致性:保证数据在事务的执行周期内,是一致的!
隔离型:多个事务之间的干扰关系!隔离级别!
持久性:事务一旦被提交,就不可能再被回滚!
事务并发
事务并发会带来一些问题,所以才有了不同的事务隔离级别。要想了解事务的隔离级别,就必须首先了解事务并发会带来的问题。
一般来说,会出现三类数据读问题和数据更新问题。
读
一个事务正在对一条记录做修改,但未提交,另一个事务读取了这些数据,并进一步处理,就会产生未提交的数据依赖。
时间转账事务A取款事务B
T1开始事务
T2开始事务
T3查询账户余额为1000元
T4取出500元把余额改为500元
T5查询账户余额为500元(读)
T6撤销事务余额恢复为1000元
T7汇入100元把余额改为600元
T8提交事务
A读取了B尚未提交的数,导致余额为600元。
不可重复读
一个事务在不同时间读取数据不一致。
时间取款事务A转账事务B
T1开始事务
T2开始事务
T3查询账户余额为1000元
T4查询账户余额为1000元
T5取出100元把余额改为900元
T6提交事务
T7查询账户余额为900元(和T4读取的不一致)可以看到读取的数据不一致。
幻读
幻读和不可重复读的概念类似,都是不同时间数据不一致,只不过幻读是针对新增数据,而不可重复读是针对更改数据。
看一个例子:
时间统计金额事务A转账事务B
T1开始事务
T2开始事务
T3统计总存款数为10000元
T4新增一个存款账户,存款为100元
T5提交事务
T6再次统计总存款数为10100元(幻象读)
更新丢失
两个事务对同一数据进行更新,后者会覆盖先者的更新。
时间取款事务A转账事务B
T1开始事务
T2开始事务
T3查询账户余额为1000元
T4查询账户余额为1000元
T5汇入100元把余额改为1100元
T6提交事务
T8撤销事务
T9余额恢复为100表中的 customer_phone 有可能依赖于 order_id 、 customer_id 两列,也就不满足了第三范式的设计:其他列之间不能有传递依赖关系。0元(丢失更新)
隔离级别
事务并发带来的问题前文已经描述得非常仔细了。事务的隔离级别就是为了针对并发出现的问题,不同的级别可以保证不同的一致性。
为了解决上面讲到的并发事务处理带来的问题,SQL标准提出了4个等级的事务隔离级别。不同的隔离级别在同样的环境下会出现不同的结果。
下面看看四种隔离级别的比较:
隔离级别读数据一致性读不可重复读幻读
未提交读(Read uncommitted)级别,只能保证不读取物理上损坏的数据是是是已提交读(Read committed)语句级否是是
可重复读(Repeatable read)事务级否否是
可序列化(Serializable)别,事务级否否否
MySQL的RR隔离级别与幻读问题
最近在网上看了不少mysql锁的文章,不少文章都提到InnoDB的RR隔离级别(Repeatable Read)无法解决幻读的问题。对此问题作者亲自做了一些实验,将实验结论记录在此。
本次实验的mysql版本为5.7.22 。
此篇文章的重点在于通过实验的形式解释清楚InnoDB的RR隔离级别是否解决了幻读问题。所以文中将不会对一些相关的概念进行解释,默认读者已经具备相关知识。如果读者对于以下的知识点不甚清楚,自行查阅相关资料,理解清楚之后再阅读接下来的实验内容,以免造成困惑。
进行此次实验需要具备的知识点(包括但不限于):
创建表结构:
创建两条数据:
最终的表数据如下:
打开两个终端,连上mysql,分别启动事务a和事务b。
在事务a和事务b上面分别执行如下命令:
查询出来的结果如下:
事务a:
事务b:
很明显事务b没有查询到事务a未提交的新插入数据。原因也很简单,因为 普通的select语句是快照读,而事务b启动时,它的快照数据就已经被版本锁定了 。
那么我们在事务b里面执行如下命令来看看执行结果:
执行完成之后我们发现事务b此时会block住,原因是 事务a的insert语句排它锁住了id为3的新插入数据,而事务b想请求所有行的共享锁,肯定是需要等待的。
那么此时事务b当前读id为1或2的数据(非事务a新插入数据)是否可行呢?
事务a和事务b执2.3 Repeatable read(可重复读)行如下命令:
事务b打印的结果:
还是一样, 因为普通select是快照读,事务b还是读取到的是快照数据,所以不包含事务a提交之后的新数据 。
让我们在事务b下面转载: 使用共享锁查看当前版本数据:
结果如下:
可以查询到事务a已提交的新数据,所以此时使用当前读就产生了幻读 。
还有另一种情况也会产生幻读,并且只需要执行普通的select语句。下面请看演示。
条命令使用update更新了事务a已提交的新数据,第二条命令通过普通的select语句查看快照数据。
打印结果如下:
可以看到事务a已提交的新数据被事务b使用update语句更新了,并且通过普通的select语句给查询出来了,很显然,出现了幻读 。
所以说InnoDB的RR隔离级别没有或者解决了幻读问题都不太准确。应该说它并没有完全解决幻读的问题。
如果在同一个事务里面,只是总是执行普通的select快照读,是不会产生幻读的。
但是如果在这个事务里面通过当前读或者先更新然后快照读的形式来读取数据,就会产生幻读。
Phantom Rows
Innodb 中 RR 隔离级别能否防止幻读?
mysql修改事务级别需要重启吗
??事务的原子性(Atomicity)是指一个事务要么全部执行,要么不执行,也就是说一个事务不可能只执行了一半就停止了,比如你从取款机取钱,这个事务可以分成两个步骤:1划卡,2出钱。不可能划了卡,而钱却没出来,这两步必须同时完成.要么就不完成。不需要。查看MySQL事务隔离级别,默认可重复读,对于数据互联网项目修改成读已提交。Ssettransactionisolationll目标隔离级别;ELECT@@transaction_isolation,设置读已提交。SETglobaltransactionisolationLEVELreadcommitted。但是,重启之后就不生效了。需要再次设置。
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系 836084111@qq.com 删除。