悲观锁,乐观锁,排他锁,行锁----MYSQL

在说具体的锁结构时,先思考一个问题,那就是为什么要上锁?然后我要如何选择锁?锁具体如何实现?

在文章得末尾我给出了我的个人答案。

一、什么是悲观锁?

  1、悲观锁就是在操作数据时,认为此操作会出现数据冲突,所以在进行每次操作时都要通过获取锁才能进行对相同数据的操作,这点跟java中的synchronized很相似。

  2、在MySQL中如何实现悲观锁。?

  mysql中有悲观锁的实现,我们想实现悲观锁时调用相对应得语句。

  测试用表的结构和插入一行数据,下面其他的锁也会同时用到这个表。

 use test;
 create table msq_test (
   id int primary key,
   status  ) engine = innodb default character set = 'utf8';

 , '1');

  操作:1、set autocommit=0;  2、select  .....for update实现锁

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

  

    //开始事务
    begin;
    //查询出主键id=1的信息
     for update;
    //修改status为2
    ;
    //提交事务
    commit;

注:上面的begin/commit为事务的开始和结束,因为在前一步我们关闭了mysql的autocommit,所以需要手动控制事务的提交

  在这里,我们使用了select…for update的方式,这样就通过数据库实现了悲观锁。此时在msq_test表中,id为1的 那条数据就被我们锁定了,其他事务的操作必须等待我们自己主动commit提交事务之后才能操作,这样我们可以保证当前的数据不会被其它事务修改。

二、什么是乐观锁 ?

  1、乐观锁就是每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。

  2、在mysql如何实现乐观锁?

  乐观锁不是数据库自带的,需要我们自己去实现

  

 use test;
 create table msq_test (
 d int primary key,
 status ),
 version not null
 ) engine = innodb default character set = 'utf8';

 , );

  在上表中添加一个数字类型的 “version” 字段来实现。当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数据。

  

;//得出一开始设置得version字段(versionValue)
   where version = versionValue;

  这样子就实现了乐观锁机制。

三、什么是行锁和表锁?

  行锁:

    行锁,由字面意思理解,就是给某一行加上锁,也就是一条记录加上锁。

"  lock in share mode; 

在msq_test表中,id字段为主键,就也相当于索引。执行加锁时,会将id这个索引为1的记录加上锁,那么这个锁就是行锁。

到这里,我一开始也是很懵逼,啥是 lock in share mode???

select.....lock in share mode走的是IS锁(意向共享锁),即在符合条件的rows(行)上都加了共享锁,这样的话,其他session(事务)可以读取这些记录,也可以继续添加IS锁,但是无法修改这些记录直到你这个加锁的session执行完成(否则直接锁等待超时)。

  表锁:

    根据行锁,你能理解啥是表锁吗?我想你智商比我高,应该可以。

    不过我们需要注意一些锁的级别,MySQL InnoDB默认Row-Level Lock,所以只有「明确」地指定主键,MySQL 才会执行Row lock (只锁住被选取的数据) ,否则MySQL 将会执行Table Lock (将整个数据表单给锁住)。

四、排他锁

  1、什么是排他锁

    若事务T对数据对象A加上X锁,则只允许T读取和修改A,其他任何事务都不能再对A加任何类型的锁,直到T释放A上的锁。这就保证了其他事务在T释放A上的锁之前不能再读取和修改A。

    啥?这定义太过复杂,难道买菜我会需要用高数去计算菜钱?我也觉得没必要。所以可以认为上了排他锁,其他线程既不能读也不能修改。

  2、如何用排他锁

    用法: select … for update;

    例如:select * from msq_test where id = 1 for update;

    排他锁的申请前提:没有线程对该结果集中的任何行数据使用排他锁或共享锁,否则申请会阻塞。

    for update仅适用于InnoDB,且必须在事务块(BEGIN/COMMIT)中才能生效。在进行事务操作时,通过“for update”语句,MySQL会对查询结果集中每行数据都添加排他锁,其他线程对该记录的更新与删除操作都会阻塞。排他锁包含行锁、表锁。

  注意:

    事务保证整个操作的成一个组,要么全做要么全不做 但是不能保证多个事务同时读取同一个数据
    数据对象被加上排它锁时,其他的事务不能对它读取和修改;加了共享锁的数据对象可以被其他事务读取,但不能修改

文章的末尾,回到一开始的问题,为何要上锁?锁的具体实现上文已经给出。

  答:为何上锁?

  事务可以用锁实现,可以保证一致性和隔离性,但是锁用来保证并发性;但是隔离性只是保证不会出现相互读取中间数据(却无法解决并发的问题)

  为啥保持一致性的原因: MySQL会出现丢失更新:一个事务的更新覆盖了其它事务的更新结果,就是所谓的更新丢失。例如:用户A把值从11改为8,用户B把值从8改为11,则用户A丢失了他的更新。
        为啥保持隔离性的原因:MySQL会出现脏读:当一个事务读取其它完成一半事务的记录时,就会发生脏读取。例如:用户A,B看到的值都是9,用户B把值改为5,用户A读到的值仍为9。

  

    

上一篇:nginx+uwsgi+django 部署原理


下一篇:JS截取后缀名,文件全名,非后缀名的方法---收藏(冷饭_)