高并发情况下扣除库存锁表情况

[TOC]

## 1. 锁表情景:查询条件没有索引时

​    库存表中,扣库存时的where条件居然不是id而是奖品类型,而刚好是用事务控制的,微信红包还得调用微信发奖,**导致锁表(间隙锁)**,并且这锁住的时间还是很严重的,每次都得等第三方接口调用成功处理完后续逻辑后,事务才会结束,秒杀场景下就导致了大部分用户超时失败的情况了,以下就是复现了当时的情景

​    总结起来就是两个严重问题:

1. 扣库存时没走索引
2. 在事务中,调第三方接口

```sql
create table gap(
     id int,
     age int,
     primary key(id)
)

select * from gap;
INSERT INTO `test`.`gap`(`id`, `age`) VALUES (1, 13);
INSERT INTO `test`.`gap`(`id`, `age`) VALUES (2, 24);

show engine innodb status;
show status like '%lock%';

show OPEN TABLES where In_use > 0;-- 查询是否锁表,in_use = 1表示加锁,0表示未加锁
show processlist;-- 查询到相对应的进程

-- 查看正在锁的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS; 
-- 查看等待锁的事务
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;

-- 事务1
start transaction;
update gap set age = 25 where age = 24;

-- 事务2
start transaction;
update gap set age = 14 where age = 13;
-- 这一行会等待,age没有索引时,会导致锁表
-- 查看正在锁的事务,为啥不是锁表而是record呢,明明是锁表呀
-- 19988193:890:3:2    19988193    X    RECORD    `test`.`gap`    PRIMARY    890    3    2    1
-- 19988194:890:3:2    19988194    X    RECORD    `test`.`gap`    PRIMARY    890    3    2    1
```

## 2. 解决

​        通过redis实现库存扣减,而不是通过db层来控制库存,减少db的压力。

## 3. 参考:

https://www.cnblogs.com/aspirant/p/9177978.html

> **Gap Lock在InnoDB的唯一作用就是防止其他事务的插入操作,以此防止幻读的发生。**
>
>   **Innodb自动使用间隙锁的条件:**
>
> **(1)必须在Repeatable Read级别下**
>
> **(2)检索条件必须有索引(没有索引的话,mysql会全表扫描,那样会锁定整张表所有的记录,包括不存在的记录,此时其他事务不能修改不能删除不能添加)**

你可能感兴趣的:(高并发情况下扣除库存锁表情况)