高并发下MYSQL防重设计

1,479 阅读2分钟

前言

业务开发中,经常会遇到重复提交的情况,无论是由于网络问题无法收到请求结果而重新发起请求,或是前端的操作抖动而造成重复提交情况。在这里有两个比较重要的概念:

  1. 防重

防重的目的是防止重复数据的产生,比如 insert 操作时,用户快速点击两次,如果没做防重,就会产生重复数据。

  1. 幂等

请求多次,只有第一次请求才会做数据处理,后面的请求不会产生数据改变,例如退款,第一次退款成功后,后面的请求,不会再次进行退款。

备注:之所以两个概念放在一起进行说明,是因为不论是防重还是幂等,很多技术手段都是一样的,只不过在业务场景下,返回结果可能会有不同。

场景分析

数据库的操作基本就是增、删、改、查,需要做防重处理的是增加和修改;查询和删除本身执行一次和多次,产生的效果是一样的,有天然幂等性。

例如:下单的重复支付,库存剩余量的更新。

  1. insert :通常情况下 select + insert

如果此时存在并发请求,则会出现如下情况:

  1. update :通常情况下 select + status 判断 + update

并发请求,则会出现如下情况:

唯一索引

alter table tomato.unique_demo add UNIQUE KEY  (unique_no);

在 insert 操作中,最直接有效的方法就是添加唯一性索引,通过最终的数据库阶段进行控制。

这种方式确实可以进行防重,但是在逻辑删除的场景下是存在问题的。

有人会说在 step3 时可以更新数据而不是新创建数据,此时会有如下问题:

  1. 用户删除记录无法查询。
  2. 唯一性索引并不一定是全部的数据,每次新增的数据有可能存在其他的数据不一致。

防重表

替代唯一性索引情况下,逻辑删除的问题。

  1. 通用防重表

核心字段是:防重字段和业务码,通过唯一性索引的方式进行防重设计。

`unique_no` varchar(130) not null comment '防重字段',
`unique_type` varchar(36) not null comment '业务码',
 unique key `ux_unique_no_type` (`unique_no`,unique_type)
  1. 定制防重表(一个业务一张表)

核心字段:ID 和防止重复的业务表保持一致,提高数据库的执行效率。

`unique_table_customer_id` bigint(20) not null comment 'id和业务表保持一致(防重字段)',

核心使用流程都是一样的,区别在于是否多个业务或者说多个表使用同一个防重表。