我有一个表格监视列表,其中包含今天将近 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。使用索引需要 <0.2 秒。
问题在于, INDEXES 产品页面本身的加载时间越来越长,因为随着表的增长,写入操作需要超过 5 秒。此外,每次查看产品页面时,mysqld 进程的可用 CPU 达到 10-15%(大约每 2 秒一次)。我们已经不得不升级服务器硬件,因为在以前的服务器上它达到 100% 并导致 mysqld 崩溃。
我的计划是尝试 2 表解决方案。一个表用于 INSERT 操作,另一个表用于 SELECT 操作。我计划在 INSERT 表达到 1000 条记录时使用 TRIGGER 清除,并将最早的 900 条记录复制到 SELECT 表中。报告页面是实时(最近查看)和分析(哪个区域)的混合体,但实时页面往往只需要少量的新记录,而分析页面不需要知道最近的趋势(即最近 1000 次浏览)。所以我可以用小表做前者,用大表做后者报告。
我的问题:这是该问题的理想解决方案吗?
此外:在 MySQL 中使用 TRIGGERS 是否可以优化 trigger_statement,使其花费更长的时间,但不会消耗太多 CPU?每 30 分钟运行一次 cron 作业 是否更好,并且在需要时执行清除是更好的解决方案吗?
最佳答案
将单行写入数据表的操作应该不会花费 5 秒,无论表有多大。
你的聚簇索引是基于timestamp字段的吗?如果不是,它应该是,所以你不会在 table 中间的某个地方写字。此外,请确保您使用的是 InnoDB 表 - MyISAM 未针对写入进行优化。
我建议写入两个表:一个长期表,一个短期报告表,很少或没有索引,然后根据需要转储。
另一种解决方案是为实时报告数据使用内存缓存或内存数据库,这样就不会对生产数据库造成影响。
再想一想:这些报告中的任何一个到底应该有多“活”?也许定时检索新列表而不是每次 页面浏览一次就足够了。
关于mysql - 如何优化mysql索引,让读写频繁的大表快速INSERT操作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1102378/