布尔字段索引以删除分区 table 中的记录
Index on Boolean field to delete records in a partitioned table
我有一个很大的 MySQL table,其中可能包含 1 亿条记录。 table 的模式是这样的-
Id varchar(36), --guid, primary key
IsDirty bit(1),
CreatedOn(Date),
Info varchar(500)
我在 CreatedOn 字段上创建了一个分区,它为每月数据创建了一个分区。 table 中的某些行已更新并且 isDirty
设置为 1。最多只有 10% 的行具有 IsDirty = 1
。有一个进程每晚运行并删除 6 个月前值为 IsDirty = 0 的数据。
如果我也在 IsDirty 字段上创建索引,是否会提高性能?据我所知,在位字段上创建索引可能不会增加太多性能,但删除记录后重新索引可能会因索引而降低性能。
我的理解对吗?是否有更好的方法来实现所需的功能?
有一条经验法则表明,最好为具有高基数的列编制索引。基数是列中不同值的估计数量。当您执行 show indexes from your_table;
时,您会看到,您的 IsDirty
列的基数为 2。非常糟糕。
不过这并没有考虑数据的分布。当只有 10% 有 IsDirty = 1
时,像 select * from your_table where IsDirty = 1
这样的查询将从索引中受益。另一方面,检查 IsDirty = 0
的删除作业不会受益,因为简单地进行完整的 table 扫描会更便宜,因为使用二级索引意味着从索引中可以得到主键读取(在每个二级索引中存储主键,因此使主键尽可能小总是好的)来标识要读取的行。
manual 说明了何时首选完整 table 扫描:
Each table index is queried, and the best index is used unless the optimizer believes that it is more efficient to use a table scan. At one time, a scan was used based on whether the best index spanned more than 30% of the table, but a fixed percentage no longer determines the choice between using an index or a scan. The optimizer now is more complex and bases its estimate on additional factors such as table size, number of rows, and I/O block size.
另请注意,位数据类型不适合存储值 0 或 1。有一个 bool 数据类型(在内部实现为 tinyint(1))。我想我已经在某处读到一个原因,但我忘记了)。
不要为分区而烦恼,它不太可能对性能有帮助。无论如何,您将需要越来越多的分区并使用 PARTITION BY RANGE(to_days(..))
。您将无法使用 DROP PARTITION
,这会使删除速度非常快。
我会暂时收回。 可能有效,可能允许DROP PARTITION
,但我对语法感到困惑。
PARTITION BY RANGE(TO_DAYS(CreatedOn))
SUBPARTITION BY LINEAR KEY(IsDirty)
SUBPARTITIONS 2
如果你确实每晚都大吃 DELETE
,那么
- 每小时(或连续)执行一次,这样删除就不会太大
- 按照讨论的方式分块 here
还有
INDEX(IsDirty, CreatedOn) -- in this order.
(注意:如果子分区可以工作,则不需要此索引。)
其他提示:
- 使用 InnoDB。
- 将
innodb_buffer_pool_size
设置为大约 RAM 大小的 70%。
- 由于访问的随机性,UUID 对于大型 tables 是可怕的——因此很高 I/O。
Id varchar(36), --guid, primary key
-- 打包成BINARY(16)
。 (如果您需要帮助,请告诉我。)节省 space --> 收缩 table --> 削减 I/O.
- 由于 uuid 的可怕性,分区 可能 有助于避免很多 I/O -- 这是因为本月的所有插入内容都将进入一个分区。也就是说,"working set",因此 buffer_pool 大小可以更小。
我有一个很大的 MySQL table,其中可能包含 1 亿条记录。 table 的模式是这样的-
Id varchar(36), --guid, primary key
IsDirty bit(1),
CreatedOn(Date),
Info varchar(500)
我在 CreatedOn 字段上创建了一个分区,它为每月数据创建了一个分区。 table 中的某些行已更新并且 isDirty
设置为 1。最多只有 10% 的行具有 IsDirty = 1
。有一个进程每晚运行并删除 6 个月前值为 IsDirty = 0 的数据。
如果我也在 IsDirty 字段上创建索引,是否会提高性能?据我所知,在位字段上创建索引可能不会增加太多性能,但删除记录后重新索引可能会因索引而降低性能。
我的理解对吗?是否有更好的方法来实现所需的功能?
有一条经验法则表明,最好为具有高基数的列编制索引。基数是列中不同值的估计数量。当您执行 show indexes from your_table;
时,您会看到,您的 IsDirty
列的基数为 2。非常糟糕。
不过这并没有考虑数据的分布。当只有 10% 有 IsDirty = 1
时,像 select * from your_table where IsDirty = 1
这样的查询将从索引中受益。另一方面,检查 IsDirty = 0
的删除作业不会受益,因为简单地进行完整的 table 扫描会更便宜,因为使用二级索引意味着从索引中可以得到主键读取(在每个二级索引中存储主键,因此使主键尽可能小总是好的)来标识要读取的行。
manual 说明了何时首选完整 table 扫描:
Each table index is queried, and the best index is used unless the optimizer believes that it is more efficient to use a table scan. At one time, a scan was used based on whether the best index spanned more than 30% of the table, but a fixed percentage no longer determines the choice between using an index or a scan. The optimizer now is more complex and bases its estimate on additional factors such as table size, number of rows, and I/O block size.
另请注意,位数据类型不适合存储值 0 或 1。有一个 bool 数据类型(在内部实现为 tinyint(1))。我想我已经在某处读到一个原因,但我忘记了)。
不要为分区而烦恼,它不太可能对性能有帮助。无论如何,您将需要越来越多的分区并使用 PARTITION BY RANGE(to_days(..))
。您将无法使用 DROP PARTITION
,这会使删除速度非常快。
我会暂时收回。 可能有效,可能允许DROP PARTITION
,但我对语法感到困惑。
PARTITION BY RANGE(TO_DAYS(CreatedOn))
SUBPARTITION BY LINEAR KEY(IsDirty)
SUBPARTITIONS 2
如果你确实每晚都大吃 DELETE
,那么
- 每小时(或连续)执行一次,这样删除就不会太大
- 按照讨论的方式分块 here
还有
INDEX(IsDirty, CreatedOn) -- in this order.
(注意:如果子分区可以工作,则不需要此索引。)
其他提示:
- 使用 InnoDB。
- 将
innodb_buffer_pool_size
设置为大约 RAM 大小的 70%。 - 由于访问的随机性,UUID 对于大型 tables 是可怕的——因此很高 I/O。
Id varchar(36), --guid, primary key
-- 打包成BINARY(16)
。 (如果您需要帮助,请告诉我。)节省 space --> 收缩 table --> 削减 I/O.- 由于 uuid 的可怕性,分区 可能 有助于避免很多 I/O -- 这是因为本月的所有插入内容都将进入一个分区。也就是说,"working set",因此 buffer_pool 大小可以更小。