数据库事务

原子性(Atomic)

原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚:如果在执行的过程中发生了错误,就把已经执行的操作恢复成没有执行之前的样子。


一致性(Consistency)

一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致状态。

比如用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱加起来应该还得是5000。


持久性(Durability)

持久性意味着该次转换对应的数据库操作所修改的数据都应该在磁盘种保留下来,无论之后发生了什么事故,本次转换造成的影响都不应该丢失。


隔离性(Isolation)

当数据库上有多个事务同时执行的时候,就可能出现脏读(dirty read)、不可重复读(non-repeatable read)、幻读(phantom read)的问题,为了解决这些问题,就有了“隔离级别”的概念。

隔离得越严实,效率就会越低,因此很多时候,都要在二者之间寻找一个平衡点。SQL 标准的事务隔离级别包括:

  • 读未提交是指,一个事务还没提交时,它做的变更就能被别的事务看到。
  • 读提交是指,一个事务提交之后,它做的变更才会被其他事务看到。
  • 可重复读是指,一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的。
  • 串行化,顾名思义是对于同一行记录,"写"会加"写锁","读"会加"读锁"。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。
mysql> create table T(c int) engine=InnoDB;
insert into T(c) values(1);
image.png

若隔离级别是"读未提交", 则 V1 的值就是 2,这时候事务 B 虽然还没有提交,但是结果已经被 A 看到了。因此,V2、V3 也都是 2。

若隔离级别是“读提交”,则 V1 是 1,V2 的值是 2。事务 B 的更新在提交后才能被 A 看到。所以, V3 的值也是 2。

若隔离级别是“可重复读”,则 V1、V2 是 1,V3 是 2。之所以 V2 还是 1,遵循的就是这个要求:事务在执行期间看到的数据前后必须是一致的。

若隔离级别是“串行化”,则在事务 B 执行“将 1 改成 2”的时候,会被锁住。直到事务 A 提交后,事务 B 才可以继续执行。所以从 A 的角度看, V1、V2 值是 1,V3 的值是 2。

开启事务的语法

显式启动事务语句
  • begin
    begin标志着开启一个事务。
  • start transaction
    start transaction 同样标志着开启一个事务,该语句后面可以跟1个或多个修饰符
    • READ ONLY 表明当前事务是一个只读事务
    • READ WRITE 表明当前事务是一个读写事务
显示提交事务

提交语句是 commit

事务回滚

如果写了几条语句发现某条语句写错了,可以使用回滚语句是 rollback 将数据库恢复到事务执行之前的样子。

自动提交
mysql> SHOW VARIABLES LIKE 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit    | ON    |
+---------------+-------+
1 row in set (0.00 sec)

可以看到默认值是 ON,也就是说在默认情况下,如果不是按照上面的方式显示开启一个事务,那么每条语句都是一个独立的事务。

隐式提交

当使用 beginstart 开启了一个事务,或者把系统变量 'autocommit' 设置为 OFF 时,事务就不会进行自动提交。但如果我们输入了某些语句,且这些语句会导致之前的事务悄悄地提交掉。那么这种特殊地语句而导致地提交称为隐式提交。

  • 定义或者修改数据库对象的数据定义语言
  • 隐式使用或者修改 MySQL 数据库中的表
  • 事务控制关于锁定的语句
  • 加载数据的语句
  • 关于MySQL复制的一些语句
  • 其他语句

实践

隔离级别的影响

在MySQL数据库中,支持上面四种隔离级别,默认的为可重复读(Repeatable read);
在Oracle数据库中,只支持 串行化(Serializable) 和 读已提交(Read committed) 这两种级别,其中默认的为 读已提交(Read committed) 级别。

在MySQL数据库中查看当前事务的隔离级别:

SELECT @@tx_isolation;
SELECT  @@transaction_isolation;     # 8.0+ 版本 

在MySQL数据库中设置事务的隔离级别:

set [glogal | session] transaction isolation level 隔离级别名称;
set tx_isolation='隔离级别名称;'
事务隔离级别 脏读 不可重复读 幻读
读未提交(read-uncommitted)
读已提交(read-committed)
可重复读(repeatable-read)
串行化(serializable)
CREATE database trans;
CREATE TABLE account
(
    id int NOT NULL,
    name char(128) NULL,
    balance int NULL
);

INSERT INTO `account` VALUES (1,'lilei',450),(2,'hanmei',16000),(3,'lucy',2400);
  • 读未提交

    # 设置隔离级别:
    mysql > set session transaction isolation level read uncommitted;
    
    # A客户端开始事务:
    mysql > start transaction;
    mysql> SELECT * FROM account;
    +----+--------+---------+
    | id | name   | balance |
    +----+--------+---------+
    |  1 | lilei  |     450 |
    |  2 | hanmei |   16000 |
    |  3 | lucy   |    2400 |
    +----+--------+---------+
    3 rows in set (0.00 sec)
    
    # B 客户端开始事务
    mysql> start transaction;
    mysql> UPDATE account set balance = balance - 50 where id = 1;
    mysql> SELECT * FROM account;
    +----+--------+---------+
    | id | name   | balance |
    +----+--------+---------+
    |  1 | lilei  |     400 |
    |  2 | hanmei |   16000 |
    |  3 | lucy   |    2400 |
    +----+--------+---------+
    3 rows in set (0.00 sec)
    
    # 虽然B的事务没有提交,但是A 已经可以查询到B更新后的数据
    mysql> SELECT * FROM account;
    +----+--------+---------+
    | id | name   | balance |
    +----+--------+---------+
    |  1 | lilei  |     400 |
    |  2 | hanmei |   16000 |
    |  3 | lucy   |    2400 |
    +----+--------+---------+
    3 rows in set (0.00 sec) 
    

    假设此时B回滚,此时客户端A执行update account set balance = balance - 50 where id = 1会发现 lilei 的balance没有变成350,居然是400。在应用程序中会觉得400-50=350。解决上面的问题就是"读已提交"隔离级别。

  • 读已提交

  • 可重复读

参考资料
[1] https://www.zhihu.com/question/31346392
[2] http://www.cnblogs.com/fjdingsd/p/5273008.html#3904071
[3] https://blog.csdn.net/whoamiyang/article/details/51901888

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容