2

我有一个非常简单的表,用于记录对成员资料的访问,具有多列键(member_id、visitor_id、month_visited)和更精确的日期。month_visited 是这样的 CHAR(7) 列:'2013-10'

每个新月,我想在另一个表中压缩上个月的数据,然后将其删除。

我的要求很简单:

DELETE FROM visits WHERE month_visited = '2013-10'

删除这些行需要 AGES 时间,比如在我的专用服务器上需要几分钟。当我只查询一个简单的SELECT COUNT(*) FROM visits.

我在 2013-10 年有 180 万个条目。

但这需要很长时间。当我尝试

EXPLAIN SELECT * FROM visits WHERE month_visited = "2013-10"

它告诉我:

id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   SIMPLE  visits  ref idx_month_visited   idx_month_visited   21  const   1782148 Using where

“在哪里使用”,认真的吗?

编辑:对不起,我忘了指定我还在month_visited列上添加了一个索引:)(实际上,正如解释所示,但它没有使用它......)

我怎样才能改进那些(显然)简单的查询?我是 MySQL 的菜鸟,但我认为执行这些查询需要几分钟时间是不正常的。

感谢您的任何意见!

最好的祝福,

4

2 回答 2

5

我在这个答案中总结了我的评论。

In general, when an index is not being used, it's because using it won't help much. That is, it will not save much time compared with a full table scan (this tends to happen when the cardinality of the index is low). This seems to be the case here since you have about the same number of rows in the table than rows that you want to select. In this case, a full scan is usually cheaper than using the index.

Also, deleting is a "write" action. Indices optimize reads, at the cost of making writes more expensive (because of index rebuilding on writes). So the fact that you have some complex indices does not help, but aggravates the problem. An index makes sense when it narrows down the number of rows to retrieve; otherwise it offers no real gain and could even impose some extra overhead. Also, an index may, in the best case, make a SELECT more efficient. But it won't make writes (insert, update and delete) work faster; on the contrary, it will make them perform worse.

因此,您应该尝试摆脱并非绝对必要的索引。请记住,索引是一种权衡,它可能会使读取操作(选择)更快,但代价是使写入操作(插入、更新、删除)更慢。这是因为必须在写入后重建索引。

您可能想尝试一下:“如果您要从表中删除许多行,使用 DELETE QUICK 然后使用 OPTIMIZE TABLE 可能会更快。这会重建索引而不是执行许多索引块合并操作。” dev.mysql.com/doc/refman/5.0/en/delete.html

Yet another option (may work or not, just thinking out loud here): if you want to delete all but a few rows from visitss, perhaps you could insert the rows "WHERE month != '2013-10' into an auxiliary table, TRUNCATE visits, then insert back the rows from the aux table into visits and finally TRUNCATE the aux table. As you point out, though, you'll need to put up some sort of locking while this process is running.

于 2013-11-01T12:57:59.610 回答
1

只有在条件中使用了第一个键组件时,才能使用多列键。在您的情况下,这意味着您的密钥(member_id, visitor_id, month_visited)仅在您的条件包括

  • member_id 或
  • member_id 和 visitor_id
  • member_id 和 visitor_id 和 month_visited。

创建一个month_visited作为第一个组件的密钥。

于 2013-11-01T00:53:27.480 回答