我有一张表关注列表,其中包含了今天几乎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作业,如果需要,它会执行清除是否是更好的解决方案?
答案 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)
执行批量加载时经常有帮助的事情是删除任何索引,执行批量加载,然后重新创建索引。这通常比数据库必须不断更新插入的每一行的索引要快得多。