MySQL5.X都已经发布好久了,但是还有很多人认为MySQL是不支持事务处理的,这不得不怪他们是孤陋寡闻的,其实,只要你的MySQL版本支持BDB或InnoDB表类型,那么你的MySQL就具有事务处理的能力。这里面,又以InnoDB表类型用的最多,虽然后来发生了诸如Oracle收购InnoDB等令MySQL不爽的事情,但那些商业上的斗争与技术无关,下面以InnoDB表类型为例简单说一下MySQL中的事务。 先来明确一下事务涉及的相关知识: 事务都应该具备ACID特征。所谓ACID是Atomic(原子性),Consistent(一致性),Isolated(隔离性),Durable(持续性)四个词的首字母所写,下面以“银行转帐”为例来分别说明一下它们的含义: 原子性:组成事务处理的语句形成了一个逻辑单元,不能只执行其中的一部分。换句话说,事务是不可分割的最小单元。比如:银行转帐过程中,必须同时从一个帐户减去转帐金额,并加到另一个帐户中,只改变一个帐户是不合理的。 一致性:在事务处理执行前后,数据库是一致的。也就是说,事务应该正确的转换系统状态。比如:银行转帐过程中,要么转帐金额从一个帐户转入另一个帐户,要么两个帐户都不变,没有其他的情况。 隔离性:一个事务处理对另一个事务处理没有影响。就是说任何事务都不可能看到一个处在不完整状态下的事务。比如说,银行转帐过程中,在转帐事务没有提交之前,另一个转帐事务只能处于等待状态。 持续性:事务处理的效果能够被永久保存下来。反过来说,事务应当能够承受所有的失败,包括服务器、进程、通信以及媒体失败等等。比如:银行转帐过程中,转帐后帐户的状态要能被保存下来。 再来看看哪些问题会用到事务处理: 这里不说“银行转帐”的例子了,说一个大家实际更容易遇到的“网上购书”的例子。先假设一下问题的背景:网上购书,某书(数据库编号为123)只剩最后一本,而这个时候,两个用户对这本书几乎同时发出了购买请求,让我们看看整个过程: 在具体分析之前,先来看看数据表的定义:
create table book ( book_id unsigned int(10) not null auto_increment, book_name varchar(100) not null, book_price float(5, 2) not null, #我假设每本书的价格不会超过999.99元 book_number int(10) not null, primary key (book_id) ) 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; 购书成功 -------------------------------------------------------------------------------
对于用户乙来说,他的动作稍微比甲慢一点点,其购买过程所触发的动作和甲相同: ------------------------------------------------------------------------------- 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) MYSQL的事务处理主要有两种方法。 1、用begin,rollback,commit来实现 begin 开始一个事务 rollback 事务回滚 commit 事务确认 2、直接用set来改变mysql的自动提交模式 MYSQL默认是自动提交的,也就是你提交一个QUERY,它就直接执行!我们可以通过 set autocommit=0 禁止自动提交 set autocommit=1 开启自动提交 来实现事务的处理。 当你用 set autocommit=0 的时候,你以后所有的SQL都将做为事务处理,直到你用commit确认或rollback结束。 注意当你结束这个事务的同时也开启了个新的事务!按第一种方法只将当前的作为一个事务! 个人推荐使用第一种方法! MYSQL中只有INNODB和BDB类型的数据表才能支持事务处理!其他的类型是不支持的! ***:一般MYSQL数据库默认的引擎是MyISAM,这种引擎不支持事务!如果要让MYSQL支持事务,可以自己手动修改: 方法如下:1.修改c:\appserv\mysql\my.ini文件,找到skip-InnoDB,在前面加上#,后保存文件。 2.在运行中输入:services.msc,重启mysql服务。 3.到phpmyadmin中,mysql->show engines;(或执行mysql->show variables like 'have_%'; ),查看InnoDB为YES,即表示数据库支持InnoDB了。 也就说明支持事务transaction了。 4.在创建表时,就可以为Storage Engine选择InnoDB引擎了。如果是以前创建的表,可以使用mysql->alter table table_name type=InnoDB; 或 mysql->alter table table_name engine=InnoDB;来改变数据表的引擎以支持事务。 */ /*************** transaction--1 ***************/ $conn = mysql_connect('localhost','root','root') or die ("数据连接错误!!!"); mysql_select_db('test',$conn); mysql_query("set names 'GBK'"); //使用GBK中文编码; //开始一个事务 mysql_query("BEGIN"); //或者mysql_query("START TRANSACTION"); $sql = "INSERT INTO `user` (`id`, `username`, `sex`) VALUES (NULL, 'test1', '0')"; $sql2 = "INSERT INTO `user` (`did`, `username`, `sex`) VALUES (NULL, 'test1', '0')";//这条我故意写错 $res = mysql_query($sql); $res1 = mysql_query($sql2); if($res && $res1){ mysql_query("COMMIT"); echo '提交成功。'; }else{ mysql_query("ROLLBACK"); echo '数据回滚。'; } mysql_query("END"); /**************** transaction--2 *******************/ /*方法二*/ mysql_query("SET AUTOCOMMIT=0"); //设置mysql不自动提交,需自行用commit语句提交 $sql = "INSERT INTO `user` (`id`, `username`, `sex`) VALUES (NULL, 'test1', '0')"; $sql2 = "INSERT INTO `user` (`did`, `username`, `sex`) VALUES (NULL, 'test1', '0')";//这条我故意写错 $res = mysql_query($sql); $res1 = mysql_query($sql2); if($res && $res1){ mysql_query("COMMIT"); echo '提交成功。'; }else{ mysql_query("ROLLBACK"); echo '数据回滚。'; } mysql_query("END"); //事务处理完时别忘记mysql_query("SET AUTOCOMMIT=1");自动提交 /******************对于不支持事务的MyISAM引擎数据库可以使用表锁定的方法:********************/ //MyISAM & InnoDB 都支持, /* LOCK TABLES可以锁定用于当前线程的表。如果表被其它线程锁定,则造成堵塞,直到可以获取所有锁定为止。 UNLOCK TABLES可以释放被当前线程保持的任何锁定。当线程发布另一个LOCK TABLES时,或当与服务器的连接被关闭时,所有由当前线程锁定的表被隐含地解锁。 */ mysql_query("LOCK TABLES `user` WRITE");//锁住`user`表 $sql = "INSERT INTO `user` (`id`, `username`, `sex`) VALUES (NULL, 'test1', '0')"; $res = mysql_query($sql); if($res){ echo '提交成功。!'; }else{ echo '失败!'; } mysql_query("UNLOCK TABLES");//解除锁定
相关推荐
关于mysql的事务处理 public static void StartTransaction(Connection con, String[] sqls) throws Exception { if (sqls == null) { return; } Statement sm = null; try { // 事务开始 System....
本文实例讲述了php实现mysql事务处理的方法。分享给大家供大家参考。具体分析如下: 要实现本功能的条件是环境 mysql 5.2 /php 5 支持事务的table 类型,需要InnoDB,有了这些条件你就可以做上面的实现了,这个事物回滚...
在PHP中使用MySQL事务,通常涉及到几个关键函数:`mysql_query()`、`mysql_rollback()` 和 `mysql_commit()`。下面通过示例代码来详细说明: ```php // 开启事务 mysql_query("SET AUTOCOMMIT=0"); mysql_query(...
### 基于MySQL和PHP的分布式事务处理 #### 一、引言 随着互联网技术的发展,越来越多的应用程序需要跨越多个服务器或者多个地理位置的数据中心来处理数据。这导致了分布式事务处理的需求日益增长。分布式事务处理...
### PHP与MySQL事务处理示例 #### 示例1:使用BEGIN/COMMIT/ROLLBACK ```php $conn = mysql_connect('localhost', 'root', 'root') or die("连接失败!!!"); mysql_select_db('test', $conn); mysql_query("set ...
接下来,我们来看PHP下PDO的MySQL事务处理实例。以下是几个关键步骤: 1. **关闭自动提交**:在开始事务之前,需要关闭自动提交模式。在PDO中,可以通过设置`PDO::ATTR_AUTOCOMMIT`属性为0来实现。例如: ```php ...
本文将深入探讨如何解决在高并发条件下,使用PHP进行加锁事务处理的问题,以确保数据的正确性和唯一性。 首先,让我们回顾一下问题背景。假设我们需要在数据库中插入一条数据,条件是当`username`字段为`'mraz'`时...
MySQL还支持事务处理,确保数据的一致性和完整性。 在构建【PHP+MYSQL网站】时,首先需要设计数据库架构,创建所需的表和字段,定义数据类型和约束。然后,使用PHP编写前端页面,处理用户的请求,并通过PHP的MySQLi...
将详细介绍PDO(PHP Data Objects)和mysqli扩展,它们是PHP连接MySQL的主要方式,包括预处理语句、事务处理等安全高效的数据库操作技巧。 4. Web应用开发:通过实际案例,书中将展示如何使用PHP和MySQL开发用户...
5. **事务处理**:使用PHP进行事务控制,确保数据的一致性。 **学习资源** "PHP 6与MySQL 5基础教程(网上文件)"这个文档很可能包含了关于这两者的基础教学,涵盖了从安装配置到实际应用的方方面面。初学者可以...
4. PHP操作MySQL:插入和更新数据、查询记录、删除数据、事务处理以及错误处理。 5. PHP动态网页:利用PHP从MySQL数据库获取数据,生成动态HTML内容,实现交互式功能,如用户注册、登录、评论系统等。 6. PHP表单...
3. PHP与MySQL交互:连接数据库、执行查询、处理结果集、事务处理、预编译语句,确保数据安全。 4. 高级PHP:面向对象编程、错误和异常处理、文件系统操作、会话管理、邮件发送等。 5. 高级MySQL:视图、存储过程、...
在结合PHP和MySQL进行Web开发部分,本书涵盖了连接和断开数据库、执行SQL查询、处理结果集以及事务管理等内容。此外,还讨论了如何使用PHP的预处理语句防止SQL注入攻击,确保应用程序的安全性。 随着Web应用复杂性...
结合PHP和MySQL,本书将深入探讨如何使用PHP连接和操作MySQL数据库,包括PDO(PHP Data Objects)和mysqli扩展的使用,以及预处理语句和事务处理,这些都是确保数据安全性和一致性的关键技术。此外,还会介绍如何...
是PHP在处理MySQL数据库连接时所依赖的关键组件。 标签 "dll PDO MYSQL php" 为我们指出了关键元素:DLL(动态链接库)、PDO(PHP数据对象)、MYSQL(MySQL数据库)以及PHP编程语言。这些标签说明了讨论的内容集中...
MySQL的核心特性包括事务处理、ACID(原子性、一致性、隔离性和持久性)属性、多种存储引擎(如InnoDB用于事务处理,MyISAM用于读取密集型应用)以及高效的索引机制。 三、PHP与MySQL的交互 PHP通过MySQLi或PDO_...
MySQL支持事务处理,具有高可用性和可扩展性,能够满足从小型到大型网站的数据存储需求。 PHP与MySQL的结合使用,使得开发者可以构建功能丰富的交互式Web应用程序。以下是它们结合开发的一些关键知识点: 1. **...