【问题标题】:How to optimize mysql indexes so that INSERT operations happen quickly on a large table with frequent writes and reads?如何优化 mysql 索引,使 INSERT 操作在频繁写入和读取的大表上快速发生?
【发布时间】:2010-11-09 07:08:56
【问题描述】:

我有一个表关注列表,其中包含今天近 300 万条记录。

mysql>  select count(*) from watchlist;
+----------+
| count(*) |
+----------+
|  2957994 |
+----------+

它被用作记录大型电子商务网站(超过 50,000 种产品)上产品页面浏览量的日志。它记录了查看产品的productID、查看者的IP地址和USER_AGENT。以及发生时间的时间戳:

mysql> show columns from watchlist;
+-----------+--------------+------+-----+-------------------+-------+
| Field     | Type         | Null | Key | Default           | Extra |
+-----------+--------------+------+-----+-------------------+-------+
| productID | int(11)      | NO   | MUL | 0                 |       |
| ip        | varchar(16)  | YES  |     | NULL              |       |
| added_on  | timestamp    | NO   | MUL | CURRENT_TIMESTAMP |       |
| agent     | varchar(220) | YES  | MUL | NULL              |       |
+-----------+--------------+------+-----+-------------------+-------+

然后在整个网站的后端(例如检查 GoogleBot 索引的内容)和前端(例如“最近查看的产品”的侧边栏框和显示用户“您所在地区的人也喜欢”等)。

为了让这些“报告”页面和侧边栏快速加载,我将索引放在相关字段上:

mysql> show indexes from watchlist;
+-----------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table     | Non_unique | Key_name  | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+-----------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| watchlist |          1 | added_on  |            1 | added_on    | A         |        NULL |     NULL | NULL   |      | BTREE      |         |
| watchlist |          1 | productID |            1 | productID   | A         |        NULL |     NULL | NULL   |      | BTREE      |         |
| watchlist |          1 | agent     |            1 | agent       | A         |        NULL |     NULL | NULL   | YES  | BTREE      |         |
+-----------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+

如果没有索引,例如带有侧边栏的页面将花费大约 30-45 秒执行查询以获取 7 个最近的 ProductID。使用索引需要

问题在于, 使用 INDEXES,产品页面本身的加载时间越来越长,因为随着表的增长,写入操作需要 5 秒以上的时间。此外,每次查看产品页面(大约每 2 秒一次)时,mysqld 进程都会出现峰值,相当于可用 CPU 的 10-15%。我们已经不得不升级服务器硬件,因为在以前的服务器上它达到了 100% 并导致 mysqld 崩溃。

我的计划是尝试 2-table 解决方案。一个表用于 INSERT 操作,另一个表用于 SELECT 操作。我计划在 INSERT 表达到 1000 条记录时使用 TRIGGER 清除,并将最旧的 900 条记录复制到 SELECT 表中。报告页面是实时(最近查看)和分析(哪个区域)的混合体,但实时页面往往只需要少量新记录,而分析页面不需要知道最近的记录趋势(即最后 1000 次观看)。所以我可以将小表用于前者,而将大表用于后者的报告。


我的问题:这是解决这个问题的理想方案吗?

另外:在 MySQL 中使用 TRIGGERS 是否可以很好地 触发 trigger_statement,使其花费更长的时间,但不会消耗太多 CPU?每 30 分钟运行一次 cron 作业 会不会是更好的解决方案?

【问题讨论】:

  • 只是为了说明您一次要插入多少条记录?您是否按照 Adam 的建议批量加载?
  • 您是否考虑过使用 INNOdb,以便获得行级锁定,而不是像 MyISAM 那样锁定整个表。
  • 每次插入一条记录。我可以试试 InnoDB

标签: mysql optimization indexing


【解决方案1】:

将单行写入数据表的操作不应花费 5 秒,无论表有多大。

您的聚集索引是否基于时间戳字段?如果不是,它应该是,所以你不会在某个地方写到你的桌子中间。此外,请确保您使用的是 InnoDB 表 - MyISAM 未针对写入进行优化。

我建议写入两张表:一张长期表,一张带有很少或没有索引的短期报告表,然后根据需要转储。

另一种解决方案是使用 memcached 或内存数据库来存储实时报告数据,因此不会影响生产数据库。

再想一想:这些报告中的任何一个究竟必须有多“真实”?也许在时间的基础上检索一个新列表而不是为每个页面浏览一次就足够了。

【讨论】:

    【解决方案2】:

    一个快速修复可能是使用INSERT DELAYED 语法,它允许mysql 将插入排队并在有时间时执行它们。不过,这可能不是一个非常可扩展的解决方案。

    我实际上认为您将尝试的原则是合理的,尽管我不会使用触发器。我建议的解决方案是让数据累积一天,然后使用夜间运行的批处理脚本将数据清除到辅助日志表。这主要是因为这些频繁的一千行传输仍然会给服务器带来相当大的负载,并且因为我并不真正信任 MySQL 触发器的实现(尽管这不是基于任何真实的内容)。

    【讨论】:

    • 更新:MySQL 不再支持 INSERT DELAYED。
    【解决方案3】:

    您可以使用某种数据库写卸载来代替优化索引。您可以通过异步队列(例如 ActiveMQ)将写入委托给某个后台进程。将消息插入 ActiveMQ 队列非常快。我们正在使用 ActiveMQ,并且在测试平台上有大约 10-20K 的插入操作(这是单线程测试应用程序!所以你可以有更多)。

    【讨论】:

      【解决方案4】:

      以这种方式重建表时寻找“影子表”,您无需写入生产表。

      【讨论】:

        【解决方案5】:

        即使使用之前提到的 InnoDB 表或 MyISAM,我也遇到了同样的问题,没有针对写入进行优化,并通过使用第二个表写入临时数据(可以定期更新主大表)解决了这个问题。主表超过 1800 万条记录,用于只读记录并将结果写入第二个小表。

        问题是插入/更新到大型主表,需要一段时间,如果队列等待中存在多个更新或插入,即使启用了 INSERT DELAYED 或 UPDATE [LOW_PRIORITY] 选项也会更糟

        为了更快,请先读取小辅助表,搜索记录时,如果有记录,则仅处理第二个表。使用主大表作为参考并仅获取新数据记录 *如果数据不在辅助小表上,您只需从主表中读取记录(在 InnoDB 表或 MyISAM 方案上读取速度很快),然后插入该记录记录在小秒表上。

        就像一个魅力,在不到 5 秒的时间内从巨大的主记录中读取 2000 万条记录并在不到一秒的时间内将 100K 到 300K 记录写入第二个小表。

        这很好用。

        问候

        【讨论】:

          【解决方案6】:

          在进行批量加载时通常会有所帮助的是删除所有索引,进行批量加载,然后重新创建索引。这通常比数据库必须为插入的每一行不断更新索引要快得多。

          【讨论】:

          • 不会工作,因为读取和写入是交错的。重建索引会锁定整个表几分钟,这会导致网站无法使用。
          • 一般来说它仍然是一个有效的优化,它可能仍然比执行一千个并发插入更快。
          猜你喜欢
          • 2011-12-27
          • 1970-01-01
          • 2017-10-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-02-13
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多