mysql的事务处理经常会被运用,以前我也没有重视,感觉用的地方应该不多,但是后来看别人的项目因为这个出问题才发现其实挺重要的,所以着重写个博客给自己记录一下。
首先理解一下事务,在理论部分其实该要记住的都要记住。
我对事务的理解就是:事务是多个数据库操作的集合,该集合内必须所有的数据库操作完成,事务才能完成,只要有一个操作失败,事务就不会成功,之前成功的数据库操作会进行回滚以保证事务的完整性不会遭到破坏,因此事务具有不错的安全性!
事务的四个特性,简称为ACID,这是这四个特性的英文头个字母。
一:原子性(Atomic)
所谓原子性就是指的事务的不可分割性,原子是最小的单位,所以理解事务的原子性就是理解事务无法进行分割,事务里的数据库操作要么全部成功,要么全部失败,不可能出现一部分成功,一部分失败的情况,这就是原子性。
二:一致性(Consistent)
事务操作会确保数据库状态保持一致性,事务提交前和提交后数据库状态会发生改变,但会保持一致性,例如购买商品,一旦完成一笔交易的事务,数据库里的商品数必然减1,同时商店收益必然增加,这就是保持了数据库状态的一致性。
三:隔离性(Isolated)
隔离性指的是事务间是相互独立的,不会互相影响,例如点餐过程为一个事务,从电话订购到做出饭菜送餐完成结算完成所有这些操作为一个事务,不同的人进行点餐操作都是相互独立的,并不会互相影响,这就是事务的隔离性,如果一个事务影响了其他的事务,其他的事务将会进行回滚,就如上面的点餐例子,如果饭菜已经卖完,最后点餐的事务将会影响后面的事务,因为没有餐可点了,那么后面的事务将会回滚。
四:持久性(durable)
事务提交的结果或者效果在出现各种故障(例如断网、服务器出问题之类的)的情况依旧能够保存下来,这就是事务的持久性。
Mysql事务基本语法:
1)使用事务首先要关闭自动提交,mysql默认自动提交,也就是你提交一个query语句便立即执行,事务是多个query语句的集合,因此必须关闭自动提交才行。
set autocommit=0或者1 //0为不自动提交,1为自动提交,默认为1
这里autocommit也可以直接在mysql的配置文件里更改,windows是改my.ini文件,linux是改my.cnf文件
在[mysqld]下加入autocommit=0/1即可。
2)开始一个事务
start transaction或者begin
3)保存点
savepoint name(保存点名为name)
4)回退一个事务
rollback(全部回退)
rollback to savepoint(回退到特定的保存点)
5)提交一个事务
commit
Mysql INNODB事务隔离级别:
一)未提交读(readuncommited),其实英文原来的表述最贴切,readuncommit就是read uncommit,翻译过来就是读取没有提交的,也就是没有提交的事务可以被其他事务读取,这个隔离级别是最低的!但是并发的性能高,会出现脏读、幻读、不可重复读(下面会进行进一步的解释,先别急)。
为了更好的理解举个例子:
事务A正在提交张三的各科成绩,虽然
二)提交读(readcommit),同样看英文拆分一下就是read commit,很好理解,就是读取提交的,也就是只有提交后的事务才能被读取,会出现幻读和不可重复读。
三)重复读(repeatableread),英文拆分为repeatable read,也就是重复性的读取,指的是在同一个事务里select的读取的是事务最开始读取的点的状态,这种隔离会出现幻读。
四)串行化(serializable),英文翻译过来就是串行化,读取操作会使用共享锁,可以保证不同事务的互斥,也就是锁表。
在说明脏读,幻读,不可重复读这几个之前,我们先来了解怎么设置和查询事务隔离级别,因为下面的讲解需要进行实验,这些实验需要更改隔离级别来实现:
一)查询当前这个点的下一个事务隔离级别:select @@tx_isolation;
下面是个范例,搜索到的当前事务隔离级别是默认的可重复读
二)查询全局事务隔离级别,也就是从这个点开始的下一个事务起所有的事务都采用设置的该隔离级别:select @@global.tx_isolation;
范例,
三)查询当前所在的事务的隔离级别:select @@session.tx_isolation;
四)设置事务隔离级别
方法1:在my.cnf文件中的mysqld中进行更改。可供更改的leve 【READ-UNCOMMITTED | READ-COMMITTED | REPEATABLE-READ | SERIALIZABLE】
如:transaction_isolation = SERIALIZABLE
更改文件后需要重启mysql,下面是文件后查询的结果:
方法2:用mysql语句进行更改,语法 set tx_isolation= level level级别【READ-UNCOMMITTED | READ-COMMITTED | REPEATABLE-READ | SERIALIZABLE】
范例:
下面我们来理解一下上面说的脏读,幻读,不可重复读!
脏读:脏读指的是一个事务修改本事务的数据,但没有提交的情况下,本事务可以查看到更改了却没有提交的数据。这种情况只可能发生在未提交读readuncommitted的隔离级别里。
我们来看看例子:
首先设置事务隔离级别为read-uncommited
事务一:给t表添加数据a=1但并不提交!查询t表的结果里本事务是可以看到未提交的操作结果的。
事务二:查询t表的数据,这个时候事务一是没有提交的!要确保事务二的隔离级别是read-uncommited。
这个情况就是所谓的脏读!只可能出现在read-uncommit隔离级别里!如果是其他的隔离级别,我们来看一下下图的结果就一目了然了!下图里我出了点错,串行化是serializable,略过这里,我们可以看到查询到的结果都是没有insert操作的结果,看不到a=1,所以可以认为其他的结果隔离级别解决了脏读的这个问题。
不重复读:指的是一个事务多次读取同一个数据,但此过程中第二个事务对第一个事务进行了修改,这就造成了第一个事务的多次读取过程中的结果出现了前后不一致的情况,这就是不可重复读!这个情况可以出现在read-uncommited和read-committed两个隔离级别里!
我们来看看例子,先看反例repeatable-read隔离级别下的情况,这种级别下只会读取首次select的点的数据结果,以避免出现不重复读的情况:
事务一:查询t表a=5,然后update进行修改a=22,查看修改后的结果最后提交
事务二:在事务一开始后select操作时同时也进行两次查询,查询结果和事务一的结果一致,都是5,等到事务一update操作后事务二进行第三次查询,查询结果依旧为5,事务一提交后,事务二进行第四次查询,结果依旧是5,没有任何改变,所以repeatable-read的隔离级别解决了重复读的问题。
下面我们再来看read-commit隔离级别下的情况:
事务一:
事务二:
幻读:在repeatable-read级别下解决了不可重复读的问题,但是还有没有解决的地方,那就是幻读,幻读是指在一个事务中,事务一对数据进行了更改并提交了,而事务二也对该数据进行了更改,但是更改的情况可能会受到事务一更改的影响,从而引起更改仿佛不存在,这就是幻读!这是由于repeatable-read的隔离措施只读取初次select的点导致的。
范例:t表的有一行数据,a=5
事务一:首先读取t表,发现有一个a=5的数据,然后更改该数据为25并提交
事务二:首先读取t表,发现有一个a=5的数据,这个时候事务一已经删除数据并期间了,但是由于隔离级别是repeatable-read导致读取的结果是a=5的数据还在,这个时候如果事务二对该数据进行更改,改为26,提交后发现之前的更改无效,该行数据a=25,这是因为事务一已经对a=5的数据进行了更改,改为了25,这个时候a=5的数据已经不存在了,因此这次更改无效,等同于幻影,这就是幻读!
可以从这里看出,提交读解决了脏读问题,而重复读解决了不重复读的问题,串行化解决幻读的问题,但是也会造成锁竞争,可能造成大量的超时问题,因为串行化是给每个读的数据行加上共享锁,通过强制事务进行排序,以此防止相互冲突,具体原理以后单独作为一个博客内容整理。