MySQL数据库事务及其原理

基本概念

MySQL 事务主要用于处理操作量大,复杂度高的数据。

银行转账是经典的解释事务的例子。用户A给用户B转账5000元主要步骤可以概括为如下两步。
  第一,账户A账户减去5000元;
  第二,账户B账户增加5000元;
  这两步要么成功,要么全不成功,否则都会导致数据不一致。这就可以用到事务来保证,如果是不同银行之间的转账还需要用到分布式事务。

事务的特性(ACID)

(1)在 MySQL 中只有使用了 Innodb 数据库引擎的数据库或表才支持事务。

(2)事务处理可以用来维护数据库的完整性,保证成批的 SQL 语句要么全部执行,要么全部不执行。

(3)事务用来管理 insert,update,delete 语句。

原子性:构成事务的的所有操作必须是一个逻辑单元,要么全部执行,要么全部不执行。

一致性:在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则。

隔离性:并发事务之间不会相互影响。

持久性:事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。事务执行成功后必须全部写入磁盘。


事务隔离性的实现原理

数据库事务会导致脏读、不可重复读、幻读等问题。

1、脏读:事务还没提交,他的修改已经被其他事务看到。

2、不可重复读:在一个事务内读取到了表中的某一行数据,多次读取结果不同。

3、幻读:同一个事务突然发现他以前没发现的数据。幻读是前后读取到表中的记录总数不一样,读取到了其它事务插入的数据。

事务的隔离用是通过锁机制实现的,不同于MyISAM使用表级别的锁,InnoDB采用更细粒度的行级别锁,提高了数据表的性能。InnoDB的锁通过锁定索引来实现,如果查询条件中有主键则锁定主键,如果有索引则先锁定对应索引然后再锁定对应的主键(可能造成死锁),如果连索引都没有则会锁定整个数据表。

MyISAM类型不支持事务处理等高级处理,而InnoDB类型支持。MyISAM类型的表强调的是性能,其执行数度比InnoDB类型更快,但是不提供事务支持,而InnoDB提供事务支持以及外部键等高级数据库功能。

数据库锁

1、悲观锁(更新多,查询少时用)
如果我们采用悲观锁。就是我们在操作数据库时采用悲观的态度,认为别人会在此时并发访问数据库。我们在查询语句中select * from account where name=‘aaa’ for update; 等于加了排它锁。当A查询余额的时候,select money from account where name=‘aaa’ forupdate; 增加了排它锁,B查询账户余额的时候, select money from account wherename=‘aaa’ forupdate; 也要求对数据库加排它锁,因为A已经拿到了排它锁,导致B不能加锁,所以B只有等待A执行完毕,释放掉锁以后才能继续操作。

2、乐观锁(更新少,查询多时用)
如果我们采用乐观锁,就是我们在操作数据库的时候会认为没有其它用户并发访问,但是乐观锁也不是完全乐观的,乐观锁是采用版本号的方式进行控制的。在数据库表中有一列版本号。从数据库中查询的时候,将版本号也查询过来,在进行更新操作的时候,将版本号加1,查询条件的版本号还是查询过来的版本号。比如,A执行查询操作的时候,selectmoney,version from account where name=‘aaa’; 假设此时查询到的版本号为0,A在进行更新操作的时候 update account set money=money+100, version=version+1where name=‘aaa’ and version=0; 未提交时B来查询,查询到的版本号依然是 0,B也执行更新操作update account set money=money+100,version=version+1 wherename=‘aaa’ and version=0;现在A提交了事务,B再提交事务的时候发现版本号为 0的记录没有了,所以就避免了数据丢失的问题。不过这种情况也导致了多个用户更新操作时,只有一个用户的更新被执行。

3、行级锁(为某一条记录加锁)
如果想对数据库中的某条记录加行级锁,那么 where 条件后面必须为索引列。否则 for update加的都是表级锁。行级锁就是只对要访问的当前行加锁,其他用户访问其它行记录的时候可以访问。 select * from accountwhere id=1 for update;

4、表级锁(为一张表加锁)
在查询语句后增加 for update 时,where 条件后不是索引列,那么此时都是表级锁。select * fromaccount where name=‘aaa’ for update;

原子性、一致性和持久性实现原理

原子性、稳定性和持久性是通过redo 和 undo 日志文件实现的,不管是redo还是undo文件都会有一个缓存我们称之为redo_buf和undo_buf。同样,数据库文件也会有缓存称之为data_buf。

undo记录了数据在事务开始之前的值,当事务执行失败或者ROLLBACK时可以通过undo记录的值来恢复数据。

redo日志记录数据修改后的值,可以避免数据在事务提交之前必须写入到磁盘的需求,减少I/O。

事务操作

BEGIN; //开始事务,挂起自动提交
insert into t_cart_shopcart (user_id, sku_id, amount, shop_id,  status) values(10001, 10001, 1, 10001, 0);
insert into t_cart_shopcart (user_id, sku_id, amount, shop_id,  status) values(10001, 10002, 1, 10001, 0);
COMMIT; //提交事务,恢复自动提交
set autocommit = 0; //挂起自动提交
insert into t_cart_shopcart (user_id, sku_id, amount, shop_id,  status) values(10001, 10001, 1, 10001, 0);
insert into t_cart_shopcart (user_id, sku_id, amount, shop_id,  status) values(10001, 10002, 1, 10001, 0);
COMMIT; //提交事务
set autocommit = 1; //恢复自动提交
发布了150 篇原创文章 · 获赞 280 · 访问量 32万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 黑客帝国 设计师: 上身试试

分享到微信朋友圈

×

扫一扫,手机浏览