mysql事务实战
mysql事务主要用于处理操作量大,复杂度高的数据。比如说,在人员管理系统中,你删除一个人员,你既要删除人员的基本资料,也要删除和该人员相关的信息,如信箱,文章等。这样,这些数据库操作语句就构成一个事务。注意一下几点:
- 在MySQL中只有使用了Innodb数据库引擎的数据库或表才支持事务。
- 事务处理可以用来维护数据库的完整性,保证成批的sql语句要么全部执行,要么全部不执行。
- 事务用来管理insert,update,delete语句。
mysql> SELECT * FROM ordertotals; +-----------+-----------------+-------------+ | order_num | total_not_taxed | total_taxed | +-----------+-----------------+-------------+ | 20005 | 149.87 | 222.00 | | 20009 | 38.47 | 40.78 | | 20006 | 55.00 | 58.30 | | 20007 | 1000.00 | 1060.00 | | 20008 | 125.00 | 132.50 | | 20008 | 125.00 | 132.50 | | 11111 | 100.00 | 125.00 | | 11111 | 100.00 | 125.00 | +-----------+-----------------+-------------+ 8 rows in set (0.00 sec) mysql> START TRANSACTION; Query OK, 0 rows affected (0.00 sec) mysql> DELETE FROM ordertotals WHERE order_num = 11111; Query OK, 2 rows affected (0.00 sec) mysql> SELECT * FROM ordertotals; +-----------+-----------------+-------------+ | order_num | total_not_taxed | total_taxed | +-----------+-----------------+-------------+ | 20005 | 149.87 | 222.00 | | 20009 | 38.47 | 40.78 | | 20006 | 55.00 | 58.30 | | 20007 | 1000.00 | 1060.00 | | 20008 | 125.00 | 132.50 | | 20008 | 125.00 | 132.50 | +-----------+-----------------+-------------+ 6 rows in set (0.00 sec) mysql> ROLLBACK; Query OK, 0 rows affected (0.01 sec) mysql> SELECT * FROM ordertotals; +-----------+-----------------+-------------+ | order_num | total_not_taxed | total_taxed | +-----------+-----------------+-------------+ | 20005 | 149.87 | 222.00 | | 20009 | 38.47 | 40.78 | | 20006 | 55.00 | 58.30 | | 20007 | 1000.00 | 1060.00 | | 20008 | 125.00 | 132.50 | | 20008 | 125.00 | 132.50 | | 11111 | 100.00 | 125.00 | | 11111 | 100.00 | 125.00 | +-----------+-----------------+-------------+ 8 rows in set (0.00 sec) 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语句,将得不到这种保护。