在 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。为此,我正在分析两种策略
- STRATEGY1:通过复合索引:
create index user_type_creation_date_idx on dummy_table(user, type, create_date)
- 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
这是我的问题:
- 在许多 SO post 中,我已经阅读过不创建分区,但似乎分区概念对于批量插入表现良好。有什么问题吗
我正在使用 STRATEGY1?
- 有没有像分区、子分区、mysql/innodb 属性 那样加快批量插入的速度,因为我们有单一(没有任何连接)table 的优势,那也不需要 UPDATE 吗?
- 有没有办法通过并行加载多个文件来提高批量插入速度? 从 S3 加载数据 正在阻止其他此类命令。我们可以 运行 它们以某种方式并行吗?
版本:
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 PARTITION
比 DELETE
快得多。有关详细信息,请参阅 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 作为分区。
我们有一个 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。为此,我正在分析两种策略
- STRATEGY1:通过复合索引:
create index user_type_creation_date_idx on dummy_table(user, type, create_date)
- 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
这是我的问题:
- 在许多 SO post 中,我已经阅读过不创建分区,但似乎分区概念对于批量插入表现良好。有什么问题吗 我正在使用 STRATEGY1?
- 有没有像分区、子分区、mysql/innodb 属性 那样加快批量插入的速度,因为我们有单一(没有任何连接)table 的优势,那也不需要 UPDATE 吗?
- 有没有办法通过并行加载多个文件来提高批量插入速度? 从 S3 加载数据 正在阻止其他此类命令。我们可以 运行 它们以某种方式并行吗?
版本: 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 PARTITION
比DELETE
快得多。有关详细信息,请参阅 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 作为分区。