一、MyISAM
1、MyISAM支持表锁,表级锁有两种模式:表共享读锁(Table Read Lock)和表独占写锁(Table Write Lock)
2、MyISAM存储引擎的读锁和写锁是互斥的,读写操作是串行的
3、默认情况下,MyISAM写锁优先级大于读锁,一个进程请求某个 MyISAM表的读锁,同时另一个进程也请求同一表的写锁,最终是写进程先获得锁。这是因为MySQL认为写请求一般比读请求要重要。

这也正是MyISAM表不太适合于有大量更新操作和查询操作应用的原因,大量的更新操作会造成查询操作很难获得读锁


二、InnoDB引擎


1、相对MyISAM,InnoDB引入了事务机制和行锁


2、InnoDB既支持表锁也支持行锁,默认使用行锁


3、InnoDB具有两种类型行锁:


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


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


4、InnoDB行锁是基于锁引实现的,如果不通过索引访问数据,InnoDB会使用表锁


5、InnoDB间隙锁:当用范围条件而不是相等条件检索数据或者如果使用相等条件请求给一个不存在的记录加锁,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的 索引项加锁,目的为了防止幻读


例如:


使用范围条件检索被加间隙锁:假如emp表中只有101条记录,其empid的值分别是 1,2,...,100,101,下面的SQL:


Select * from  emp where empid > 100 for update;是一个范围条件的检索,InnoDB不仅会对101加锁,也会对empid大于101(这些记录并不存在)的“间隙”加锁


使用相等条件给不存在记录加锁:假如emp表中只有101条记录,其empid的值分别是1,2,......,100,101,下面SQL:


select * from emp where empid = 102 for update




最佳实践:在实际应用开发中,尤其是并发插入比较多的应用,我们要尽量优化业务逻辑,尽量使用相等条件来访问更新数据,避免使用范围条件





三、关于死锁


1、MyISAM表锁总是一次获得所需的全部锁,要么全部满足,要么等待,因此不会出现死锁


2、InnoDB中,除单个SQL组成的事务 外,锁是逐步获得的,因此容易发生死锁,例如


session_1:                                                                   session_2: 

 set autocommit = 0;                                                       set autocommit = 0; 

 select * from table_1 where id=1 for update;                 select * from table_2 where id=2 for update; 

 select * from table_2 where id =2 for update;                select * from table_1 where id =1 for update;



死锁原因:在行锁前提下,两个Session加锁顺序不一致

怎样避免:让不同的session加锁有次序,如果session_2也是先给id=1加锁,那么session_2会一直等待session_1释放锁,也就不会有机会给id=2加锁了



四、避免死锁


应当尽量从设计和程序实现上减少甚至避免死锁,而不是探究死锁的解决办法


五、悲观锁与乐观锁

1   悲观锁

定义:假定数据可能随时被其它事务更新,当前事务更新很有可能失败

实现:

使用数据库内置锁机制:当前事务想要更新某一条数据,在读取时立即对该条数据加锁,锁具有排他性,在当前事务处理完成之前,其它事务没有机会更新该条数据。

锁分两种类型:行锁和表锁。MyIASM只支持表锁,InnoDB既支持表锁也支持行锁,默认是行锁。MyIASM不会发生死锁,InnoDB会发生死锁。

悲观锁模拟:

 测试表格:t_goods

mysql 锁读 mysql读写锁怎么实现_mysql 锁读

mysql 锁读 mysql读写锁怎么实现_mysql 锁读_02

(1)     表格无主键:只会出现表锁

(2)     表格有主键

----SQL明确使用主键,并且查询有数据:行锁

SELECT * from t_goods where id = 11 for update;

----SQL明确使用主键,并且查询无数据:无锁

SELECT * from t_goods where id = 1111 for update;

----SQL中主键不明确,并且查询有数据:表锁

SELECT * from t_goods where id <> 11 for update;

SELECT * from t_goods where id >= 11 for update;

----SQL中主键不明确,并且查询无数据:无锁

SELECT * from t_goods where id >= 1111 for update;

----SQL中不使用主键,并且查询有数据:表锁

SELECT * from t_goods where name = 'nick' for update;

----SQL中不使用主键,并且查询无数据:表锁

SELECT * from t_goods where name = 'hehe' for update;



2    乐观锁

定义:假定数据更新不会发生冲突,只在提交更新时去检测是否有冲突

实现:

----使用Version字段:

数据表格增加一个Version字段,读取数据时连Version一起读出,在执行更新时将此Version与数据库中Version进行比对,如果相同则更新,如果不相同说明有其它事务执行了更新,此时提示用户

----使用TimeStamp字段:原理同Version字段,比较更新前后时间戳是否一样





参考:http://xu20cn.blog.51cto.com/274020/72574