mysql系列之(1)——事务和锁

数据库事务是指作为单个逻辑工作单元执行的一系列操作,要么完全执行,要么完全地不执行。

事务必须具备ACID四个特性:
原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚。
一致性是指事务必须使数据库从一个一致的状态变到另外一个一致的状态,也就是执行事务之前和之后的状态都必须处于一致的状态。
隔离性是指当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
持久性是指一个事务一旦被提交了,那么对于数据库中的数据改变就是永久性的,即便是在数据库系统遭遇到故障的情况下也不会丢失提交事务的操作。

提交事务:
>set autocommit = 0 禁止自动提交
>start transaction;
>update accout set money=money+100 where name="Jason";
>commit;
回滚事务:
>set autocommit = 0 禁止自动提交
>start transaction;
>update account set money=money-100 where name="justin";
>rollback;

1、事务的隔离级别

mysql修改事务的隔离级别
>set  [global | session]  transaction isolation level 隔离级别名称;
>set tx_isolation=’隔离级别名称;’
隔离级别:Serializable | Repeatable read | Read committed |Read uncommitted
设置默认级别是指当前session的下一个事务
设置session级别是指当前session以后的所有事务
设置global级别是指对之后的所有session,不包括当前session

四种隔离级别和可能产生的问题

不可重复读:Read committed读已提交事务的数据,可能是两次查询过程中间插入了一个事务更新的原有的数据,导致出现同一事务的不同实例先后读取的内容不同。在 Repeatable read通过数据库事务版本号方式解决。
事务1:
mysql> begin;
Query OK, 0 rows affected

mysql> select * from student where stu_id = 2;
+----+------+--------+
| id | name | stu_id |
+----+------+--------+
|  2 | 生物 |      2 |
+----+------+--------+
1 row in set
事务2:
mysql> begin;
Query OK, 0 rows affected

mysql> update student set name = '地理' where stu_id = 2;
Query OK, 1 row affected
Rows matched: 1  Changed: 1  Warnings: 0

mysql> commit;
Query OK, 0 rows affected
接下来事务1再次查询:
mysql> select * from student where stu_id = 2;
+----+------+--------+
| id | name | stu_id |
+----+------+--------+
|  2 | 地理 |      2 |
+----+------+--------+
1 row in set
上述过程可见,带事务1的一个事务中,两次请求得到了不同的结果,就导致了不可重复读的现象。
幻读:在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。
数据库默认隔离级别是 Repeatable read。

2、锁与事务

数据库采用两段锁协议(Two-Phase Locking――2PL)

在对任何数据进行读、写操作之前,首先要申请并获得对该数据的封锁,即扩展阶段。
在释放一个封锁之后,事务不再申请和获得其它任何封锁,即收缩阶段。
若所有事务均遵守两段锁协议,则这些事务的所有交叉调度都是可串行化的。

mysql的读操作,通常是不会加锁的(和隔离机制有关),也就是说通常的读操作是不加锁的,而是通过mvcc去解决的,对于通常的写请求,insert、update、delete通常会加行锁、间隙锁或表锁(这和索引是有关系的),这些锁通常是排他的,会阻塞其他的事务写事务。具体的情况需要结合隔离机制。

(1)、锁的类型:
共享锁(S Lock)和排他锁(X Lock)

意向共享锁(IS Lock):事务想要获取一张表中的某几行共享锁。

意向排他锁(IX Lock):事务想要获取一张表中的某几行的排它锁。

表锁:
对一整张表加锁,并发能力低下(即使有分读锁、写锁),一般在DDL处理时使用。
行锁:
只锁住特定行的数据,并发能力强,MySQL一般都是用行锁来处理并发事务。

在更新记录的时候会对此记录加行锁,在事务没有commit之前不会释放锁,所以事务2的更新会阻塞等待事务1的排它锁,当事务1Commit后,行锁释放事务2获得行锁,更新成功。
Gap锁(间隙锁):
是MySQL使用索引对行锁两边的区间进行加锁,避免其他事务在这两个区间insert的一种锁。


如图所示:数据库中存在值5,30。那么数据库会将数据段切分以下几个区间:
(negative infinity, 5],(5,30],(30,positive infinity)
select * from table where number=30 for update;
当对值为30这一行加行锁的时候,会同时对(5,30]和(30,positive infinity)加GAP锁。这样其他事务如果想在这两个区间进行insert操作的时候,需要等待本次事务完成。如果对不存在的数据进行更新,比如更新20(不存在)对应数据行,那么数据库也会对其存在的区间(5,30]加GAP锁。
Next-Key锁:
Next-Key锁是行锁和GAP锁的合并(MySQL使用它来避免幻读)。
当按照id(非唯一索引,不是主键)进行更新或删除的时候会先对id索引进行next_key锁,防止幻读,因为新增加的记录只能在30的左边和30的右边或者就是30。那么锁住范围后就能保证防止“幻读”。
注意:如果用到无索引的字段,那么MySQL会在存储引擎层面将所有的记录加锁,然后由MySQL Server过滤,如果不满足会调用unlock_row把不满足条件的记录释放锁(这里违背了二段锁协议)。

(2)、Mvcc(多版本并发控制):
Mvcc是多版本的并发控制协议,Innodb中的乐观锁实现。读不加锁,读写不冲突。通过它提高MySQL的读取操作的性能。并能解决MySQL的重复读问题。
MVVC在每一行记录的后面加两个隐含列(记录创建版本号和删除版本号)。这里的版本号指的是事务的版本号(每个事务启动的时候,都有一个递增的版本号)。在更新时进行版本号的递增,插入时新建一个版本号,同时旧版本数据存储在undo日志中。
  ● 比如插入一条记录(事务id为1)


  ● 如果把这条记录name更新为dog



在更新操作的时候,采用的是先标记旧的那行记录为已删除,并且删除版本号是事务版本号,然后插入一行新的记录的方式。


  ● 删除这条记录时



删除操作的时候,就把事务版本号作为删除版本号


执行查询操作需要符合如下规则才能被查出来


1) 删除版本号 大于 当前事务版本号,就是说删除操作是在当前事务启动之后做的。


2) 创建版本号 小于或者等于 当前事务版本号 ,就是说记录创建是在事务中(等于的情况)或者事务启动之前。

mvcc分为快照读和当前读。
快照读只是针对于目标数据的版本小于等于当前事务的版本号,也就是说读数据的时候可能读到旧的数据,但是这种快照读不需要加锁,性能很高。
当前读是读取当前数据的最新版本,但是更新等操作会对数据进行加锁,所以当前读需要获取记录的行锁,存在锁争用的问题。
下面是快照读和当前读的常见操作:
快照读:就是select * from table ....;
当前读:特殊的读操作(加共享锁或排他锁),插入/更新/删除操作,需要加锁。
select from table where ? lock in share mode;
select from table where ? for update;
其实Mysql实现的Mvcc并不纯粹,因为在当前读的时候需要对记录进行加锁,而不是多版本竞争。下面是具体操作时的Mvcc机制:
  1. SELECT时,读取创建版本号<=当前事务版本号,删除版本号为空或>当前事务版本号。
  2. INSERT时,保存当前事务版本号为行的创建版本号
  3. DELETE时,保存当前事务版本号为行的删除版本号
  4. UPDATE时,插入一条新纪录,保存当前事务版本号为行创建版本号,同时保存当前事务版本号到原来删除的行

总结:
RC提交读,采用行锁和MVCC的当前读,有幻读和不可重复读问题。RR可重读,采用Next-key(默认)和MVCC的快照读,无幻读和可重复读。
mysql的锁机制和事务隔离级别有关。并不是说所有的读操作都不加锁,写操作加锁,加什么锁也和索引类型、有无索引有关。

感谢分享:
http://www.jianshu.com/p/bcc614524024
http://www.jianshu.com/p/edbe22beaecb

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

推荐阅读更多精彩内容