本文目录一览:
- 1、如何在mysql 的存储过程中使用事务
- 2、数据库详解之事务
- 3、mysql中事务的主要应用场景是什么
- 4、Mysql中的事务是什么如何使用
- 5、Mysql数据库中,事务是指什么?如何使用该功能?
如何在mysql 的存储过程中使用事务
6.7 MySQL 事务与锁定命令
6.7.1 BEGIN/COMMIT/ROLLBACK 句法
缺省的,MySQL 运行在 autocommit 模式。这就意味着,当你执行完一个更新时,MySQL 将立刻将更新存储到磁盘上。
如果你使用事务安全表 (例如 InnoDB、BDB),通过下面的命令,你可以设置 MySQL 为非 autocommit 模式:
SET AUTOCOMMIT=0
在此之后,你必须使用 COMMIT 来存储你的更改到磁盘上,或者使用 ROLLBACK ,如果你希望忽略从你的事务开始所做的更改。
如果你希望为一系列语句从 AUTOCOMMIT 模式转换,你可以使用 START TRANSACTION 或 BEGIN 或 BEGIN WORK 语句:
START TRANSACTION;
SELECT @A:=SUM(salary) FROM table1 WHERE type=1;
UPDATE table2 SET summmary=@A WHERE type=1;
COMMIT;
START TRANSACTION 在 MySQL 4.0.11 中被加入;这是被推荐的开始一个特别(ad-hoc)事务的方式,因为这是 ANSI SQL 句法。
注意,如果你使用的是一个非事务安全表,更改会立刻被存储,不受 autocommit 模式状态的约束。
当你更新了一个非事务表后,如果你执行一个 ROLLBACK,你将得到一个错误 (ER_WARNING_NOT_COMPLETE_ROLLBACK) 作为一个警告。所有事务安全表将被恢复,但是非事务安全表将不会改变。
如果你使用 START TRANSACTION 或 SET AUTOCOMMIT=0,你应该使用 MySQL
二进制日志做备份以代替老的更新日志。事务处理被以一个大块形式存储在二进制日志中,在 COMMIT
上面,为了保护回滚的事务,而不是被存储的。查看章节 4.9.4 二进制日志。 如果您使用起动事务处理或集AUTOCOMMIT=0
,您应该使用MySQL 二进制日志为备份代替更旧的更新日志。 事务处理存储在二进制登录一大块,做,保证, 滚的事务处理不存储。 参见部分4
。9.4 二进制日志。
下列命令自动的结束一个事务 (就好像你在执行这个命令之前,做了一个 COMMIT):
命令 命令 命令
ALTER TABLE BEGIN CREATE INDEX
DROP DATABASE DROP TABLE RENAME TABLE
TRUNCATE
你可以使用 SET TRANSACTION ISOLATION LEVEL ... 改变事务的隔离级。查看章节 6.7.3 SET TRANSACTION 句法。
6.7.2 LOCK TABLES/UNLOCK TABLES 句法
LOCK TABLES tbl_name [AS alias] {READ [LOCAL] | [LOW_PRIORITY] WRITE}
[, tbl_name [AS alias] {READ [LOCAL] | [LOW_PRIORITY] WRITE} ...]
...
UNLOCK TABLES
LOCK TABLES 为当前线程锁定表。UNLOCK TABLES 释放当前线程拥有的所有锁定。当线程发出另一个 LOCK TABLES,或当与服务器的连接被关闭时,被当前线程锁定的所有表将被自动地解锁。
为了在 MySQL 4.0.2 使用 LOCK TABLES ,你必须拥有一个全局的 LOCK TABLES 权限和一个在相关表上的
SELECT 权限。在 MySQL 3.23 中,你对该表需要有 SELECT、insert、DELETE 和 UPDATE 权限。
使用 LOCK TABLES 的主要原因是,仿效事务处理或在更新表时得到更快的速度。此后会有更详细的描述。
如果一个线程在一个表上得到一个 READ 锁,该线程 (和所有其它线程) 只能从表中读取。如果一个线程在一个表上得到一个 WRITE 锁,那么只有拥有这个锁的线程可以从表中读取和写表。其它的线程被阻塞。
READ LOCAL 和 READ 之间的不同就在于,当锁被加载时,READ LOCAL 允许非冲突(non-conflicting) INSERT 语句执行。如果当你加载着锁时从 MySQL 外部操作数据库文件,这将仍不能被使用。
当你使用 LOCK TABLES 是地,你必须锁定所有你将使用的表,并且必须使用与你的查询中将使用的别名相同!如果你在一个查询中多次使用一个表(用别名),你必须为每一个别名获得一个锁。
WRITE 锁通过比 READ 锁有更高的权限,以确保更新被尽快地处理。这就意味着,如果一个线程获得一个 READ
锁,而同时另外一个线程请求一个 WRITE 锁,并发的 READ 锁请求将等待直到 WRITE 线程得到了锁并释放了它。你可以使用
LOW_PRIORITY WRITE 锁,当该线程在等待 WRITE 锁时,它将允许其它的线程获得 READ 锁。 你应该只使用
LOW_PRIORITY WRITE 锁,如果你确信这将是最后一次,当没有线程将拥有 READ 锁。
LOCK TABLES 工作如下:
以内部定义的次序排序所有被锁定的表 (从用户立场说,该次序是不明确的)。
如果一个表被以一个读锁和一个写锁锁定,将写锁放在读锁之前。
一次只锁定一个表,只到线程得到所有的锁定。
这个方案是为了确保,表锁定死锁释放。 对于这个模式你仍然有些其它事情需要知道:
如果你对一个表使用一个 LOW_PRIORITY WRITE 锁定,这就意味着,MySQL 将等待这个锁,直到没有线程请求一个 READ
锁。当线程得到了 WRITE 锁,并等待获得锁定表列表中的下一个表的锁定时,其它所有的线程将等待 WRITE
锁被释放。如果这在你的应用程序中会引起一个严重的问题,你应该考虑将你的某些表转换为事务安全表。
你可以使用 KILL 安全地杀死一个正在表锁定的线程。查看章节 4.5.5 KILL 句法。
注意,你不应该 锁定你正在对其使用 INSERT DELAYED 的表。这是因为,在这种情况下,INSERT 是通过单独的线程完成的。
通常,你不需要锁定任何表,因为所有单 UPDATE 语句都是原子的;其它的线程无法干扰当前执行的 SQL 语句。当你无论如何希望锁定表时,这里有一些情况:
如果你在一束表上运行许多操作,锁定你将要使用的表,这会更快一些。当然有不利的方面,其它线程将不能更新一个 READ
锁的表,并且没有其它线程要以读取一个 WRITE 锁的表。 在 LOCK TABLES 下,某些事运行得更快一些的原因是,MySQL
将不会转储清除被锁定表键高速缓冲,直到 UNLOCK TABLES 被调用 (通常键高速缓冲在每个 SQL 语句后都会被转储清除)。这将加速在
MyISAM 表上的插入、更新、删除。
如果你在 MySQL 中正在使用一个不支持事务的存储引擎,如果你希望能确保没有其它的线程会出现在一个 SELECT 和 一个 UPDATE 之间,你必须使用 LOCK TABLES 。下面的示例显示为了安全地执行,这里需要LOCK TABLES :
mysql LOCK TABLES trans READ, customer WRITE;
mysql SELECT SUM(value) FROM trans WHERE customer_id=some_id;
mysql UPDATE customer SET total_value=sum_from_previous_statement
- WHERE customer_id=some_id;
mysql UNLOCK TABLES;
不使用 LOCK TABLES,将可能发生在 SELECT 和 UPDATE 语句执行期间有另外一个线程可能在 trans 表中插入一行新记录。
通过使用递增更新 (UPDATE customer SET value=value+new_value) 或 LAST_INSERT_ID() 函数,你可以在很多情况下避免使用 LOCK TABLES。
你也可以使用用户级锁定函数 GET_LOCK() 和 RELEASE_LOCK() 解决一些情况,这些锁被保存在服务器上的一个哈希表中,并以
pthread_mutex_lock() 和 pthread_mutex_unlock() 实现以获得高速度。查看章节 6.3.6.2
辅助功能函数。
查看章节 5.3.1 MySQL 如何锁定表,以获取关于锁定方案的更多信息。
你可以使用 FLUSH TABLES WITH READ LOCK 命令以读锁锁定所有数据库中的所有表。查看章节 4.5.3 FLUSH 句法。如果你有一个可以及时建立文件快照的文件系统,例如 Veritas,这将是得到备份的非常方便方式。
注意:LOCK TABLES 不是事务安全的,在尝试锁定一个表之前,将自动地提交所有的活动事务。
6.7.3 SET TRANSACTION 句法
SET [GLOBAL | SESSION] TRANSACTION ISOLATION LEVEL
{ READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE }
设置全局的、整个会话或下一个事务的事务隔离级。
缺省行为是设置下一个(未启动的)事务的隔离级。如果你使用 GLOBAL
关键词,语句为所有在那个点上建立的新连接设置默认的全局事务隔离级。为了这样做,你需要有 SUPER 权限。使用 SESSION
关键词为当前连接所有将来执行的事务设置默认的事务隔离级。
你可以使用 --transaction-isolation=... 为 mysqld 设置默认的全局隔离级。查看章节 4.1.1 mysqld 命令行选项
数据库详解之事务
究竟什么是数据库的事务,为什么数据库需要支持事务,为了实现数据库事务各种数据库的是如何设计的。还是只谈理解,欢迎大家来讨论。
1. 数据库事务是什么
事务的定义,已经有太多文章写过,我就不重复了。我理解的事务就是用来保证数据操作符合业务逻辑要求而实现的一系列功能。换句话说,如果数据库不支持事务,上面业务系统的程序员就需要自己写代码保证相关数据处理逻辑的正确性。而数据库事务就是把一系列保证数据库处理逻辑正确性的通用功能在数据库内实现,并且尽量提高效率。
举个例子,数据库最开始普及就是在金融业,银行的存取款场景就是一个最典型的OLTP数据库场景,而事务就是设计用来保证类似场景的业务逻辑正确性的。
![事务的四个基本特性]()
**原子性**,如果你要给家人转账,必须在你的账户里扣掉100块,在家人账户里加上100块,这两笔操作需要一起完成,业务逻辑才是正确的。但是程序在做修改的时,肯定会有先后顺序,试想一下程序扣了你的钱,这个时候程序崩溃了,家人账户的钱没有加上。那这100块是不是消失了?你是不是要发疯?那么,就把这两笔操作放进一个事务里,通过原子性保证,这两笔操作要么都成功,要么都失败。这样才能保证业务逻辑的正确性。
**一致性**,有很多文章讲过一致性,但是很多人会把一致性跟原子性混在一起说。事务的一致性指的是指每一个事务必须保证执行之后所有库内的规则依旧成立。比如内外键,constraint,触发器等。举例来说,你在储蓄卡里有100元,理财账户里有100元,基金账户有100元,那么你在资产总和里会看到300元,这个300元必须是其他三个账户余额加在一起得到的。你在给家人转帐100元是从储蓄卡里转出去了100元,那么在数据库上可以通过创建触发器的方式,当储蓄卡余额账户减100元的同时,把资产总和也同步减去100,不然的话,就会出现逻辑上的错误,因为你已经转走了100块储蓄卡余额,实际资产总和应该是200,如果还是300,数据库状态就不一致了。所以实现事务的时候,必须要保证相关联的触发器以及其他所有的内部规则都执行成功,事务才能算执行成功。如果在减去资产总时出错,那么这笔转帐交易也不能成功。因为这样数据库就会进入不一致的状态。
那么这里跟原子性的区别到底在哪里呢?原子性是指个多个用户指令之间必须作为一个整体完成或失败,而一致性更多是数据库内的相关数据规则必须同时完成或失败。
**持久性**,最容易理解的一个,事务只要提交了,那么对数据库的修改就会保存下来不会丢了。简单来说,只要提交了,数据库就算崩溃了,重启之后你刚存的100块依然在你的账户里。
**隔离性**,每个事务相对于其他的事务是有一定独立性的,不能互相影响。因为数据库需要支持并发的操作来提高效率。在并发操作时,一定要通过操作之间的隔离来保证业务逻辑的正确性。比如,你转帐100块给家人,一系列操作的最后一步可能是输入验证码,这个时候转帐还没有完成,但是在数据库里你的账户对应的记录中已经减去100块,家人账户也加了100块,就等着验证码输入以后,事务提交,完成操作。那么,这个时候,家人通过手机银行能够查到这100块么?你的答案可能是不能,因为这样才符合业务逻辑,因为你的转帐操作还没有提交,事务还没有完成。那么数据库就应该保证这两个并发操作之间具有一定的隔离性。
那么到底应该隔离到什么程度呢?隔离性又分为4个等级:由低到高依次为Read uncommitted(读未提交)、Read committed(读提交)、Repeatable read(可重复读取)、Serializable(序列化),这四个级别可以逐个解决脏读、不可重复读、幻象读这几类问题。这些东西是什么意思?请有兴趣的小伙伴自行百度,很多文章都写的很清楚。
那么怎么理解不同的隔离等级呢,首先要理解并发操作,并发操作就是指有不同的用户同时对一个数据进行读、写操作,那么在这个过程中,每个用户应该看到什么数据才能保证业务逻辑的正确性呢? 如果是前面存取款的场景,我必须看到的是已经存进来的钱,也就是必须是已经提交的事务。而12306刷火车票呢,你可以看到有10张余票,但是在下单的时候告诉你票卖完了,因为同时有10个用户把票买掉了,你需要重新刷余票,这个也是可以接受的,也就是说我可以读到一些虚假的余票,这样在业务上也没有什么问题。那么在设计这两个不同系统时,就可以选择不同的事务隔离级别来实现不同的并发效果。不同的隔离等级就是要在系统的并发性和数据逻辑的严谨性之间做出的平衡。
2. 数据库如何实现事务
数据库实现事务会有多种不同的方式,但基本的原理类似,比如都需要对事务进行统一的编号处理,都需要记录事务的状态(是成功了还是失败了),都需要在数据存储的层面对事务进行支持,以明确哪些数据是被哪些事务、插入、修改和删除的。同时还会记录事务日志等,对事务进行系统化的管理以实现数据的原子性,一致性和持久性。
要实现事务的隔离性,最基础的就是通过加锁机制把并发操作适当的串行化来保证数据操作的正确逻辑。但是为了要保证系统具有良好的并发性能,必须要在实现事务隔离性时需要找到合理的平衡点。大部分数据库(包括Oracle,MySQL,Postgres在内)在做并发控制的时候都会采用MVCC(多版本并发控制)的机制来保证系统具有较高的并发性,不同数据库实现MVCC的具体方案也不尽相同,但其基本原理类似。
3. MVCC实现原理
所谓MVCC,就是数据库中的同一查询根据相关事务执行的先后顺序以及隔离级别的不同,可能会存在不同版本的结果,通过这样的手段来保证大部分查询操作不会被修改操作阻塞并保证数据逻辑的正确性。也就是数据库通过保存多个版本的数据( 历史 数据)来提高系统的并发查询能力。简单来说就是用存储空间来交换并发能力。下面以Postgres为例介绍一下MVCC的一种实现方式帮助大家理解这个重要的数据库概念。通过下面的图来解释Posrgres里最基本的数据可见性是如何实现多版本控制的。
![在这里插入图片描述]()
首先,Postgres里的每一个事务都有编号,这里可以简单理解为时间顺序编号,编号越大的事务发生越晚。然后,数据库里的每一行记录都会保存创建这条记录的事务号(Cre),也会在记录删除时保存删除这条记录的事务号(Exp),换句话说,只要Exp这里一列里记录了事务编号,就说明这条记录被删除了。那么一个事务应该能看见那些记录呢?Postgres里每一个事务都会保存一个当前系统的事务快照(Snapshot),这个快照里会保存事务创建时当前系统的最高(最晚)事务编号,以及目前还在进行中的事务编号。那么如上图所示的一个事务的快照里最高事务编号为100,目前正在进行的事务有25,50和75。那么对应左边数据记录,这6行数据的可见性就如同标注的一般:
第一行,Cre 30,没有删除,在100这个时间点,应该能看到。
第二行,Cre 50,没有删除,但是50这个事务还没有提交,正在进行中,所以看不见。
第三行,Cre 110,没有删除,但是100这个时间点110事务还没有发生,所以看不见。
第四行,Cre 30,Exp 80,在80的时候数据被删掉了,所以看不见。
第五行,Cre 30,Exp 75,在30的时候被创建,75时候被删掉了,但是75这个事务在100的时候还没有提交,所以这条记录在100的时候还没有删掉,所以看得见。
第六行,Cre30,Exp 110,在30的时被创建,110时候被删掉,但是在100时候,110还没有发生,所以看得见。
综上,就是这个事务对这六条记录的可见性,也就是一个数据版本。那么大家可以看一下如果另一个事务的快照里存的是最高事务编号为110,正在进行的事务为50,那么它能看到的数据应该是哪几行呢?同时大家也看到,Postgres里删除一行数据其实就是在这一行的Exp这个列记录一个删除事务的编号,相当于做了一个删除标记,而数据没有真正被删除,因此Postgres数据库需要定期做数据清理操作(Vacuum)。Pstgres的在现实场景里会比这里介绍的要复杂,因为我们这里假定所有的事务最终都是正确提交了,如果存在某些事务没有提交的情况,那么可见性就会更加复杂,这里不再展开了。
数据库事务是基本的数据库概念,之前已经有很多很好文章做过介绍,这里希望能把自己的理解用比较通俗的描述分享给大家,欢迎来讨论交流。
mysql中事务的主要应用场景是什么
事务的使用场景是很广泛的,举个简单的例子
有个买东西的网站,你下单买了某个货品,那么数据库中的订单表会插入一条你下单的记录,同时货物表需要把你所购买货品的数量进行更新,候新增订单和更新货品数量必须是一致的,不应该出现有人下了单但是货品数量不变,也不应该货品数量减少了而订单却没有新增这种情况发生
那么插入订单信息和更新货品数量就是一个完整的事务,要么都执行成功,如果两个操作中有任意一个操作失败,则整个流程就应该是失败的,已经执行的操作就应当回滚
在数据库中使用事务,可以保证多个数据库操作的一致性
这就是一个简单的事务的使用场景
Mysql中的事务是什么如何使用
事务能保证用户做的一系列动作,要么全部成功。如果有一个操作失败,就回退到修改前。 比如你要做下面几个操作,
1、删除表A中的某些记录
2、向B添加一些记录。
3、修改C表中的一些数据。
使用事务,如果1,2都成功了,3却失败了。就会回退到第1步执行前的样子,ABC表都没被修改。
Mysql数据库中,事务是指什么?如何使用该功能?
什么是事务? \x0d\x0a\x0d\x0a事务是逻辑上的一组操作,组成这组操作的各个单元,要不全都成功要不全都失败,这个特性就是事务 \x0d\x0a\x0d\x0a注意:mysql数据支持事务,但是要求必须是innoDB存储引擎 \x0d\x0a\x0d\x0a解决这个问题: \x0d\x0a\x0d\x0amysql的事务解决这个问题,因为mysql的事务特性,要求这组操作,要不全都成功,要不全都失败,这样就避免了某个操作成功某个操作失败。利于数据的安全 \x0d\x0a\x0d\x0a如何使用: \x0d\x0a\x0d\x0a(1)在执行sql语句之前,我们要开启事务 start transaction; \x0d\x0a\x0d\x0a(2)正常执行我们的sql语句 \x0d\x0a\x0d\x0a(3)当sql语句执行完毕,存在两种情况: \x0d\x0a\x0d\x0a1,全都成功,我们要将sql语句对数据库造成的影响提交到数据库中,committ \x0d\x0a\x0d\x0a2,某些sql语句失败,我们执行rollback(回滚),将对数据库操作赶紧撤销 \x0d\x0a\x0d\x0a(注意:mysql数据支持事务,但是要求必须是innoDB存储引擎) \x0d\x0amysql create table bank(name varchar(20),money decimal(5,1))engine=innodb defau \x0d\x0alt charset=utf8; \x0d\x0a\x0d\x0amysql inset into bank values('shaotuo',1000),('laohu',5000); \x0d\x0a\x0d\x0amysql select*from bank; \x0d\x0a+---------+--------+ \x0d\x0a| name | money | \x0d\x0a+---------+--------+ \x0d\x0a| shaotuo | 1000.0 | \x0d\x0a| laohu | 5000.0 | \x0d\x0a+---------+--------+ \x0d\x0a\x0d\x0a------没有成功“回滚”执行rollback \x0d\x0amysql start transaction; //开启事务 \x0d\x0aQuery OK, 0 rows affected (0.00 sec) \x0d\x0a\x0d\x0amysql update bank set money=money+500 where name='shaotuo'; \x0d\x0aQuery OK, 1 row affected (0.00 sec) \x0d\x0aRows matched: 1 Changed: 1 Warnings: 0 \x0d\x0a\x0d\x0amysql update bank set moey=money-500 where name='laohu'; \x0d\x0aERROR 1054 (42S22): Unknown column 'moey' in 'field list' \x0d\x0amysql rollback; //只要有一个不成功,执行rollback操作 \x0d\x0aQuery OK, 0 rows affected (0.01 sec) \x0d\x0a\x0d\x0amysql select*from bank; \x0d\x0a+---------+--------+ \x0d\x0a| name | money | \x0d\x0a+---------+--------+ \x0d\x0a| shaotuo | 1000.0 | \x0d\x0a| laohu | 5000.0 | \x0d\x0a+---------+--------+ \x0d\x0a------成功之后 进行commit操作 \x0d\x0amysql start transaction; //开启事务 \x0d\x0aQuery OK, 0 rows affected (0.00 sec) \x0d\x0a\x0d\x0amysql update bank set money=money+500 where name='shaotuo'; \x0d\x0aQuery OK, 1 row affected (0.01 sec) \x0d\x0aRows matched: 1 Changed: 1 Warnings: 0 \x0d\x0a\x0d\x0amysql update bank set money=money-500 where name='laohu'; \x0d\x0aQuery OK, 1 row affected (0.00 sec) \x0d\x0aRows matched: 1 Changed: 1 Warnings: 0 \x0d\x0a\x0d\x0amysql commit; //两个都成功后执行commit(只要不执行commit,sql语句不会对真实的数据库造成影响) \x0d\x0aQuery OK, 0 rows affected (0.05 sec) \x0d\x0a\x0d\x0amysql select*from bank; \x0d\x0a+---------+--------+ \x0d\x0a| name | money | \x0d\x0a+---------+--------+ \x0d\x0a| shaotuo | 1500.0 | \x0d\x0a| laohu | 4500.0 | \x0d\x0a+---------+--------+