Mysql的事务

前几天阅读了《高性能MySQL》第一章,里面讲解了一些Mysql事务相关的知识,一下就帮我解决了对Mysql事务的一些疑惑。


先讨论一个问题:

假设有两个人A和B,A要往B的银行卡里面转200块钱,那么这涉及到两步数据库操作:1、从A的账户扣200块,2、把B账户的钱加200。

如果只完成了第一步,第二步执行失败,那么B肯定不会答应。如果只完成了第二步,银行又不会答应。所以银行必须保证这两步操作能够成功完成或者都失败。那么这就可以使用事务来处理。


事务的特性:ACID

原子性atomicity:一个事务是一个不可分割的工作单位,一个事务里面所有的操作要么都全部执行成功,要么全部执行失败。上述问题中的两步操作要么都成功执行,要么全部执行失败。

一致性consistency:事务必须是使数据库从一个一致性状态到另一个一致性状态,一致性与原子性是密切相关的。

隔离性isolation:一个事务的执行不能被其他事务干扰,即一个事务的数据操作对其他事务来说是不可见的。举个例子就是有两个事务AB,A对数据库作出的修改在A提交之前,B事务是不可见的。在Mysql中分为4个隔离级别从低到高:READ UNCOMMITED、READ COMMIT、REPEATABLE READ、SERIALIZABLE四个级别。隔离级别保证了一个事务的执行是否会被其他事务干扰。

持久性durability指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。


在讲使用事务之前先说下Mysql的隔离级别:

READ UNCOMMITED:一个事务可以读取在另一个事务未提交前的数据,即用户表张三sex=女,有事务AB,A对用户表张三用户设置了sex=男,B可以在A提交前读取到张三sex=男。这个级别是最低的,一般不使用。且这个级别可能会引发脏读问题,即事务A对数据做出了修改,B读取了数据,A由于某种原因撤销了对数据的修改,那么B得到的错误数据。解决脏读问题可以使用下一个隔离级别READ COMMITED。


READ COMMITED:提交读,一个事务可以读取在另一个事务提交后的数据。即用户表张三sex=女,有事务AB,A队用户表张三用户设置了sex=男,B在A提交前读取的数据都是张三sex=女,只有在A提交后,才会读取到张三sex=男。但这会引发不可重复读问题。即一个事务执行相同的查询可能结果会不一致。Oracle、SqlSever默认使用这个隔离级别。要解决不可重复读问题可以使用下一个隔离级别REPEATABLE READ。


REPEATABLE READ:可重复读,一个事务执行相同的查询总是返回相同的结果。Mysql默认隔离级别。这级别解决了不可重复读问题,但是会产生幻读:即事务A在读取某个范围的记录时,另一个事务B向该范围内插入或删除了数据,当A再读取的时候就会产生幻行。听着幻读和不可重复读很像同一个东西,但是不可重复读的重心是修改,即读取的内容有区别,而幻读重心是插入和删除,即读取的数据量差异。


SERIALIZABEL:串行化,最高隔离级别。每个事务串行执行,一般不使用这个级别。


在Mysql中设置隔离性级别使用语句 SET  TRANSACTION  ISOLATION  LEVEL + 级别。

再来讲讲事务,Mysql提供了两种支持事务的引擎InnoDB和NDB Cluster。此外还有一些第三方的引擎支持事务。

Mysql的事务默认是自动提交的(autocommit),即在命令行模式下每一条sql语句都是当做一个事务提交的。可以使用指令set autocommit = 0取消mysql的自动事务提交,set autocommit = 1启动mysql的自动事务提交。

Mysql打开事务使用指令start transaction 或者begin,结束事务commit或者rollback,commit是提交,rollback是回滚,提交会改变数据,而回滚则会将数据恢复到开启事务前。如下图所示:

       来做一个实验,开启事务并查询出当前数据库的表:  

     下一步查询出表t_user的数据,并将id为1的用户的密码改为951001:

 

    需要注意的是此时事务还没有提交,所以在当前事务外面是看不到id为1的用户的密码被修改了,我们可以再打开一个命令窗口查询一下,如下图所示,现在还不能看到被修改后的数据。

 

    让我们在执行一条指令,

   执行完上述指令,我们在切换到新启动的窗口查询数据(事务还未提交): 

      数据居然在事务没有提交的情况下改变了,再执行show tables查询表: 

      事务中创建表的语句执行了,多了一张admin1表,而且数据也改变了,那这是什么情况,按道理说事务提交前,另一个事务是不能查询到当前修改的数据的。造成这样结果的原因是使用DDL语句,SQL语句是分为两种语言的,一种叫做DDL语言,即数据定义语言,另一种叫做数据操纵语言DML,DDL就是我们常用的CREATE、DROP、ALTER等语句,而DML就是SELECT、UPDATE、DELETE等语句。原因就是当执行DDL语句的时候,Mysql会在执行之前强制提交当前的活动事务,当然还有其他情况也会造成这种结果。还有注意的是,如果将Mysql的autocommit关闭的话,那么当前执行的所有语句(不包括上述讲到的DDL语句)都会在一个事务中,只有手动commit提交之后其他事务才能看到数据的改变。而且关闭autocommit只对当前窗口有效,重新再打开一个窗口就无效了,可以使用指令show variables like ‘autocommit’查看当前自动提交是否开启。


       JDBC的事务,JDBC是J2EE的一个工业标准。在Mysql的JDBC中,每一个连接是开启了自动提交的,这一点可以查询源代码,当然你在编写代码的时候可以手动关闭。而流行的ORM框架Mybatis依赖于JDBC,但是它在JDBC的基础上获得了连接之后关闭了自动提交,所以当使用Mybatis对数据库进行修改时,记得手动提交事务。

       最后讲讲Mysql的同步,首先先打开两个窗口中打开两个事务:


      然后事务A将表t_user中的id为1的用户的密码设置为456789,,但不提交,如下图:


      然后事务B对t_user中的数据进行修改(也是id为1的用户):


     发现事务B卡在这儿了,过了一段时间输出了如下信息:


      这是因为事务A对这一行数据进行了修改,那么这一行数据就被锁定了,其他事务不能对这一行数据进行修改,那么我们试试修改其他行的数据:


      我们发现修改该条数据的语句不能执行了,但是可以查询和插入其他行。这是因为当一个事务在修改数据时,mysql会锁定数据(锁定分为表锁和行锁,这个锁就是字面上的意思),而锁的类型分为两种:读锁和写锁。不能修改但是可以查询表示当前行数据被锁定了,是写锁,这种情况下只能查询数据,但不能修改。这保证了同一时刻只能有一个事务对数据进行修改。

最后千万要注意在使用事务的时候不要插入执行DDL语句,编码过程中用到的sql语句也都应当是DML语句。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值