首先,我们要理解,在mysql里面主要为两种,一种是概念上的区分,另一种是实际运用效果不同的区分。

概念区分的锁

  • 乐观锁
  • 悲观锁

实际运用的锁

  • 行锁
  • 表锁

指事务某个状态的锁

  • 死锁

锁的介绍

mysql7种锁 mysql锁有哪些_数据库

一、表锁

表锁分为两种类型:

(1)、表读锁

lock tables db.goods read;	//给数据库 db 的 goods表上读锁

当给这个表上读锁的时候,其他线程请求对这个表的操作只能读,不能写

(2)、表写锁

lock tables db.goods write;	//给数据库 db 的 goods表上写锁

当给这个表上写锁的时候,其他线程请求对这个表的操作会阻塞,不能读和写

解锁

unlock tables;

总结

在实际开发场景其实很少用到,毕竟锁住整个表对业务场景来说基本都是痛鸡
.
.
.

二、行锁

行锁就是说,一个线程update表里一条数据,当其他线程请求这条数据时,是不能操作这条数据的,只有等第一个线程结束了才能去操作,即第一个线程独享这条数据的操作权限

举个栗子,查询商品的库存,在sql语句后面加 for update ,给商品表 id = 1 的记录上锁

select  quantity from goods where id =1 for update;

扩展思考

值得注意的是,被扫描的行也会上锁,所以使用锁的时候必须走索引,指定一条数据上锁,否则全表扫会导致其它行甚至整个表上锁!

课外提示

(1)事务上锁的时机:

使用mysql的事务机制,当执行到update等更新数据库的操作语句时候,才会为当前的操作行上锁,直到事务释放为止。
.
.

三、死锁

死锁是多个事务出现互相等待对方释放资源的状态

举个栗子:

事务A事务B请求同一个表

事务A ,需要请求id=1id=2 的行资源,
事务B, 需要请求 id=2id=1的行资源。

就是并发场景下,A事务给id=1的行上锁了想拿id=2的行锁,发现B事务已经拿了id=2的锁,就在阻塞等待事务B释放锁;同时,事务B在拿了id=2的锁后,想拿id=1的锁,却发现了A事务拿了id=1的锁,也阻塞等待事务A释放锁,就造成了两个事务傻傻的互相等待对方释放资源

mysql7种锁 mysql锁有哪些_php_02

怎么解决呢?

mysql7种锁 mysql锁有哪些_mysql_03

(1)、事务的顺序合理性

什么是合理的顺序呢?就以上面例子,出现死锁的原因其实两个加锁的方向反了才导致,可以调整一下事务的顺序,例如

大家都从id=1 的行去一起开始处理,就大概率不会出现上面的情况

(2)、设置死锁时间

在innodb里,默认死锁时间是50S,可以通过innodb_lock_wait_timeout命令设置合理的时间

(3)、避免长事务

一般使用事务的时候,如非必要尽量不要使用长事务。如果你非要用长事务,我也很无奈(只能采用其他手段尽量避免避免并发事务,如redis、消息队列、异步等等方案)
.
.
.

四、悲观锁

理解

对一条数据操作时,持悲观态度,觉得数据可能产生冲突,所以利用mysql的锁机制上锁。所以,上面提到的事务和行锁都是悲观锁的一种实现

五、乐观锁

理解

与之相反乐观锁,就是操作数据的时候持乐观态度,觉得数据不会冲突,只在代码的逻辑层做限制,一旦出现冲突,就返回提示取消执行

那么代码怎么做限制呢?

乐观锁的实现一般需要借助字段来做版本控制

(1)举个栗子🌰 ,扣减库存

//获取商品id为1 的库存
$data = select quantity,update_time from goods where id =1;
//增加用户id=1的订单
insert into order(goodsid,userid) values(1 ,1);
//修改商品库存
update goods quantity=quantity-1 ,data=time()  where id=1 and update_time = $data['time'];

从上面可以看出,使用update_time字段作为版本变更的依据,查出库存 => 比较时间戳(或版本) => 存入库存,在执行第三条sql语句的时候,如果 update_time字段发生了变化,语句就会返回执行失败,然后返回提示给 id=1的用户。

当然不是一定是使用date_time字段,也有很多人是用version作用控制版本的字段,每一次操作就+1

总结

其实从上面的例子,不难看出乐观锁在大并发的场景,用户大概率经常返回下单失败的提示,这样对用户的体验是很严重的打击的,而个人方案的建议是用redis缓存中间减少mysql的并发,在redis中操作缓存扣减库存,在用异步队列把库存扣减操作到mysql中,实现持久化。