首页 体育 教育 财经 社会 娱乐 军事 国内 科技 互联网 房产 国际 女人 汽车 游戏

大并发热点行更新的两个骚操作

2019-12-31

在大并发的情况下加上夸网络屡次交互,就不可避免的因为网络推迟、丢包等原因导致业务的履行时刻过长,呈现雪崩概率会大大添加。主张在功能和并发要求比较高的场景下尽量少用orm,假如非要竭尽量操控好业务的规模和履行时刻。

大并发db操作的准则便是业务操作尽量少跨网络交互,一旦跨网络运用业务尽量用达观锁来处理,少用失望锁,尽量缩短当时 session 持有锁的时刻。

在大并发的情况下加上夸网络屡次交互,就不可避免的因为网络推迟、丢包等原因导致业务的履行时刻过长,呈现雪崩概率会大大添加。主张在功能和并发要求比较高的场景下尽量少用orm,假如非要竭尽量操控好业务的规模和履行时刻。

下面共享两个在mysql innodb engine 上的大并发更新行的骚操作,这两个骚操作都是尽可能的缩小db锁的规模和时刻。

比较常见的大并发场景之一便是热门数据的 update,比方具有预算类的库存、账户等。

update从原理上需求innodb engine 先获取row数据,然后进行row format转换到mysql服务层,再经过mysql服务器层进行数据修正,最终再经过innodb engine写回。

这整个进程每一个环节都有必定的开支,首要需求一次innodb查询,然后需求一次row format,最终还需求一次更新和一次写入,大约需求四个小阶段。

一次update就需求上述四进程开支。此刻假如qps十分大,必定会有必定功能开支。那么咱们能不能将单个行的热门分散开来,一起将update转换成insert,咱们来看下怎么骚操作。

咱们创立一个sku库存表。

CREATE TABLE `tb_sku_stock`  unsigned NOT NULL AUTO_INCREMENT,
 `sku_id` bigint NOT NULL,
 `sku_stock` int DEFAULT '0',
 `slot` int NOT NULL,
 PRIMARY KEY ,
 UNIQUE KEY `idx_sku_slot` ,
 KEY `idx_sku_id` 
) ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARSET=utf8mb4
表中唯一性索引 idx_sku_slot 用来束缚同一个 sku_id 不同 slot 。

咱们创立一个sku库存表。

CREATE TABLE `tb_sku_stock`  unsigned NOT NULL AUTO_INCREMENT,
 `sku_id` bigint NOT NULL,
 `sku_stock` int DEFAULT '0',
 `slot` int NOT NULL,
 PRIMARY KEY ,
 UNIQUE KEY `idx_sku_slot` ,
 KEY `idx_sku_id` 
) ENGINE=InnoDB AUTO_INCREMENT=30 DEFAULT CHARSET=utf8mb4

库存添加操作和削减操作要分隔来处理,咱们先看添加操作。

insert into tb_sku_stock 
values*9)+1) 
on duplicate key update sku_stock=sku_stock+values

咱们来看下削减库存,削减库存没有添加库存那么简略,最大的问题是要做前置查看,不能超扣。

咱们先看库存总数查看,比方咱们扣减10个库存数。

select sku_id, sum as ss
from tb_sku_stock
where sku_id= 101010101
group by sku_id having ss = 10 for update

mysql的查询是运用mvcc来完结无锁并发,所以为了实时一致性咱们需求加上for update来做实时查看。

假如库存是够扣减的话咱们就履行 insert into select 刺进操作。

咱们来看下削减库存,削减库存没有添加库存那么简略,最大的问题是要做前置查看,不能超扣。

咱们先看库存总数查看,比方咱们扣减10个库存数。

select sku_id, sum as ss
from tb_sku_stock
where sku_id= 101010101
group by sku_id having ss = 10 for update

mysql的查询是运用mvcc来完结无锁并发,所以为了实时一致性咱们需求加上for update来做实时查看。

insert into tb_sku_stock 
select sku_id,-10 as sku_stock,round *9+ 1)
from as ss
 from tb_sku_stock
 where sku_id= 101010101
 group by sku_id having ss = 10 for update) as tmp
on duplicate key update sku_stock= sku_stock+ values

整个操作都是在一次db交互中履行完结,假如操控好单表的数据量加上 unique key 合作功能是十分高的。

大型OLTP体系,都会有一些需求周期性履行的使命,比方定时结算的订单、定时撤销的协议等,还有许多兜底的查看、对账程序等都会查看必定时刻规模内的状况数据,这些使命一般都需求扫描表里的某个状况字段。

这些查询根本根据相似status状况字段,因为区分度十分低,所以索引根本上在这类场景下没有太大作用。

因为是排查锁,数据的 insert、update 都会受到影响,在 repeatable read 且没有 unqiue key 的场合下还会触发Gap lock。

咱们能够经过一个办法来消除 select...for update,而且进步数据并发处理才能。

CREATE TABLE `tb_order`  unsigned NOT NULL AUTO_INCREMENT,
 `order_id` bigint NOT NULL,
 `order_status` int NOT NULL DEFAULT '0',
 `task_id` int DEFAULT NULL,
 PRIMARY KEY 
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4

咱们简略创立一个订单表,task_id 是使命id,先让数据结构支撑多使命并行。

select order_id from tb_order where order_status=0 limit 10 for update

一般做法是经过select...for update 锁住行。咱们换个办法完结相同的作用一起不会存在并发履行问题。

update tb_order set task_id=10 where order_status=0 limit 10;
Query OK, 4 rows affected
select order_id from tb_order where task_id=10 limit 4;

假定咱们当时有许多并行使命,假定task_id=10使命履行,先update抢占自己的数据行。这个操作根本上在奇数ms内,然后再经过select 带上自己的taskid获取到归于当时task的行,一起能够带上精确的limit,因为update是会回来受影响行数。

这儿会有一个问题,便是履行的task假如因为某个原因停止了怎么办,简略办法便是用一个兜底job定时查看超越必定时刻的task,然后将task_id置为空。

作者:王清培

热门文章

随机推荐

推荐文章