mysql-锁

mysql从锁的范围上来说的话,它主要有2个范围。

·表级锁:开销小,加锁快,发生锁冲突概率高,并发度低,不会出现死锁。

·行级锁:开销大,加锁慢,发生锁冲突概率低,并发度高,会发生死锁。

MyISAM存储引擎默认为表级锁,不支持行级锁,所以说MyISAM存储引擎锁的力度较粗,并发能力低。

InnoDB存储引擎默认使用的是行级锁,所以InnoDB的并发能力高。

从类型上划分InnoDB的锁,分为以下几个类型:

·共享锁(S):行级,读取一行

·排他锁(X):行级,更新一行

·意向共享锁(IS):表级,准备加共享锁

·意向排他锁(IX):表级,准备加排他锁

·间隙锁(NK):行级,使用范围条件时,锁住这个范围内不存在的数据

1、事务a加了意向共享锁,事务b不可以加排他锁,可以加意向共享锁、意向排他锁、共享锁。

2、事务a加了意向排他锁,事务b不可以加共享锁和排他锁,事务b可以加意向共享锁、意向排他锁。

3、事务a加了共享锁,事务b不可以加意向排他锁和排他锁,事务b可以加意向共享锁和共享锁。

4、事务a加了排他锁,事务b任何锁都不可以加。

加锁:意向锁是自动加的。

1、增加行级锁之前,InnoDB会自动给表加意向锁。

2、执行DML语句时,InnoDB会自动给数据加排他锁。

3、执行DQL语句时:

    共享锁(S):select---from---where---Lock IN SHARE MODE;

    排他锁(X):select---from---where---FOR UPDATE;

    间隙锁(NK):上述sql采用范围条件时,InnoDB会对不存在的记录自动加间隙锁。

死锁:行级锁可能会出现死锁的情况

场景:

事务1: update t set---where id =1;

            update t set---where id =2;

事务2: update t set---where id =2;

            update t set---where id =1;

如果在很巧的情况下,id=1这条数据被事务1加锁,id=2这条数据被事务2加锁,那么就会产生死锁。

死锁的解决方案:

1、一般InnoDB会自动检测到,并使一个事务回滚,另一个事务继续执行。

2、设置超时等待参数:innoDB_lock_wait_timeout。

如何避免死锁?

1、不同的事务并发访问多个表时,应约定以相同顺序来访问这些表。

2、以批量的方式处理数据时,应事先对数据排序,保证线程以固定顺序来处理数据。

3、在事务中,如果需要更新记录,应直接申请足够级别的锁。

悲观锁:数据库中的锁都是悲观锁。

乐观锁:自定义

乐观锁一般有2种实现机制

1.版本号机制:

update---set---version=#{version+1}where---and version=${version}

这个比较麻烦的是,我们得给每张表加一个字段版本号。每个sql得把这个version带上。这对sql、对表的侵入性比较强,但是他的确很有效,或者说效率比较高。

如果更新数据比较频繁的话,最好使用悲观锁;如果说更新数据比较少,查询数据比较多的话,使用乐观锁可以很明显提高效率。

乐观锁给表加了版本号之后,每次更新数据都需要对版本号加1,where条件之前不管是什么,现在都得加上version=${version}。这个version为更新之前查到的version

2、CAS算法

cas算法是一种无锁的算法,该算法涉及3个操作数(内存值V,旧值A,新值B),当V等于A时,采用原子方式用B的值替换A的值。该算法采用的是自旋锁。

他的缺点是:

1、ABA问题:某个线程将A改为B,然后再改回A,则CAS算法会误认为A没有被改过。

2、自旋锁采用循环的方式实现,若加锁时间长,则会给cpu带来巨大的开销。

3、cas只能保证一个共享变量的原子操作。

你可能感兴趣的:(mysql-锁)