mysql 事务处理(表的引擎必须是 innodb / BDB)
主要是两种两法:推荐用第一种
1.用 begin,rollback,commit 来实现
begin 开始一个事务
rollback 事务回滚
commit 事务提交
2.直接用 set 来改变 mysql的自动提交模式,系统默认是自动提交的
set autocommit = 0; 禁止自动提交
set autocommit = 1; 开启自动提交
用第一种方式可这样:
<?php
/*
表信息 innodb(id,text)
*/
require_once('conn.php'); // 数据库连接信息
$flag = 1; // 标识是完成提交还是回滚 1.提交完成 0.失败回滚
//因为是自动提交的(默认),所以要想用事务,就必须手动开始事务,并手动提交
//开启事务
mysql_query("BEGIN"); // 或者 mysql_query("START TRANSACTION"); 或者 mysql_query("begin work");
$sql1= "insert into innodb values('', 'xxx')";
$sql2 = "insert into innodb values('', 'iiii')";
$sql3 = "insert into innodb values('', 'eee'ee')";
if(!mysql_query($sql1)){$flag = 0;}
if(!mysql_query($sql2)){$flag = 0;}
if(!mysql_query($sql3)){$flag = 0;}
if($flag)
{
mysql_query("commit");
echo "commit";
}else
{
mysql_query("rollback");
echo "rollback";
}
// 因为 $sql3有误,所以是无法插入以上数据的
//结束这事务
mysql_query("END");
----------------
第二种方式可用:
<?php
require_once('conn.php'); // 数据库连接信息
// 相当于开启了事务,禁止自动提交
mysql_query("set autocommit=0", $conn);
$flag = 1; // 标识是完成提交还是回滚 1.提交完成 0.失败回滚
/*
表信息 innodb(id,text)
*/
$sql1= "insert into innodb values('', 'xxx')";
$sql2 = "insert into innodb values('', 'iiii')";
$sql3 = "insert into innodb values('', 'eee'ee')";
if(!mysql_query($sql1)){$flag = 0;}
if(!mysql_query($sql2)){$flag = 0;}
if(!mysql_query($sql3)){$flag = 0;}
if($flag)
{
mysql_query("commit");
echo "commit";
}else
{
mysql_query("rollback");
echo "rollback";
}
mysql_query("END", $conn); // 结束事务
mysql_query("set autocommit=1", $conn); // 还原自动提交,取消事务
// 因为 $sql3有误,所以是无法插入以上数据的
/*一定在提交或者回滚后,要调用 mysql_query("END", $conn);和mysql_query("set autocommit=1", $conn);,不然会把后面的 查询也当做事务来处理,不提交时就无法执行*/
事务应具备的ACID特征.
Atomic(原子性), Consistent(一致性),Isolated(隔离性),Durable(持续性)
原子性:组成事务处理的语句形成了一个逻辑单元,不能只执行其中的一部分。换
句话说,事务是不可分割的最小单元。比如:银行转帐过程中,必须同时从一个帐
户减去转帐金额,并加到另一个帐户中,只改变一个帐户是不合理的。
一致性:在事务处理执行前后,MySQL数据库是一致的。也就是说,事务应该正确
的转换系统状态。比如:银行转帐过程中,要么转帐金额从一个帐户转入另一个帐
户,要么两个帐户都不变,没有其他的情况。
隔离性:一个事务处理对另一个事务处理没有影响。就是说任何事务都不可能看到
一个处在不完整状态下的事务。比如说,银行转帐过程中,在转帐事务没有提交之
前,另一个转帐事务只能处于等待状态。
持续性:事务处理的效果能够被永久保存下来。反过来说,事务应当能够承受所有
的失败,包括服务器、进程、通信以及媒体失败等等。比如:银行转帐过程中,转
帐后帐户的状态要能被保存下来。
为了一致性,可用到 select * ... for update;(默认是行锁定,必须明确是主键
,不然变成了表锁了)[be careful](作用于 update / delete 操作)[此时是悲观锁]
如:
begin;
//id为主键,此行被锁定,直到这个事务提交,不然不让别的线程来更新此行数据
select * from tablename where id=1 for update;
//没有指定主键,会锁住个表
select * from tablename for update;
//明确指定主键,但无此数据,则无 lock
select * from tablename where id = 0 for update;
//无主键,表锁
select * from tablename where name='xx' for update;
//主键不明确,锁表
select * from tablename where id <> '1' for update;
select * from tablename where id like '1' for update;
例子:在两个命令窗口可测试:
连接1:
begin;
select * from tablename where id=1 for update;
如果此时没有 commit或rollback;
此时开如连接2:
update tablename set name="xxx" where id=2;
一定要等到连接1实现了 commit或rollback,连接2才会真正执行,不然一直在等待,直到超时或提交或回滚
for update独占锁
lock in share mode 共享锁模式(会造成死锁)
例如:
连接A:
begin;
select * from tablename where id=1 for update;
接着打开连接B:
begin;
update tablename set name="xx" where id=1;
或者 select * from tablename where id=1 for update;
此时必须等连接A中 commit或rollback连接B更新或删除才会执行生效
即 for update总是先以连接A为基准点
lock in share mode:
连接C:
begin;
select * from tablename where id=1 lock in share mode;
接着打开连接D:
begin;
select * from tablename where id=1 lock in share mode;
再继续执行连接C中的如下语句:
update tablename set name="xx" where id=1;
再往下执行连接D中的如下语句:
update tablename set name="yyyy" where id=1; ////此时出现死锁
如果要避免这种情况就得在连接D中先实现提交,再继续执行
这样理解:
如果是 for update,就得先完成最开始的事务,如果是 lock in share mode,就
得先完成最近的事务锁
myisam引擎(一定是)可实现锁表法:
lock tables tablename write;
insert into tablename values();
unlock tables;
如果上述中没有 unlock的话,另外的线程无法 curd,直到上面执行 unlock
如果 :
lock tables tablename read;
此时该线程只能执行查询,不能写入操作,另外的线程可以查询,但也无法写入,直到unlock
以上锁可归纳为悲欢锁
乐观锁则是这样的:
一般在数据表增加一个表示状态的字段,如开始状态了1,则A连接,还没来得更新,此时B连接上来,并修改了其中的数据,此时,该状态变为2,然后,A再更新数据,结果发现此时的状态与之前的连接的不一致,变为2了,说明已经变更过了,所以会通知A连接更新失败。