mysql事物主要用于处理操作量大,复杂度高的数据。比如说,在人员管理系统中,你删除一个人员,你既要删除人员的基本资料,也要删除和该人员相关的信息,如信箱,文章等。这样,这些数据库操作语句就构成一个事物。注意一下几点:
- 在MySQL中只有使用了Innodb数据库引擎的数据库或表才支持事务。
- 事物处理可以用来维护数据库的完整性,保证成批的sql语句要么全部执行,要么全部不执行。
- 事物用来管理insert,update,delete语句。
先简单介绍一下事物吧。事物是DBMS的执行单位。它由有限的数据库操作序列组成的,但不是任意的数据库操作序列都能成为事物。一般来说,事物必须满足4个条件(ACID):
原子性:组成事物处理的语句形成了一个逻辑单元,不能只执行其中一部分。换句话说,事物是不可分割的最小单元。比如银行转账过程中,必须同时从一个账户减去转账金额,并加到另一个账户中,只改变一个账户是不合理的。
一致性:在事物处理执行前后,mysql数据库是一致的。也就是说,事物应该正确的转换系统状态。比如银行转账过程中,要么转账金额从一个账户到另一个账户,要么两个账户都不变,没有其他情况。
隔离性:一个事物处理对另外一个事物处理没有影响。比如说银行转账过程中,在转账事物没有提交之前,另一个转账事物只能处于等待状态 。
可靠性:事物处理的效果能够被永久保存下来。反过来说,事物能够承受所有的失败。包括服务器,进程,通信以及媒体失败等等。比如银行转账过程中,转账后账户的状态要能被保存下来。
mysql的事物处理主要有两种方法:
1. begin,rollback,commit来实现
begin 开始一个事物
rollback 事物回滚
commit 事物确认,事物提交
2,直接用set来改变mysql的自动提交模式
mysql默认z是自动提交的,也就是你提交一个query,它就直接执行。
set autocommit = 0 禁止自动提交模式
set autocommit = 1 开启自动提交模式
mysql中只用INNODB和BDB类型的数据表才能支持事物处理(切记)!
来看一个例子:
先假设一下问题的背景:网上购书,某书《mysql数据库》编号为123,只剩最后一本,而这个时候两个用户几乎同时对这本书发出了购书请求,让我们来看看整个过程:
在具体分析之前,先来看看数据表的定义:
对于用户甲来说,他的动作稍微比乙快一点点,其购买过程中所触发的动作大致是这样的:
1.select book_number from book where book_id = 123;
book_number 大于0,确认购买行为并更新book_number
2.update book set book_number = book_number—1 where book_id = 123;
购书成功
表面上看甲乙的操作都成功了,他们都买到了书,但是库存只有一本,他们怎么可能都成功呢?再看看数据表里book_number的内容,已经变成“-1”了,这当然是不能允许的(实际上,声明这样的列类型应该加上unsigned的属性,以保证其不能为负,这里是为了说明问题所以没有这样设置)
好了,问题陈述清楚了,再来看看怎么利用事务来解决这个问题,打开MySQL手册,可以看到想用事务来保护你的SQL正确执行其实很简单,基本就是三个语句:开始,提交,回滚。
开始:START TRANSACTION或BEGIN语句可以开始一项新的事务
提交:COMMIT可以提交当前事务,是变更成为永久变更
回滚:ROLLBACK可以回滚当前事务,取消其变更
此外,SET AUTOCOMMIT = {0 | 1}可以禁用或启用默认的autocommit模式,用于当前连接。
那是不是只要用事务语句包一下我们的SQL语句就能保证正确了呢?比如下面代码:
答案是否定了,这样依然不能避免问题的发生,如果想避免这样的情况,实际应该如下:
由于加入了FOR UPDATE,所以会在此条记录上加上一个行锁,如果此事务没有完全结束,那么其他的事务在使用SELECT ... FOR UPDATE请求的时候就会处于等待状态,直到上一个事务结束,它才能继续,从而避免了问题的发生,需要注意的是,如果你其他的事务使用的是不带FOR UPDATE的SELECT语句,将得不到这种保护。