简单库存场景的数据库实现
一般来说,从数据库层面讲,库存业务会分为两步,第一步是插入一条记录到扣减明细表inventory_detail,第二步是对库存扣减表inventory的一条记录进行扣减,这两步往往是在一个事务中实现的。
数据库业务架构图如下,所有的请求均发往同一个Database。
从上文的架构图不难看出,所有的商品的库存信息都存在单一的表和库里,当商品种类繁多或者业务并发请求暴涨时,单实例的数据库显然会成为容量或者性能瓶颈。该数据库架构一般只是功能性的实现,主要用于微型库存系统或者测试使用。
高并发库存系统的数据库实现
为了解决单实例存在的容量和性能上限问题,阿里巴巴所有的库存系统在十年前就实现了分库分表设计,主要通过数据的水平拆分实现不同商品的库存扣减请求路由到不同的数据库。基本数据库架构图如下
从上图不难看出,库存扣减表和扣减明细表一般都使用商品id作为片键,这样可以保证满足整个系统在高并发扣减请求的同时,同一商品的库存扣减操作和添加明细操作在同一个事务中实现。如果数据分布和业务请求足够均匀,理论上经过分库分表设计后,整个系统的吞吐量将会是线性的增长,主要取决于分实例的数量。
热点行更新
在电商业务中,商家活动比如秒杀不可避免。秒杀活动会给电商库存系统带来巨大的挑战,尤其体现在数据库层面。因为往往一个商品id对应于数据库的一行记录,所以在DB架构上按照商品维度做了分库分表也是无效的。而更新这行记录时必然需要给这行记录加X锁。热点商品的库存扣减本质上就是热点行更新的能力,高并发的同行更新会造成严重的行锁等待现象,从而导致数据库的threads_running和rt飙升,造成雪崩。在当前的官方mysql中,一般单行更新的QPS在以内,对于热点商品的秒杀需求,这个量往往是不达标的。
阿里巴巴PolarDB-X数据库团队基于以上场景的需求,针对内核优化,引入了先进的水车模型,在识别出热点SQL后,实现了在内核层面优化处理,相比官方MySQL提高了10倍以上的热点行扣减能力,广泛应用于集团电商库存集群,资金平台,权益发放平台等核心数据库集群。
其主要的核心思想是:针对应用层SQL做轻量化改造,带上热点行SQL的标签,当这种SQL进入内核后,在内存中维护一个hash表,将主键或唯一键相同的请求(一般也就是同一商品id)hash到同一个地方做请求的合并,经过一段时间后(默认us)统一提交,从而实现了将串行处理变成了批处理,让每个热点行更新请求并不需要都去扫描和更新btree。
类似原理,阿里云RDS数据库团队同样在内核层面针对热点行更新做了大量的优化,核心思路为引入SQL语句的排队机制,将可能存在行锁冲突的语句放在一个组内排序,从而减少行锁冲突带来的额外系统开销。StatementQueque和InventoryHint可以结合使用,不过在事务中,热点行更新必须是该事务的最后一条记录,因为