在InnoDB中,锁是逐步获得的,因此发生死锁是可能的。发生死锁后,InnoDB一般都能自动检测到,并使一个事务释放锁并回退,另外一个事务获得锁,并继续完成事务。但在涉及外部锁,或涉及表锁的情况下,InnoDB并不能完全自动检测到死锁,这需要通过设置锁等待超时参数innodb_lock_wait_timeout来解决。

1. mysql锁机制

InnoDB存储引擎既支持行级锁(row-level locking),也支持表级锁,但默认情况下是采用行级锁。

表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。  行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。

innodb 行级锁 record-level  lock大致有三种:record lock, gap lock and Next-KeyLocks。

record lock 锁住某一行记录  gap lock 锁住某一段范围中的记录  next key lock 是前两者效果的叠加。

nnoDB实现了以下两种类型的行锁:

共享锁:允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁;

排他锁:允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排他写锁。

为了允许行锁和表锁共存,实现多粒度锁机制,InnoDB还有两种内部使用的意向锁(意向共享锁和意向排他锁)。这两种意向锁都是表锁。意向锁是InnoDB自动加的,不需要用户干预。  对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他锁;对于普通SELECT语句,InnoDB不会加任意锁。

事务可以通过以下语句显示给记录集加共享锁或者排他锁:

1

2

1

2SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE #共享锁 SELECT * FROM table_name WHERE ... FOR UPDATE #排他锁

InnoDB的行锁实现的特点:只有通过索引条件检索数据,InnoDB才会使用行级锁,否则,InnoDB将会使用表锁。因为MySQL的行锁是针对索引加的锁,  而不是针对记录加的锁,所以虽然是访问不同行的记录,但是如果是使用相同的索引建,是会出现锁冲突的。

对于键值在条件范围内但并不存在的记录,叫做间隙。InnoDB会对这个间隙加锁,这种锁机制就是所谓的间隙锁(Next-Key锁)。  InnoDB使用间隙锁的目的:一是为了防止幻读,二是为了满足其恢复和复制的需要。

InnoDB如何解决死锁问题的:

在InnoDB中,锁是逐步获得的,因此发生死锁是可能的。发生死锁后,InnoDB一般都能自动检测到,并使一个事务释放锁并回退,另外一个事务获得锁,并继续完成事务。但在涉及外部锁,  或涉及表锁的情况下,InnoDB并不能完全自动检测到死锁,这需要通过设置锁等待超时参数innodb_lock_wait_timeout来解决。

2. 数据库加锁分析

MySQL InnoDB存储引擎,实现的是基于多版本的并发控制协议——MVCC (Multi-Version Concurrency Control) (注:与MVCC相对的,  是基于锁的并发控制,Lock-Based Concurrency Control)。MVCC最大的好处,相信也是耳熟能详:读不加锁,读写不冲突。  在读多写少的OLTP应用中,读写不冲突是非常重要的,极大的增加了系统的并发性能,这也是为什么现阶段,几乎所有的RDBMS,都支持了MVCC。

在MVCC并发控制中,读操作可以分成两类:快照读 (snapshot read)与当前读 (current read)。快照读,读取的是记录的可见版本 (有可能是历史版本),  不用加锁。当前读,读取的是记录的最新版本,并且,当前读返回的记录,都会加上锁,保证其他事务不会再并发修改这条记录。

在一个支持MVCC并发控制的系统中,哪些读操作是快照读?哪些操作又是当前读呢?以MySQL InnoDB为例:

快照读:简单的select操作,属于快照读,不加锁。(当然,也有例外,下面会分析)

1

1select * from table where ?;

当前读:特殊的读操作,插入/更新/删除操作,属于当前读,需要加锁。

1

2

3

4

5

1

2

3

4

5select * from table where ? lock in share mode;

select * from table where ? for update;

insert into table values (…);

update table set ? where ?;

delete from table where ?;

所有以上的语句,都属于当前读,读取记录的最新版本。并且,读取之后,还需要保证其他并发事务不能修改当前记录,对读取记录加锁。  其中,除了第一条语句,对读取记录加S锁 (共享锁)外,其他的操作,都加的是X锁 (排它锁)。

2.1 事务隔离级别

对锁进行分析前必须要先了解事务隔离级别的关系

隔离级别

脏读(Dirty Read)

不可重复读(NonRepeatable Read)

幻读(Phantom Read)

未提交读(Read uncommitted)

可能

可能

可能

已提交读(Read committed)

不可能

可能

可能

可重复读(Repeatable read)

不可能

不可能

可能

可串行化(Serializable)

不可能

不可能

不可能

- 未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据

提交读(Read Committed):只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别 (不重复读)

可重复读(Repeated Read):可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读

串行读(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞

MySQL InnoDB默认使用的级别是可重复读级别(Repeatable read),查找命令如下

1

2

3

4

5

6

7

1

2

3

4

5

6

7mysql>select @@session.tx_isolation;

+------------------------+

| @@session.tx_isolation |

+------------------------+

| REPEATABLE-READ |

+------------------------+

1 row in set

脏读:当一个事务进行的操作还未提交时,另外一个事务读到了修改的数据,这就是脏读,但是RR级别事务避免了脏读。

不可重复读:是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。  那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。  这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。但是,RR级别是不会出现不一样的结果的,即使另一个事务提交了修改他也查不到变化。

幻读:第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,  这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。

2.2 sql语句加锁分析

1

2

3

4

5

6

1

2

3

4

5

6#SQL语句1

select * from table where id = 1;

#SQL语句2

update set age = age + 1 where id = 1;

#SQL语句3

update set age = age + 1 where id = 1 and nickname = 'hello';

首先我们可以确定的是语句1,他是不加锁的,属于快照读。语句2和语句3要复杂些,我们慢慢来分析。

下面我们默认事务级别为可重复读(Repeated Read),因为这是MySQL InnoDB默认级别。

语句2分析:

如果id是主键或者是索引的话,那么锁定的行只有符合条件的那几行。

如果id非索引,那么会锁表。

语句3分析:

id或者nickname只要有一个是索引或者是主键的话,那么锁住的行都是符合条件的行。

但是要注意一个情况,如果你查看索引数据值存在大量重复的数据的话(重复的数要是where条件值),那么有可能条件是不会走索引,而是进行全表查询,所以此时锁住的也是全表

因为索引扫描数超过30%时,会进行全表扫描

2.3

1

2

3

4

5

1

2

3

4

5#统计出单个用户领取该券的数量,上了悲观锁

select count(coup_id) as count_per from coupon_detail where coup_user_id = 10 and act_code = #{act_code} for update;

if(#{count_per} < #{coup_per_num}){

insert into coupon_detail values(1,act_code,'000000',10);

}

分析一下上面的select count语句可以发现他对coup_user_id = 10 and act_code = ‘000000’的数据上了锁,但是我们接下来要做的操作是insert操作,而不是update操作。  当两个事务刚进来的时候统计的数据都为0,也没办法给coup_user_id = 10 and act_code = ‘000000’的数据上锁,所以两个selec count for update 都能执行,  那么后面的insert操作也自然能成功,但是当有数据的时候,其中一个select for update会等待,这样的话就能成功。

这样的话悲观锁也是不行的,但是其实我们再回过头来想一下乐观锁为什么不行,是因为他分为了两个语句,而前面那个语句select count可能会读到脏数据,那么后面的利用某个字段去  update时判断值就有可能不对,那么如何保证统计的数据跟判断保持一致呢,因为mysql处理语句的时候是一条一条处理的,所以我们通过写成一条sql就可以达到前后数据一致问题。

此处我们使用insert的时候统计出当前领取数,并与可领取数进行对比,伪代码如下

1

2

1

2select * from coupon_activity where act_code = '000000';

insert into coupon_detail (coup_id,act_code,coup_code,coup_user_id) select (coup_id,act_code,coup_code,coup_user_id) from (select count(id) as num from coupon_detail where coup_user_id = 10 and act_code = '000000')temp where temp.num < #{coup_per_num}

上面这条复杂的sql在高并发时会发生死锁的情况,但是确能得到正确的结果。我们来分析一下死锁的情形。

上面这条语句最里面的select where coup-user-id = 10 and act-code = ‘000000’ 会锁住这一行数据,但是当数据库没有值的时候,就上不了锁,那么另外一个事务的select也能查询,  但是两个事务都对coup_user-id = 10 and act-code = ‘000000’上锁了,那么insert的时候两者都处于等待对方释放锁的状态,所以就发生了死锁,数据库解决死锁之后,只有一条数据  插入成功,这样也就得到了我们需要的结果。

MySQL并发 共享锁目的_mysql并发与锁机制相关推荐

  1. mysql 加快复制进程_MySQL并发复制进程演进

    MySQL5.5及以前的复制 一般主从复制有三个线程且都是单线程: Binlog Dump(主) --> IO Thread(从) -->  SQL Thread(从). 1.master ...

  2. mysql innodb 的锁机制_Mysql之Innodb锁机制详解

    InnoDB与MyISAM的最大不同有两点:一是支持事务(TRANSACTION):二是采用了行级锁.关于事务我们之前有专题介绍,这里就着重介绍下它的锁机制. 总的来说,InnoDB按照不同的分类共有 ...

  3. MySQL的存储引擎、事务和锁机制

    1.什么存储引擎 数据库存储引擎是数据库底层软件组织,数据库管理系统(DBMS)使用数据引擎进行创建.查询.更新和删除数据.不同的存储引擎提供不同的存储机制.索引技巧,锁定水平等功能,使用不同的存储引 ...

  4. mysql和mariadb对比_MySQL并发复制系列三:MySQL和MariaDB实现对比

    MariaDB 10通过@@binlog_commit_wait_count and @@binlog_commit_wait_usec 两个参数设置,既事务commit阶段的时候至少等binlog_ ...

  5. mysql并发插入死锁_MySQL: 并发replace into的死锁问题分析-阿里云开发者社区

    测试版本:MySQL5.6.23测试表: create table t1 (a int auto_increment primary key, b int, c int, unique key (b) ...

  6. mysql 原子自增_mysql自增锁_33

    自增锁也会导致死锁 自增锁 一个表一个自增列 auto_increment pk select max(auto_inc_col)from t for update 在事务提交前释放 其他所在事务提交 ...

  7. MySQL数据库InnoDB存储引擎中的锁机制--转载

    原文地址:http://www.uml.org.cn/sjjm/201205302.asp 00 – 基本概念 当并发事务同时访问一个资源的时候,有可能导致数据不一致.因此需要一种致机制来将访问顺序化 ...

  8. Java多线程(五) —— 线程并发库之锁机制

    参考文献: http://www.blogjava.net/xylz/archive/2010/07/08/325587.html 一.Lock与ReentrantLock 前面的章节主要谈谈原子操作 ...

  9. 7、 MySQL锁机制:数据库核心技术之一

    为了保证数据并发访问时的一致性和有效性,任何一个数据库都存在锁机制. 锁机制的优劣直接影响到数据库的并发处理能力和系统性能,所以锁机制也就成为了各种数据库的核心技术之一. 锁机制是为了解决数据库的并发 ...

最新文章

  1. ctime库函数的使用
  2. 用ajax写以随机数验证码,关于前端ajax请求url为何添加一个随机数
  3. Doxygen自动文档生成工具在Eclipse中的集成及使用举例
  4. python pip如何安装wheel文件?.whl(pip install [wheel])
  5. CSerialPort多线程串口编程工具详解
  6. 四大组建进程间通信--基础
  7. 字节输入流一次读取多个字节
  8. 一天一种设计模式之六-----工厂方法模式
  9. 新的android包格式,在Android中注册新文件类型
  10. 【python】编程语言入门经典100例--23
  11. paip.提升性能------服务器环境及编程语言架构选择
  12. Cocos2d lua 破解方案集合
  13. 计算机组成原理试题,计算机组成原理试卷A卷.doc
  14. TrueCrypt使用方法及详细教程介绍
  15. HTML5大数据可视化效果(二)可交互地铁线路图
  16. 计算机mod函数,MOD函数的公式语法及使用方法实例
  17. 解决No such file or directory: /turtlebot3/turtlebot3_description/urdf/turtlebot3_.urdf.xacro
  18. 红米k30 android版本,红米K30系列机型众多,傻傻分不清楚?看完这篇你就懂了
  19. 数据库及SQL语句入门教程
  20. 最后教一次:完美解决电脑上的流氓软件

热门文章

  1. 腾讯智维生态发展计划图扑软件正式加入,共同聚焦智能 IDC
  2. C++寻找100以内可被17整除的最大自然数
  3. 鉴客 Android Intent 用法全面总结
  4. [整理] 关于易语言论坛的一些考证
  5. iapd计算机软件,如何让iPad更强大 多款实用iPad软件推荐
  6. L2-031 深入虎穴(24分与25分的dfs)
  7. airodump-ng wlan0mon扫描不出wifi怎么办
  8. Pandas数据结构:Series定义和创建
  9. [luogu P3799] 妖梦拼木棒
  10. python爬取微博用户关注和粉丝的公开基本信息