MySQL中的锁

来源:互联网 发布:甲基异噻唑啉酮 知乎 编辑:程序博客网 时间:2024/06/06 13:06

MySQL中的锁

1.MySQL不同的存储引擎支持不同的锁:

MyISAM支持表锁:开销小,加锁快,无死锁,冲突高,并发度低;

InnoDB支持行锁:开销大,加锁慢,有可能死锁,冲突低,并发度高;(也支持表锁)

DBD支持页面锁:介于两者之间;被InnoDB取代中。

MyISAM表级锁:

查询表级锁争用情况:show status like 'table%'

Table_locks_waited值越高,说明存在较严重的表级锁的争用。

MyISAM表级锁:一次性获取所需的全部锁;

读锁和写锁:读读共享,读写--写写--写读三个都互斥;

执行select语句的时候,自动加上读锁;

执行update,delete,insert,自动加上写锁;


显示加锁可以模拟事务操作:

Lock tables tablesa read local, tablesb read local;

select sum(total) from tablesa;               //不先给两表加锁,a表执行时候,b表可能发生了改变;

select sum(subtotal) from tablesb;

unlock tables;

local 表示满足一定条件,允许其他用户在表位进行并发的插入


执行lock tables后,只能访问显式加锁的表,不能访问没有加锁的表,如果加的是读锁,只能执行查询,不能执行更新。当使用lock tables时候,别名出现多少次,对于别名需要锁定多次。

lock table actor as a read,actor as b read ;


并发插入:

MyISAM存储引擎有一个系统变量,concurrent_insert=0,1,2;

0:不允许并发插入

1:有空洞(表的中间有被删除的行)时允许并发插入;

2:没有空洞时也允许并发插入;

lock table table_a read local;

session_A 可以查询表table_a;

session_B 可以插入表table_b;


MyISAM的锁的调度:

默认情况下,写请求优先,读请求和写请求同时,会写请求获得表锁。

也可以该表执行语句的优先级调整。low_priority;或者设置系统参数max_write_lock;

可以考虑尽量通过中间表对sql语句进行分解,使得每一步能在较短的时间内完成,减少锁的冲突;

不可避免的复杂查询如统计安排在数据库空间的深夜执行;


InnoDB表级锁:

最大特点:支持事务+行级锁;

事务的四大特性ACID;

并发事务带来的问题:

1. 更新丢失:后更新覆盖前更新,解决这个问题不能单靠数据库事务控制器解决,也依赖于操作数据加

2. 脏读,不可重复读,幻读;

脏读【读到未提交的内容】,不可重复读【读未加锁,读以前读过的数据不一致】,幻读【其他事务插入了新数据,同样查询执行结果不同】是数据一致性问题,事务隔离机制解决。

实现事务隔离的方式:

1.读数据前,加锁;

2.不加锁,生成一个数据请求时间点的一致性快照,提供一定级别的一致性读,多版本并发控制


事务隔离级别对应如下:

未提交读------  已提交读        ------    可重复读          -------可序列化

  脏读   ------ 不可重复读(语句级)----幻读(事务级)-----------事务级


查看InnoDB锁争用情况:

show status like 'innodb_row_lock%';

create table innodb_monitor(a int) engine=innnodb;   //设置监视器

show innodb status \G

drop table innodb_monitor;


InnoDB的行锁模式和加锁方法;

InnoDB实现方式;

间隙锁;

不同隔离级别下的锁和一致性读策略;

MySQL恢复和复制对InnoDB锁机制和一致性读取策略的不同;


InnoDB锁的总结:

InnoDB的行锁是基于索引实现的,不通过索引访问表,InnoDB会使用表锁;

使用间隙锁的原因;



----------------------------------------------------------------------------------------------------------

下面是有关悲观锁和乐观锁的详细介绍:

悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁


乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。


两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下,即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果经常产生冲突,上层应用会不断的进行retry,这样反倒是降低了性能,所以这种情况下用悲观锁就比较合适。


在数据库的锁机制中介绍过,数据库管理系统(DBMS)中的并发控制的任务是确保在多个事务同时存取数据库中同一数据时不破坏事务的隔离性和统一性以及数据库的统一性。

乐观并发控制(乐观锁)和悲观并发控制(悲观锁)是并发控制主要采用的技术手段。

无论是悲观锁还是乐观锁,都是人们定义出来的概念,可以认为是一种思想。其实不仅仅是数据库系统中有乐观锁和悲观锁的概念,像memcache、hibernate、tair等都有类似的概念。

针对于不同的业务场景,应该选用不同的并发控制方式。所以,不要把乐观并发控制和悲观并发控制狭义的理解为DBMS中的概念,更不要把他们和数据中提供的锁机制(行锁、表锁、排他锁、共享锁)混为一谈。其实,在DBMS中,悲观锁正是利用数据库本身提供的锁机制来实现的。

下面来分别学习一下悲观锁和乐观锁。

悲观锁

在关系数据库管理系统里,悲观并发控制(又名“悲观锁”,Pessimistic Concurrency Control,缩写“PCC”)是一种并发控制的方法。它可以阻止一个事务以影响其他用户的方式来修改数据。如果一个事务执行的操作都某行数据应用了锁,那只有当这个事务把锁释放,其他事务才能够执行与该锁冲突的操作。悲观并发控制主要用于数据争用激烈的环境,以及发生并发冲突时使用锁保护数据的成本要低于回滚事务的成本的环境中。

悲观锁,正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度(悲观),因此,在整个数据处理过程中,将数据处于锁定状态。 悲观锁的实现,往往依靠数据库提供的锁机制 (也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)

在数据库中,悲观锁的流程如下:

在对任意记录进行修改前,先尝试为该记录加上排他锁(exclusive locking)。

如果加锁失败,说明该记录正在被修改,那么当前查询可能要等待或者抛出异常。 具体响应方式由开发者根据实际需要决定。

如果成功加锁,那么就可以对记录做修改,事务完成后就会解锁了。

其间如果有其他对该记录做修改或加排他锁的操作,都会等待我们解锁或直接抛出异常。

MySQL InnoDB中使用悲观锁

要使用悲观锁,我们必须关闭mysql数据库的自动提交属性,因为MySQL默认使用autocommit模式,也就是说,当你执行一个更新操作后,MySQL会立刻将结果进行提交。 set autocommit=0;

//0.开始事务begin;/begin work;/start transaction; (三者选一就可以)//1.查询出商品信息select status from t_goods where id=1 for update;//2.根据商品信息生成订单insert into t_orders (id,goods_id) values (null,1);//3.修改商品status为2update t_goods set status=2;//4.提交事务commit;/commit work;

上面的查询语句中,我们使用了 select…for update 的方式,这样就通过开启排他锁的方式实现了悲观锁。此时在t_goods表中,id为1的 那条数据就被我们锁定了,其它的事务必须等本次事务提交之后才能执行。这样我们可以保证当前的数据不会被其它事务修改。

上面我们提到,使用 select…for update 会把数据给锁住,不过我们需要注意一些锁的级别,MySQL InnoDB默认行级锁。行级锁都是基于索引的,如果一条SQL语句用不到索引是不会使用行级锁的,会使用表级锁把整张表锁住,这点需要注意。

优点与不足

悲观并发控制实际上是“先取锁再访问”的保守策略,为数据处理的安全提供了保证。但是在效率方面,处理加锁的机制会让数据库产生额外的开销,还有增加产生死锁的机会;另外,在只读型事务处理中由于不会产生冲突,也没必要使用锁,这样做只能增加系统负载;还有会降低了并行性,一个事务如果锁定了某行数据,其他事务就必须等待该事务处理完才可以处理那行数

乐观锁

在关系数据库管理系统里,乐观并发控制(又名“乐观锁”,Optimistic Concurrency Control,缩写“OCC”)是一种并发控制的方法。它假设多用户并发的事务在处理时不会彼此互相影响,各事务能够在不产生锁的情况下处理各自影响的那部分数据在提交数据更新之前,每个事务会先检查在该事务读取数据后,有没有其他事务又修改了该数据。如果其他事务有更新的话,正在提交的事务会进行回滚。乐观事务控制最早是由孔祥重(H.T.Kung)教授提出。

乐观锁( Optimistic Locking ) 相对悲观锁而言,乐观锁假设认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让返回用户错误的信息,让用户决定如何去做。

相对于悲观锁,在对数据库进行处理的时候,乐观锁并不会使用数据库提供的锁机制。一般的实现乐观锁的方式就是记录数据版本

数据版本,为数据增加的一个版本标识。当读取数据时,将版本标识的值一同读出,数据每更新一次,同时对版本标识进行更新。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的版本标识进行比对,如果数据库表当前版本号与第一次取出来的版本标识值相等,则予以更新,否则认为是过期数据。

实现数据版本有两种方式,第一种是使用版本号,第二种是使用时间戳

使用版本号实现乐观锁

使用版本号时,可以在数据初始化时指定一个版本号,每次对数据的更新操作都对版本号执行+1操作。并判断当前版本号是不是该数据的最新的版本号。

1.查询出商品信息select (status,status,version) from t_goods where id=#{id}2.根据商品信息生成订单3.修改商品status为2update t_goods set status=2,version=version+1where id=#{id} and version=#{version};

优点与不足

乐观并发控制相信事务之间的数据竞争(data race)的概率是比较小的,因此尽可能直接做下去,直到提交的时候才去锁定,所以不会产生任何锁和死锁。但如果直接简单这么做,还是有可能会遇到不可预期的结果,例如两个事务都读取了数据库的某一行,经过修改以后写回数据库,这时就遇到了问题。








0 0
原创粉丝点击