事务处理在各种管理系统中都有着广泛的应用,比如人员管理系统,很多同步数据库操作大都需要用到事务处理。比如说,在人员管理系统中,你删除一个人员,你即需要删除人员的基本资料,也要删除和该人员相关的信息,如信箱,文章等等,这样,这些数据库操作语句就构成一个事务!用事务处理。如果删除出错,你只要rollback就可以取消删除操作
MYSQL数据库从4.1就开始支持事务功能,据说5.0引入存储过程
MYSQL的事务处理主要有两种方法。
1、用begin,rollback,commit来实现
begin 开始一个事务
rollback 事务回滚
commit 事务确认
2、直接用set来改变mysql的自动提交模式
MYSQL默认是自动提交的,也就是你提交一个QUERY,它就直接执行!我们可以通过
set autocommit=0 禁止自动提交
set autocommit=1 开启自动提交
来实现事务的处理。MYSQL中只有INNODB和BDB类型的数据表才能支持事务处理!其他的类型是不支持的
事例:
<?php
$handler=mysql_connect("localhost","root","");
mysql_select_db("task");
mysql_query("SET AUTOCOMMIT=0");//设置为不自动提交,因为MYSQL默认立即执行
mysql_query("BEGIN");//开始事务定义
if(!mysql_query("insert into trans (id) values('2')"))
{
mysql_query("ROOLBACK");//判断当执行失败时回滚
}
if(!mysql_query("insert into trans (id) values('4')"))
{
mysql_query("ROOLBACK");//判断执行失败回滚
}
mysql_query("COMMIT");//执行事务
mysql_close($handler);
?>
BEGIN;
SELECT book_number FROM book WHERE book_id = 123 FOR UPDATE ;
// ...
UPDATE book SET book_number = book_number - 1 WHERE book_id = 123;
COMMIT;
-------------------------------------------------------------------------------
由于加入了FOR UPDATE,所以会在此条记录上加上一个行锁,如果此事务没有完全结束,那么其他的事务在使用SELECT ... FOR UPDATE请求的时候就会处于等待状态,直到上一个事务结束,它才能继续,从而避免了问题的发生,需要注意的是,如果你其他的事务使用的是不带FOR UPDATE的SELECT语句,将得不到这种保护。
基本命令:
- 开启事务
- START TRANSACTION 或 BEGIN
- 提交事务(关闭事务)
- COMMIT
- 放弃事务(关闭事务)
- ROLLBACK
- 折返点
- SAVEPOINT adqoo_1
- ROLLBACK TO SAVEPOINT adqoo_1
- 发生在折返点 adqoo_1 之前的事务被提交,之后的被忽略
- 事务的终止
- 设置“自动提交”模式
- SET AUTOCOMMIT = 0
- 每条SQL都是同一个事务的不同命令,之间由 COMMIT 或 ROLLBACK隔开
- 掉线后,没有 COMMIT 的事务都被放弃
- 事务锁定模式
- 系统默认: 不需要等待某事务结束,可直接查询到结果,但不能再进行修改、删除。
- 缺点:查询到的结果,可能是已经过期的。
- 优点:不需要等待某事务结束,可直接查询到结果。
- 需要用以下模式来设定锁定模式
- 1、SELECT …… LOCK IN SHARE MODE(共享锁)
- 查询到的数据,就是数据库在这一时刻的数据(其他已commit事务的结果,已经反应到这里了)
- SELECT 必须等待,某个事务结束后才能执行
- 2、SELECT …… FOR UPDATE(排它锁)
- 例如 SELECT * FROM tablename WHERE id<200
- 那么id<200的数据,被查询到的数据,都将不能再进行修改、删除、SELECT …… LOCK IN SHARE MODE操作
- 一直到此事务结束
- 共享锁 和 排它锁 的区别:在于是否阻断其他客户发出的 SELECT …… LOCK IN SHARE MODE命令
- 3、INSERT / UPDATE / DELETE
- 所有关联数据都会被锁定,加上排它锁
- 4、防插入锁
- 例如 SELECT * FROM tablename WHERE id>200
- 那么id>200的记录无法被插入
- 5、死锁
- 自动识别死锁
- 先进来的进程被执行,后来的进程收到出错消息,并按ROLLBACK方式回滚
- innodb_lock_wait_timeout = n 来设置最长等待时间,默认是50秒
- 事务隔离模式
- SET [SESSION|GLOBAL] TRANSACTION ISOLATION LEVEL
- READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE
- 1、不带SESSION、GLOBAL的SET命令
- 只对下一个事务有效
- 2、SET SESSION
- 为当前会话设置隔离模式
- 3、SET GLOBAL
- 为以后新建的所有MYSQL连接设置隔离模式(当前连接不包括在内)
- 隔离模式
- READ UNCOMMITTED
- 不隔离SELECT
- 其他事务未完成的修改(未COMMIT),其结果也考虑在内
- READ COMMITTED
- 把其他事务的 COMMIT 修改考虑在内
- 同一个事务中,同一 SELECT 可能返回不同结果
- REPEATABLE READ(默认)
- 不把其他事务的修改考虑在内,无论其他事务是否用COMMIT命令提交过
- 同一个事务中,同一 SELECT 返回同一结果(前提是本事务,不修改)
- SERIALIZABLE
- 和REPEATABLE READ类似,给所有的SELECT都加上了 共享锁
- 出错处理
- 根据出错信息,执行相应的处理
其它实例:
<?php |
02 | $conn =mysql_connect( 'localhost' , 'root' , 'yourpassword' ) or die (mysql_error()); |
03 | mysql_select_db( 'transaction' , $conn ); |
04 | mysql_query( 'set names utf8' ); |
05 |
06 | //创建事务 |
07 | mysql_query( 'START TRANSACTION' ) or die (mysql_error()); |
08 | $sqlA = "update A set account=account-1" ; |
09 | if (!mysql_query( $sqlA )){ |
10 | mysql_query( 'ROLLBACK' ) or exit (mysql_error()); //判断当执行失败时回滚 |
11 | exit (); |
12 | } |
13 | $sqlB = "update B set account=account+1" ; |
14 | if (!mysql_query( $sqlB )){ |
15 | mysql_query( 'ROLLBACK' ) or exit (mysql_error()); //判断当执行失败时回滚 |
16 | exit (); |
17 | } |
18 | mysql_query( 'COMMIT' ) or die (mysql_error()); //执行事务 |
19 | mysql_close( $conn ); |
20 | ?> |
以上代码可以作为模拟银行转账业务的事务流程。以表A、B分别表示两个已在银行开户的账户,当账户A执行转出1元给账户B的操作时,如果操作执行失败,转出将会回滚至原始状态,不继续向下执行动作。反之,如果操作执行成功,则账户B可用余额将增加1元,否则事务回滚至原始状态。