如何优化mysql索引,以便在频繁写入和读取的大型表上快速执行INSERT操作?

时间:2009-07-09 07:37:22

标签: mysql optimization indexing

我有一张表关注列表,其中包含了今天几乎3Mil的记录。

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      |         |
+-----------+------------+-----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+

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

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

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


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

另外:使用MySQL中的TRIGGERS可以很好 trigger_statement,这样它需要更长时间,但不会占用太多CPU?是否会每30分钟运行一次 cron作业,如果需要,它会执行清除是否是更好的解决方案?

6 个答案:

答案 0 :(得分:2)

的单行写入数据表需要花费5秒钟,无论表格有多大。

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

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

另一个解决方案是使用memcached或内存数据库来处理实时报告数据,因此生产数据库没有受到任何影响。

还有一个想法:这些报告中的任何一个都必须“生活”吗?也许就定时检索新列表而不是每个页面视图一次就足够了。

答案 1 :(得分:0)

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

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

答案 2 :(得分:0)

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

答案 3 :(得分:0)

以这种方式重建表时查找'影子表',您不需要写入生产表。

答案 4 :(得分:0)

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

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

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

像魅力一样工作,需要花费不到5秒的时间从巨大的硕士记录中读取2000万条记录,并在不到一秒的时间内写入第二张小表100K至300K记录。

这很好用。

此致

答案 5 :(得分:-2)

执行批量加载时经常有帮助的事情是删除任何索引,执行批量加载,然后重新创建索引。这通常比数据库必须不断更新插入的每一行的索引要快得多。