SlideShare a Scribd company logo
1 of 48
资源竞争与并发控制         (1.5小时)




            李伟    搜狐视频
     weiweili@sohu-inc.com
导航
• 1.应用层并发控制?不是重点
• 2.Lost Update?乐观锁与悲观锁
• 3.隔离级别:脏读Dirty Read+幻读Phantom
  Read?
• 4.InnoDB锁与多版本控制?
• 5.意向锁?
• 6.事务传播行为?
应用层:CAS原子操作?
应用层:单例( Singleton )




注:ClassLoader层面的问题不考虑,只考虑多线程范围内的单例。
类似的CAS问题:机票自助选座

                                                    多个
                                                  AppServ
                                                  er怎办?
                                                  持久层?



select status from seat where id={:id}
update seat set status={:status} where id={:id}
导航
• 1.应用层并发控制?不是重点
• 2.Lost Update?乐观锁与悲观锁
• 3.隔离级别:脏读Dirty Read+幻读Phantom
  Read
• 4.InnoDB锁与多版本控制?
• 5.意向锁?
• 6.事务传播行为?
源代码管理:乐观锁与悲观锁
• CAS问题=>Lost Update Problem
    – CVS|SVN(乐观锁):Version Compare
    – VSS(悲观锁):编辑前先LOCK代码文件
      • Microsoft Virtual SourceSafe
•   乐观锁:其他用户同时修改你正在修改的数据的概率很小
     • 读的时候不锁,提交的时候才锁
     • 优势:并发度高
     • 劣势:不能防止问题发生,但能检测到问题的发生


•   悲观锁:其他用户同时修改你正在修改的数据的概率很大
     • 读的时候就锁,提交的时候才释放,期间别人无法读和提交
     • 优势:能防止问题发生
     • 劣势:并发度低
机票选座解决1:乐观锁
• Update的时候增加“版本对比”
  – update seat set status={:1} where
    id={:id}
  – update seat set status={:1} where
    id={:id} and status={:previous version
    value, suppose 0}

• 乐观锁:不能防止问题发生,只能检测。
• 乐观锁:并发高峰期(假设打破),产生“抖动”,用户有种被欺
   骗的感觉
• Version Compare REFER (版本是一个抽象的概念):
http://jeffkemponoracle.com/2005/10/21/avoiding-lost-
   updates-protecting-data-in-a-multi-user-environment/
机票选座解决2:悲观锁
• Select的时候加“锁”
 – select status from seat where id={:id}
   WITH LOCK(伪代码)
 – update seat set status={:1} where
   id={:id}

• 互动思考=》先看“事务隔离级别”,后回到这些问题
  • InnoDB的Select是否是加锁的?
  • 数据库的读操作和写操作是互斥的吗?
  • WITH LOCK(伪代码)选:读锁(共享锁)vs. 写
    锁(排它锁)?
  • 上述两条语句是在一个事务内vs.两个事务内?
导航
• 1.应用层并发控制?不是重点
• 2.Lost Update?乐观锁与悲观锁
• 3.隔离级别:脏读Dirty Read+幻读Phantom
  Read
• 4.InnoDB锁与多版本控制?
• 5.意向锁?
• 6.事务传播行为?
[w1]悲观锁和乐观锁




              并发问题与事务隔离级别
         并发问题                 脏读(Dirty Read)   不 可复读( Non-Repeatable   幻 读 ( Phantom   丢失更新(Lost Update)
       隔离级别                                    Read)                   Read)




       读 未 提 交 ( Read
       Uncommitted)
                                    可能                 可能                  可能                可能


       提 交 就 读 ( Read
       Committed)
                                   不可能                 可能                  可能                可能


       可 重 复 读 ( Repeatable
       Read)
                                   不可能                不可能                  可能                可能


       串行化(Serializable)

                                   不可能                不可能                 不可能                可能




     注1: 黄色表示“问题”;绿色表示“方案”。
     注2: ANSI/ISO SQL定义标准,不同数据库产品适当实现,InnoDB全实现了。
     注3: http://en.wikipedia.org/wiki/Isolation_%28computer_science%29
实验准备:表+隔离级别设置
员工表(编号ID,姓名)
并发问题1:脏读(Dirty
      Read)
• 大论战:是否应该读取其他事务“未提交” 的
  数据(Read Uncommitted vs.
  Committed)
并发问题1:脏读(Dirty
       Read)
• Read Uncommitted vs. Committed ?
• 脏读(Dirty Read):一个事务读到了其他
  “尚未提交” 的数据修改,包括:
  (Update,Insert,Delete)

• 隔离级别-Read Committed: 要求只有/只
  要“事务提交”了,才/就能读到数据修改。
并发问题2:不可重读(Non-
  repetable Read)
并发问题2:不可重读(Non-
    repetable Read)
• 不可重读:在同一个事务内,前后两次读的
  数据不一样(相同查询语句)。

• 隔离级别-Read Committed: 要求只有/只
  要“事务提交”了,才/就能读到数据修改。
• 隔离级别-Repeatable READ: 要求只有
  “事务提交”了,才能读到数据修改。但并不
  是“只要提交了,就一定能读到”(关于
  timepoint)。
并发问题3.1:幻读
               (Phantom)




http://en.wikipedia.org/wiki/Isolation_%28computer_science%29#Phanto
m_reads
并发问题3.1:wiki错了!
时序   Session A                                     Session B                                      解说

1    mysql> start transaction;                                                                    隔 离 级 别           都   是   :
     mysql> select * from employee;                                                               REPEATABLE_READ
     /*读到Alice和Bob两记录*/




2                                                  mysql> start transaction;                      插入ID=3的记录
                                                   mysql> insert into employee values(3,'BBB');
                                                   mysql> commit;




3    mysql> select * from employee;                                                               没有出现wikipedia说的:“幻读”
     /* 读到Alice和Bob两记录*/




4    mysql> select * from employee where id = 3;
     Empty set (0.00 sec)
并发问题3.2:幻读
                                            强调:修
       (Phantom)                              改




REFER: http://cupoy.iteye.com/blog/251796
并发问题3.2:网友错了!
时序   Session A                                  Session B                                      解说
1    mysql> start transaction;                                                                 隔 离 级 别           都   是   :
     mysql> select * from employee;                                                            REPEATABLE_READ
     /*读到Alice和Bob两记录*/




2    mysql> update employee set name = 'AAA';
     Rows matched: 2 Changed: 2 Warnings: 0




3                                               mysql> start transaction;                      没有出现网友说的:“幻读”,压
                                                mysql> insert into employee values(3,'333');   根不能插入
                                                ERROR 1205 (HY000): Lock wait timeout
                                                exceeded; try restarting transaction




4    mysql> select * from employee;
     /*读到Alice和Bob两记录*/
并发问题3:真的幻觉了
时序   Session A                                           Session B                                      解说
1    mysql> start transaction;                                                                          隔 离 级 别           都   是   :
     mysql> select * from employee;                                                                     REPEATABLE_READ
     /*读到Alice和Bob两记录*/



2                                                        mysql> start transaction;                      插入ID=3的记录
                                                         mysql> insert into employee values(3,'BBB');
                                                         mysql> commit;




3    mysql> select * from employee;                                                                     没有出现wikipedia说的:“幻读”
     /* 读到Alice和Bob两记录*/



4    mysql> select * from employee where id = 3;
     Empty set (0.00 sec)



5    mysql> insert into employee values(3,'3 From A');                                                  ID=3的到底是有,还是没有?幻
     ERROR 1062 (23000): Duplicate entry '3' for key                                                    觉了!
     'PRIMARY'
导航
• 1.应用层并发控制?不是重点
• 2.Lost Update?乐观锁与悲观锁
• 3.隔离级别:脏读Dirty Read+幻读Phantom
  Read
• 4.InnoDB锁与多版本控制?
• 5.意向锁?
• 6.事务传播行为?
实现1-多版本控制:wiki为什么错
InnoDB在默认隔离级别(Repeatable Read)
 下,实现了“一致性读”:同一个事务内,前后两
 次执行相同的查询,结果集是一致的,即使两次
 查询之间其他事务提交了新的数据修改。并且,
 它没有使用“锁”手段,而使用的是“多版本控制”手
 段:
    InnoDB会给第一次的查询做一个快照
 (snapshot),并且会分配一个与快照相关的
 时间点(timepoint),然后保证其他并发事务
 提交的数据更新,只有当提交的时间点
 (timepoint)发生在快照的时间点之前,才能
 被本事务(快照所在的事务)看见。
实现1-多版本控制:刷新快照
– 提交当前事务,然后开始一个新的事务,并查询
  (因为一致性读说的是“在同一事务内,前后两次执
  行要一致”,出了这个事务,就不用保证了);
– 采用Locking Reads,也就是说用SELECT…
  LOCK IN SHARE MODE或SELECT … FOR
  UPDATE。原因:Locking Reads是要锁住被选择
  的记录集的,这样其他事务压根就不可能修改这些
  数据了,这样连“幻读”都能避免,更不用说“可重读”
  了,因此就没必要再用快照来保证“一致性读”了。
  简单的说,用Locking Reads来实现“一致性读
  (可重读)”是比Consistent Read/None Lock
  (即:多版本控制)更为严格的手段,代价是降低
  了并发度。
实现1-多版本控制:新旧混合体

其他事务提交的修改,如果timepoint晚于快
照的timepoint,则不可见(见“旧数据”);但
本事务在两次SELECT之间做的修改,是可见
的(见“新数据”)。

数据库状态变迁:从一个事务到另一个事务的
变迁。“新旧混合体”具有过渡性,是数据库不
会到达的状态。
实现2-间隙锁:网友为什么错
1. 锁的不仅仅是结果集的行,还包括间隙
  update employee set name = 'AAA';
  如果仅仅锁住Alice和Bob两条记录,插入
  ID=3的应该是可以,但InnoDB不这样。

2. InnoDB的行锁不是真正的行锁:next-key
  (1)index-row locking,基于索引/主键;
   (2) gap locking,还要锁住间隙。
实现2-index-row:基于索引
时序    Session A                               Session B                                 解说
1.1   mysql> start transaction;                                                         隔 离 级 别 都 是 :
      mysql> update employee set name='AAA'                                             REPEATABLE_READ
      where id=1;
      /*ID=1记录更新为AAA*/



1.2                                           mysql> start transaction;                 ID=1锁住;
                                              mysql> update employee set name='BBB'     ID=3 没锁;
                                              where id=1;                               表明:InnoDB“行锁”
                                              ERROR: Lock wait timeout
                                              mysql> insert into employee values(3,'3
                                              From B');
                                              Query OK, 1 row affected
                                              mysql> commit



                                      删除ID主键后,重做上面的实验

2.1   mysql> start transaction;
      mysql> update employee set name='AAA'
      where id=1;
      /*ID=1记录更新为AAA*/

2.2                                           mysql> start transaction;                 ID=1锁住;
                                              mysql> update employee set name='BBB'     ID=3锁住;
                                              where id=1;                               表明:InnoDB“表锁”
                                              ERROR: Lock wait timeout
                                              mysql> insert into employee values(3,'3
                                              From B');
                                              ERROR: Lock wait timeout
实现2-gap:锁住间隙




1.ID是主键;表中有两条记录。
2.保持跟官方例题一样:
 http://dev.mysql.com/doc/refman/5.0/en/innodb-next-
 key-locking.html
实现2-gap:锁住间隙
时序   Session A                                      Session B                                        解说
1    mysql> start transaction;                      mysql> start transaction;                        隔 离 级 别 都         是   :
     mysql> select * from employee where id > 100                                                    REPEATABLE_READ
     for update; /*读到ID=101记录*/                                                                      执行Update也可




2                                                   mysql> update employee set name = 'V101 A'       ID=101锁住;
                                                    where id = 101; //ERROR: Lock wait               ID=102(间隙)锁住;
                                                    mysql>        insert      into      employee     锁住范围:(90,+无穷)
                                                    values(102,'V102');//Lock wait
                                                    mysql> insert into employee values(91,'V191');
                                                    //LOCK
                                                    mysql> update employee set name = 'V90 A'
                                                    where id = 90;
                                                    Rows Changed: 1 //OK
                                                    mysql> insert into employee values(89,'V89');
                                                    //OK
                                                    mysql> commit




3    mysql> select * from employee where id > 100
     for update; /*读到101记录*/
     mysql> select * from employee (for update);
     89=V89; 90=V90 A; 101=V101
Wiki和网友没错,定义不同
• 1、wiki和网友关于“幻读”的理解没错,只是
  InnoDB的实现机制跟ISO/ANSI SQL92标
  准的定义有些不一样。
• 2、InnoDB主要依靠“多版本控制”实现一致
  性读,而不用锁。
• 3、InnoDB依靠Next-key(gap locking)实
  现“防止幻读”。
事务隔离级别与InnoDB实现手段
事务隔离级别与InnoDB实现手段
机票选座解决2:悲观锁
• Select的时候加“锁”
  – select status from seat where id={:id}
    WITH LOCK(伪代码)
  – update seat set status={:1} where
    id={:id}

• 问题与回答
  • InnoDB的Select是否是加锁的? 一般不,看隔离级别
  • 数据库的读操作和写操作是互斥的吗? 不一定,多版本控制
  • WITH LOCK(伪代码)选:读锁(共享锁)vs. 写锁(排它
    锁)? 写锁,读锁可能导致死锁
  • 上述两条语句是在一个事务内vs.两个事务内? 一个事务内,
    事务结束时锁会释放
Redis与Lost Update
• RPOPLPUSH
• WATCH+MULTI/EXEC
重要参考资料
1、多版本控制(Consistent Non-locking Read)
http://dev.mysql.com/doc/refman/5.1/en/inno
  db-consistent-read.html
2、间隙锁(Next-key locking)
http://dev.mysql.com/doc/refman/5.0/en/inno
  db-next-key-locking.html
3、隔离级别与InnoDB实现手段
http://blog.csdn.net/xiao7ng/article/details/50
  34013
4、Lost Update and Version Compare
http://jeffkemponoracle.com/2005/10/21/avoi
  ding-lost-updates-protecting-data-in-a-multi-
  user-environment/
导航
• 1.应用层并发控制?不是重点
• 2.Lost Update?乐观锁与悲观锁
• 3.隔离级别:脏读Dirty Read+幻读Phantom
  Read
• 4.InnoDB锁与多版本控制?
• 5.意向锁?
• 6.事务传播行为?
意向锁与多粒度锁系统
• 1、多粒度锁系统:表锁+行锁。
 – 表锁:并发低,开销小;
 – 行锁:并发高,开销大(每个记录都要登记锁)。
• 2、表中的某个行被锁了,再申请表锁?
 – 扫描全表,看是否有某行已锁:效率低。
 – 引入“意向锁(Intention)”:对行加锁前,先对表
   加意向锁;若表被加意向锁,则表明表中存在某行
   正在加锁。
 – 申请表锁时,如果表被意向锁了,则拒绝申请。当
   然表被锁了,也拒绝申请。
多粒度树

• 1、意向锁只是为了降低加/解锁的开销用的,
  是性能提升的一种手段(DB内部机制)。
• 2、意向锁:意向共享/排他锁
• 3、多粒度树
导航
• 1.应用层并发控制?不是重点
• 2.Lost Update?乐观锁与悲观锁
• 3.隔离级别:脏读Dirty Read+幻读Phantom
  Read
• 4.InnoDB锁与多版本控制?
• 5.意向锁?
• 6.事务传播行为?
Spring事务传播行为
• 1、Spring并没实现事务控制,只是封装
  JDBC、JPA等的API,目的:数据源无关性。
• 2、Spring亮点之一是:声明式事务。
基于AOP:why 传播行为?
• 1、@Transactional配置则告诉在方法的开
  头加BeginTrasaction,结尾加Commit,
  调实际方法时捕获异常,并异常时Rollback。
• 2、方法嵌套调用呢?传播行为的问题。
7种传播行为
验证1:REQUIRED
• 1、执行前: (1,alice);(2,bob)。
• 2、执行后: 两记录不变。
验证2-1:REQUIRES_NEW
• 1、执行前: (1,alice);(2,bob)。
• 2、执行后: 两记录不变。
验证2-2:REQUIRES_NEW
• 1、执行前: (1,alice);(2,bob)。
• 2、执行后: (1,AAA); (2.bob)。
验证2:Why ?
• 声明式事务是基于AOP的,验证2-1是内部
  调用,方法不是在代理对象上执行,而是在
  自己身上执行,AOP没适用上!
我们了解了这些
• 1.应用层并发控制?不是重点
• 2.Lost Update?乐观锁与悲观锁
• 3.隔离级别:脏读Dirty Read+幻读Phantom
  Read
• 4.InnoDB锁与多版本控制?
• 5.意向锁?
• 6.事务传播行为?
资源竞争与并发控制

More Related Content

Similar to 资源竞争与并发控制 (12)

丁原:海量数据迁移方案
丁原:海量数据迁移方案丁原:海量数据迁移方案
丁原:海量数据迁移方案
 
MySQL 6.0 下的cluster + replicate - 20080220
MySQL 6.0 下的cluster + replicate - 20080220MySQL 6.0 下的cluster + replicate - 20080220
MySQL 6.0 下的cluster + replicate - 20080220
 
Mysql mmm安装指南(翻译)
Mysql mmm安装指南(翻译)Mysql mmm安装指南(翻译)
Mysql mmm安装指南(翻译)
 
Mysql proxy+mysql-mmm
Mysql proxy+mysql-mmmMysql proxy+mysql-mmm
Mysql proxy+mysql-mmm
 
深入理解Andorid重难点
深入理解Andorid重难点深入理解Andorid重难点
深入理解Andorid重难点
 
Mysql集群
Mysql集群Mysql集群
Mysql集群
 
Mysql展示功能与源码对应
Mysql展示功能与源码对应Mysql展示功能与源码对应
Mysql展示功能与源码对应
 
Mysql 101014202926-phpapp01
Mysql 101014202926-phpapp01Mysql 101014202926-phpapp01
Mysql 101014202926-phpapp01
 
Showinnodbstatus公开
Showinnodbstatus公开Showinnodbstatus公开
Showinnodbstatus公开
 
My sql 同步
My sql 同步My sql 同步
My sql 同步
 
My sql管理基础 李春_v2
My sql管理基础 李春_v2My sql管理基础 李春_v2
My sql管理基础 李春_v2
 
MySQL多机房容灾设计(with Multi-Master)
MySQL多机房容灾设计(with Multi-Master)MySQL多机房容灾设计(with Multi-Master)
MySQL多机房容灾设计(with Multi-Master)
 

资源竞争与并发控制