在 Mysql table 中批量导入数据而不删除索引

Bulk data import in Mysql table without index drop

我们有一个 table,大小为 10 TB,50 亿行。

create table dummy_table (
  id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  type INT, -- ENUM type, only few fixed values
  create_date DATE,
  user VARCHAR(10),
  A VARCHAR(10),
  B VARCHAR(10),
  C VARCHAR(10),
  D VARCHAR(10)
)

数据是immutable(不需要update操作,只有批量插入(即每日数据加载)和批量删除(即每月数据清理))。

我们的主要用例是按用户搜索,然后键入,然后 creation_date。为此,我正在分析两种策略

  1. STRATEGY1:通过复合索引:

create index user_type_creation_date_idx on dummy_table(user, type, create_date)

  1. STRATEGY2:通过分区和索引

alter table dummy_table PARTITION BY LIST(type) SUBPARTITION BY HASH(YEAR(create_date) + MONTH(create_date)) ( PARTITION pA VALUES IN (0) ( SUBPARTITION s0, SUBPARTITION s1, .....) create index user_idx on dummy_table(user)

SELECT 操作给出几乎相同的执行时间。我面临的问题是批量插入。我们正在尝试从其中的 s3 文件中提取 3000 万行(4.2 GB)。

没有索引和分区,加载那么多数据大约需要 360 秒。但是使用 STRATEGY2,数据加载时间增加到 850 秒,使用 STRATEGY1,它仍然比过去的 15000 秒 运行ning并且还在继续。

删除索引不在范围内,因为创建一个索引需要 7 个多小时,我们计划再创建四个复合索引

- index on user, A
- index on user, B
- index on user, C
- index on user, D

这是我的问题:

版本: MySQL: 5.6

方案A:包含索引,不分区,按用户+类型+create_date对传入数据进行预排序。这将是最快的。但它需要空间来预排序 CSV 文件。

方案B:无索引,无分区,只加载数据,然后ADD INDEX。这需要大量磁盘 space 来进行排序。这个计划可能和计划 A 一样快。

方案C:如果不需要id(user+type+create_date)是唯一的,那么去掉id并执行A计划。现在这是最好的方法。

问题:

  • 分区对您的情况没有任何性能优势。
  • 按顺序插入行或通过排序建立索引 -- 两者都比随机插入行快得多。
  • 更多索引:(user + A/B/C/D) -- 这进一步需要将 user 作为 PK 中的 first 项。每个 user 有多少行? (听起来像数百万?)
  • 您提到每月删除。这确实需要 PARTITION BY RANGE(TO_DAYS(...)) 以及每月的分区。这是因为 DROP PARTITIONDELETE 快得多。有关详细信息,请参阅 this。所以,现在我推荐A或者C,再加上按月分区。
  • 如果一定要有id,那么为了共同SELECT(而且因为数据量巨大),

PRIMARY KEY(user, type, create_date, id), -- clustered, unique, etc
INDEX(id)   -- sufficient to keep AUTO_INCREMENT happy

数学让我困惑:5B 行,每行约 100 字节,大约 1TB(在 InnoDB 开销之后),但你说 10TB。

我使用了InnoDB;如果您使用其他引擎,我可能需要修改我的答案。

进一步思考'out of the box'...

使用 InnoDB 和并行加载,您应该能够使用 "transportable tablespaces" 执行以下操作。但直到 5.7.4 才可用于分区。

这将涉及进行一系列单独的加载,每个加载到一个单独的 table。完成后使用 "transportable tablespaces" 将每个移动到主 table 作为分区。