MySQL事务处理2

MySQL5.X 都已经发布好久了,但是还有很多人认为MySQL是不支持事务处理的,这不得不怪他们是孤陋寡闻的,其实,只要你的MySQL版本支持BDB或 InnoDB表类型,那么你的MySQL就具有事务处理的能力。这里面,又以InnoDB表类型用的最多,虽然后来发生了诸如Oracle收购 InnoDB等令MySQL不爽的事情,但那些商业上的斗争与技术无关,下面以InnoDB表类型为例简单说一下MySQL中的事务。

先来明确一下事务涉及的相关知识:

事务都应该具备ACID特征。所谓ACID是Atomic(原子性),Consistent(一致性),Isolated(隔离性),Durable(持续性)四个词的首字母所写,下面以“银行转帐”为例来分别说明一下它们的含义:

原子性:组成事务处理的语句形成了一个逻辑单元,不能只执行其中的一部分。换句话说,事务是不可分割的最小单元。比如:银行转帐过程中,必须同时从一个帐户减去转帐金额,并加到另一个帐户中,只改变一个帐户是不合理的。

一致性:在事务处理执行前后,数据库是一致的。也就是说,事务应该正确的转换系统状态。比如:银行转帐过程中,要么转帐金额从一个帐户转入另一个帐户,要么两个帐户都不变,没有其他的情况。

隔离性:一个事务处理对另一个事务处理没有影响。就是说任何事务都不可能看到一个处在不完整状态下的事务。比如说,银行转帐过程中,在转帐事务没有提交之前,另一个转帐事务只能处于等待状态。

持续性:事务处理的效果能够被永久保存下来。反过来说,事务应当能够承受所有的失败,包括服务器、进程、通信以及媒体失败等等。比如:银行转帐过程中,转帐后帐户的状态要能被保存下来。

再来看看哪些问题会用到事务处理:

这里不说“银行转帐”的例子了,说一个大家实际更容易遇到的“网上购书”的例子。先假设一下问题的背景:网上购书,某书(数据库编号为123)只剩最后一本,而这个时候,两个用户对这本书几乎同时发出了购买请求,让我们看看整个过程:

在具体分析之前,先来看看数据表的定义:

-------------------------------------------------------------------------------

1 create table book
2 (
3     book_id unsigned int(10) not null auto_increment,
4     book_name varchar(100) not null,
5     book_price float(5, 2) not null, #我假设每本书的价格不会超过999.99元
6     book_number int(10) not null,
7     primary key (book_id)
8 )
9 type = innodb; #engine = innodb也行

-------------------------------------------------------------------------------

对于用户甲来说,他的动作稍微比乙快一点点,其购买过程所触发的动作大致是这样的:

-------------------------------------------------------------------------------

1.

SELECT book_number FROM book WHERE  book_id = 123;

book_number大于零,确认购买行为并更新book_number

2.

 UPDATE book SET book_number = book_number - 1 WHERE  book_id = 123;

购书成功

-------------------------------------------------------------------------------

而对于用户乙来说,他的动作稍微比甲慢一点点,其购买过程所触发的动作和甲相同:

-------------------------------------------------------------------------------

1.

SELECT book_number FROM book WHERE  book_id = 123;

这个时候,甲刚刚进行完第一步的操作,还没来得及做第二步操作,所以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语句就能保证正确了呢?比如下面代码:

-------------------------------------------------------------------------------

BEGIN;

SELECT book_number FROM book WHERE  book_id = 123;

// ...

UPDATE book SET book_number = book_number - 1 WHERE  book_id = 123;

COMMIT;

-------------------------------------------------------------------------------

答案是否定了,这样依然不能避免问题的发生,如果想避免这样的情况,实际应该如下:

-------------------------------------------------------------------------------

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语句,将得不到这种保护。

最后看看PHP + MySQL事务操作的代码演示:

实际LAMP应用中,一般PHP使用AdoDB操作MySQL,下面给出AdoDB相应的代码方便大家查阅:

-------------------------------------------------------------------------------

<?php  // ...
$adodb -> startTrans ();

//实际,getOne所调用的查询也可以直接放到rowLock来进行,这里只是为了演示效果能更明显些。
$adodb -> rowLock ( ‘book‘ ,  ‘book_id = 123‘ );
$bookNumber  =  $adodb -> getOne ( "SELECT book_number FROM book WHERE  book_id = 123" );
$adodb -> execute ( "UPDATE book SET book_number = book_number - 1 WHERE  book_id = 123");
$adodb -> completeTrans ();
// ...  ?>

-------------------------------------------------------------------------------

其中,rowLock的方法就是调用的FOR UPDATE来实现的行锁,你可能会想把“FOR UPDATE”直接写到$adodb->getOne()调用的那条SQL语句里面去实现行锁的功能,不错,那样确实可以,但是并不是所有的数据库 都使用“FOR UPDATE”语法来实现行锁功能,比如Sybase使用“HOLDLOCK”的语法来实现行锁功能,所以为了你的数据库抽象层保持可移植性,我还是劝你 用rowLock来实现行锁功能,至于可移植性就交给AdoDB好了,嗯,有点扯远了,今儿就说到这里了。

-------------------------------------------------------------------------------

附:

AdoDB中存在一个setTransactionMode()方法,能够设置事务的隔离级别,如下:

SetTransactionMode allows you to pass in the transaction mode to use for all subsequent transactions for that connection session. Note: if you have persistent connections and using mysql or mssql, you might have to explicitly reset your transaction mode at the beginning of each page request. This is only supported in postgresql, mssql, mysql with InnoDB and oci8 currently. For example:

$db->SetTransactionMode("SERIALIZABLE");
$db->BeginTrans();
$db->Execute(...); $db->Execute(...);
$db->CommiTrans();

$db->SetTransactionMode(""); // restore to default
$db->StartTrans();
$db->Execute(...); $db->Execute(...);
$db->CompleteTrans();

Supported values to pass in:

    * READ UNCOMMITTED (allows dirty reads, but fastest)
    * READ COMMITTED (default postgres, mssql and oci8)
    * REPEATABLE READ (default mysql)
    * SERIALIZABLE (slowest and most restrictive)

You can also pass in database specific values such as ‘SNAPSHOT‘ for mssql or ‘READ ONLY‘ for oci8/postgres.
时间: 2024-11-10 20:51:35

MySQL事务处理2的相关文章

mysql事务处理用法与实例详解

MySQL的事务支持不是绑定在MySQL服务器本身,而是与存储引擎相关1.MyISAM:不支持事务,用于只读程序提高性能 2.InnoDB:支持ACID事务.行级锁.并发 3.Berkeley DB:支持事务 一个事务是一个连续的一组数据库操作,就好像它是一个单一的工作单元进行.换言之,永远不会是完整的事务,除非该组内的每个单独的操作是成功的.如果在事务的任何操作失败,则整个事务将失败. 实际上,会俱乐部许多SQL查询到一个组中,将执行所有的人都一起作为事务的一部分. 事务的特性: 事务有以下四

PHP与MYSQL事务处理

PHP与MYSQL事务处理 2016-03-28 fal PHP编程 MYSQL的事务处理主要有两种方法. 1.用begin,rollback,commit来实现 begin 开始一个事务 rollback 事务回滚 commit 事务确认 2.直接用set来改变mysql的自动提交模式 MYSQL默认是自动提交的,也就是你提交一个QUERY,它就直接执行!我们可以通过 set autocommit=0 禁止自动提交 set autocommit=1 开启自动提交 来实现事务的处理. 当你用 s

mysql事务处理的意义

MySQL的事务支持不是绑定在MySQL服务器本身,而是与存储引擎相关 1.MyISAM:不支持事务,用于只读程序提高性能 2.InnoDB:支持ACID事务.行级锁.并发 3.Berkeley DB:支持事务  一个事务是一个连续的一组数据库操作,就好像它是一个单一的工作单元进行.换言之,永远不会是完整的事务,除非该组内的每个单独的操作是成功的.如果在事务的任何操作失败,则整个事务将失败.实际上,会俱乐部许多SQL查询到一个组中,将执行所有的人都一起作为事务的一部分. 事务的特性: 事务有以下

[转]mysql事务处理用法与实例详解

转自:http://www.cnblogs.com/ymy124/p/3718439.html MySQL的事务支持不是绑定在MySQL服务器本身,而是与存储引擎相关1.MyISAM:不支持事务,用于只读程序提高性能 2.InnoDB:支持ACID事务.行级锁.并发 3.Berkeley DB:支持事务 一个事务是一个连续的一组数据库操作,就好像它是一个单一的工作单元进行.换言之,永远不会是完整的事务,除非该组内的每个单独的操作是成功的.如果在事务的任何操作失败,则整个事务将失败. 实际上,会俱

mysql 事务处理

知识点: 事务处理是什么? 当数据库表呈树状机构设计时,我们对一个表进行增.删.改的操作,可能会要求对另外的表进行相同的操作,为了保证这多个sql能同时执行成功,就要使用mysql的事务处理. 注意:只有增删改的操作可以进行回滚,alter等操作不可行! 事务特性: 1.原子性:所有的sql执行操作必须全部成功,否则则回滚到处理前状态 2.一致性: 确保数据库正确地改变状态后,成功提交的事务. 3.隔离性: 使事务操作彼此独立的和透明的. 4.持久性: 确保提交的事务的结果或效果的系统出现故障的

MySQL事务处理实现方法步骤

需求说明: 案例背景:银行的转账过程中,发生意外是在所难免.为了避免意外而造成不必要的损失,使用事务处理的方式进行处理: A账户现有余额1000元,向余额为200的B账户进行转账500元.可能由于某原因: A账户在扣除转账金额时发生错误,使用事务回滚来返回到初始状态 A账户成功扣除转账金额后,B账户添加转账金额发生错误,使用事务回滚到初始状态 提示:先建数据表account,字段包括姓名(username).余额(money),再分别利用事务处理以上两种情况. [sql] view plain 

mysql事务处理失效原因

最近项目中使用mysql数据库时出现一个问题,运用spring的事务注解(@transaction)来管理事务时,发现用事务注解@transaction标记的方法里,如果出现异常,事务并没有回滚,而是直接提交到了数据库,刚开始还以为是spring的事务配置有问题,检查一遍后还是不行,最后查阅了很多资料才发现原来是mysql数据库存储引擎的问题. mysql有多种存储引擎,有些版本(mysql5.5.5以前默认是MyISAM,mysql5.5.5以后默认是InnoDB)安装时默认的存储引擎是MyI

php mysql 事务处理

MYSQL 的事务处理主要有两种方法. 1 .用 begin,rollback,commit 来实现 begin 开始一个事务 rollback 事务回滚 commit 事务确认    2 .直接用 set 来改变 mysql 的自动提交模式 MYSQL 默认是自动提交的,也就是你提交一个 QUERY ,它就直接执行!我们可以通过 set autocommit=0 禁止自动提交 set autocommit=1 开启自动提交 来实现事务的处理. 当你用 set autocommit=0 的时候,

MYSQL 事务处理常见有两种方法

MYSQL在操作大量的数据或者比较重要的数据的时候,事务处理很重要,比如银行的转账,支付,等等,作为开发人员事务是必须的一步. 1.用 BEGIN, ROLLBACK, COMMIT来实现 BEGIN 开始一个事务ROLLBACK 事务回滚COMMIT 事务确认 2.直接用 SET 来改变 MySQL 的自动提交模式: SET AUTOCOMMIT=0 禁止自动提交SET AUTOCOMMIT=1 开启自动提交 注意的是,在 MySQL 中只有使用了 Innodb 数据库引擎的数据库或表才支持事