分析一下题干的几个关键信息:
1、一天一千万数据。
2、只查最近30天的数据
3、买家和商家都需要查询
通过以上三个信息,我们可以得出以下结论:
1、单表扛不住:
1天就1000万数据,一年就30多亿数据,那么这个量如果用一张单表存放的话,数据库肯定扛不住。
2、可以做数据归档
因为用户只查询最近30天的数据,所以,可以简单的按照时间来划分冷热数据,30天内的数据为热数据,30天以外的数据为冷数据。冷数据和热数据可以做分离,即针对30天以上的数据做归档。
3、归档后单表还是扛不住
但是,归档之后,热数据的表还要保留30的数据,也就是3个亿,那还是很多的,单靠数据库也扛不住。需要考虑做分库分表
4、分表键不能直接选择买家或者卖家
因为要同时满足买家和卖家的查询,不可能只基于买家或者卖家去做分表。如果按照卖家 id 分表,那么买家的查询就会跨表,查询很慢。按照买家 id 分表也是同理。
5、基本不太可能使用缓存提效
这个很好理解,一方面是数据量太大了,另外一方面订单信息可能会频繁修改,数据一致性不太好保障,这里用缓存不合适。
那么,这个问题最终就变成了一个,如何基于海量数据(3亿)数据走高效查询,并且要支持多个分表键。
首先,我们可以用支持海量数据存储和查询的各种分布式是数据库,如TiDB(pingcap)、OceanBase(蚂蚁)、Spanner(google)等。
用了分布式数据库之后,为了提升买家和卖家的查询效率,需要分别创建索引:
CREATE TABLE Orders (
order_id BIGINT AUTO_INCREMENT PRIMARY KEY,
buyer_id BIGINT NOT NULL,
seller_id BIGINT NOT NULL,
order_date DATETIME NOT NULL,
amout DECIMAL(10, 2),
status ENUM('Pending', 'Completed', 'Cancelled', 'Refunded'),
INDEX idx_buyer_date (buyer_id, order_date),
INDEX idx_seller_date (seller_id, order_date)
);
创建买家ID+时间、卖家 ID+时间等多个联合索引,来提升按照时间查询的效率。
除了分布式数据库,还可以用分库分表的方案,在分库分表中我们介绍过,按照买家 ID 分表后,卖家的查询怎么办问题。
也就是说,我们在做了数据归档之后(一般是先分表之后然后再归档,但是不重要,你知道需要归档即可。),针对最近30填的数据,可以按照买家 ID进行分表,这么大的量 ,建议分成64张表,这样平均下来,单张表就大概有1000万 * 30 / 32
不到一千万条数据了。这时候按照买家 ID 的查询就很高效了。
之所以按照买家 ID 先做分表,就是为了避免数据倾斜。建表语句如下:
CREATE TABLE orders (
order_id BIGINT AUTO_INCREMENT PRIMARY KEY,
buyer_id BIGINT NOT NULL,
seller_id BIGINT NOT NULL,
order_date DATETIME NOT NULL,
amout DECIMAL(10, 2),
status ENUM('Pending', 'Completed', 'Cancelled', 'Refunded'),
INDEX idx_user_date (buyer_id, order_date)
);
这样就解决了买家的查询效率问题,那么卖家的查询呢,我们可以采用数据同步的方案,利用空间换时间的方案,将数据冗余一份,再以卖家 ID 做分表:
CREATE TABLE orders_seller (
order_id BIGINT AUTO_INCREMENT PRIMARY KEY,
buyer_id BIGINT NOT NULL,
seller_id BIGINT NOT NULL,
order_date DATETIME NOT NULL,
amout DECIMAL(10, 2),
status ENUM('Pending', 'Completed', 'Cancelled', 'Refunded'),
INDEX idx_seller_date (seller_id, order_date)
);
我们说同步一张卖家维度的表来,但是其实所有的写操作还是要写到买家表的,只不过需要准实时同步的方案同步到卖家表中。也就是说,我们的这个卖家表理论上是没有业务的写操作,只有读操作的。