优化 mysql table 1.5m 记录,其中大部分被软删除
optimizing mysql table with 1.5m records where most are soft deleted
我有一个 MySQL table 大约有 150 万条记录,table 大小为 1.3GB
我在 table 中使用软删除机制,这意味着我有一个列 deleted_at
指示该行是否已被删除以及何时删除。如果记录没有被删除那么 deleted_at
值为 NULL
在这150万条记录中,只有3万条没有被软删除。这意味着它们经常被访问,而其他记录几乎不被访问,但在某些情况下它们是。
所以这个 table 被大量使用并查询 none 已删除的记录,有时还查询软删除的记录。
我有 deleted_at
记录的 BTREE
索引类型(基数为 35K)。 table 随着时间的推移变得越来越重,显然它不是一个可扩展的解决方案。
table 引擎是 MyISAM
。大多数其他 table 都是 InnoDB
,但是这个 table 被 STORED PROCEDURE
大量查询,当我更改为 InnoDB
时,查询速度变慢了。
我正在寻找不涉及硬件更改的解决方案。当前的硬件足以让 table 获得良好的性能,但是一旦 table 增长得更多,情况就不会如此了。
我想到的事情:
- 分区,但我不能使用
partitions
,因为某些列已 FULL TEXT
编入索引。
- 将数据分成两个 table。一个用于已删除的行,一个用于 none 被频繁访问和查询的已删除行。此更改需要大量基础架构更改,因此我并不急于执行此操作。
- 创建一个新的 table,它将在 10/20 分钟内与原始 table 同步一次,而不是拆分,并且将仅包含 none 已删除的行。这将需要对基础设施进行小的改动,而且维护也更加容易和安全。拆分为两个 table 可能会由于查询失败而导致丢失记录,因为 "DELETE" 操作实际上会将行从一个 table 移动到另一个 table,因此需要复杂的机制
我还有哪些其他选择?我可以优先考虑 table 和 MySQL 中的某些行吗?内存明智。
我有 10.3.20-MariaDB
和 32GB 内存
MyISAM 不缓存行,它只缓存索引。它依赖于文件系统缓存来缓冲行。
因此您可以尝试确保至少将索引完全加载到缓存中:
- 增加
key_buffer_size
,使其至少与此 table 的 MyISAM 索引一样大。使用 SHOW TABLE STATUS
找出索引大小。
- 如果您有多个 MyISAM table,您可能需要专门为此 table 提供一个密钥缓存。参见 CACHE INDEX。
- 在启动时将索引预加载到键缓存中。参见 LOAD INDEX INTO CACHE。
您可能还想考虑为您的查询量身定制的多列索引。就像如果您有一个查询 WHERE user_id = 1234 AND deleted_at IS NULL
,您应该在 (user_id, deleted_at)
上创建一个索引。
您需要哪些索引取决于您要优化的查询。
坦率地说,我会拆分 table 所以删除的行在一秒钟内 table。这将使您的 table 大小减少 98%,并且可能使查询 运行 足够快,以至于您不再需要使用 MyISAM。
我有一个 MySQL table 大约有 150 万条记录,table 大小为 1.3GB
我在 table 中使用软删除机制,这意味着我有一个列 deleted_at
指示该行是否已被删除以及何时删除。如果记录没有被删除那么 deleted_at
值为 NULL
在这150万条记录中,只有3万条没有被软删除。这意味着它们经常被访问,而其他记录几乎不被访问,但在某些情况下它们是。
所以这个 table 被大量使用并查询 none 已删除的记录,有时还查询软删除的记录。
我有 deleted_at
记录的 BTREE
索引类型(基数为 35K)。 table 随着时间的推移变得越来越重,显然它不是一个可扩展的解决方案。
table 引擎是 MyISAM
。大多数其他 table 都是 InnoDB
,但是这个 table 被 STORED PROCEDURE
大量查询,当我更改为 InnoDB
时,查询速度变慢了。
我正在寻找不涉及硬件更改的解决方案。当前的硬件足以让 table 获得良好的性能,但是一旦 table 增长得更多,情况就不会如此了。
我想到的事情:
- 分区,但我不能使用
partitions
,因为某些列已FULL TEXT
编入索引。 - 将数据分成两个 table。一个用于已删除的行,一个用于 none 被频繁访问和查询的已删除行。此更改需要大量基础架构更改,因此我并不急于执行此操作。
- 创建一个新的 table,它将在 10/20 分钟内与原始 table 同步一次,而不是拆分,并且将仅包含 none 已删除的行。这将需要对基础设施进行小的改动,而且维护也更加容易和安全。拆分为两个 table 可能会由于查询失败而导致丢失记录,因为 "DELETE" 操作实际上会将行从一个 table 移动到另一个 table,因此需要复杂的机制
我还有哪些其他选择?我可以优先考虑 table 和 MySQL 中的某些行吗?内存明智。
我有 10.3.20-MariaDB
和 32GB 内存
MyISAM 不缓存行,它只缓存索引。它依赖于文件系统缓存来缓冲行。
因此您可以尝试确保至少将索引完全加载到缓存中:
- 增加
key_buffer_size
,使其至少与此 table 的 MyISAM 索引一样大。使用SHOW TABLE STATUS
找出索引大小。 - 如果您有多个 MyISAM table,您可能需要专门为此 table 提供一个密钥缓存。参见 CACHE INDEX。
- 在启动时将索引预加载到键缓存中。参见 LOAD INDEX INTO CACHE。
您可能还想考虑为您的查询量身定制的多列索引。就像如果您有一个查询 WHERE user_id = 1234 AND deleted_at IS NULL
,您应该在 (user_id, deleted_at)
上创建一个索引。
您需要哪些索引取决于您要优化的查询。
坦率地说,我会拆分 table 所以删除的行在一秒钟内 table。这将使您的 table 大小减少 98%,并且可能使查询 运行 足够快,以至于您不再需要使用 MyISAM。