优化 SQL 以获取行数

Posted

技术标签:

【中文标题】优化 SQL 以获取行数【英文标题】:Optimize SQL to get rows count 【发布时间】:2016-10-16 13:14:23 【问题描述】:

我的网站上有一个页面可以跟踪访问它的人数,在另一部分我显示包含有关访问该页面的用户的信息的数据,它一次只显示大约 10 个。

问题是我需要创建分页,所以我需要知道每次我的表上有多少数据,这导致显示页面需要一些时间来加载 2-3 秒,有时是 7-10 秒,因为我拥有数百万的记录。我想知道,如何让这个页面加载得更快。

Select COUNT(*) as Count from visits

【问题讨论】:

你有自动增量列吗?你会从这个表中删除记录吗? @Prdp 我确实有自动增量,我还没有删除数据,但我会在未来 谢谢@GordonLinoff 使用什么数据库引擎?解决方案取决于它是 MyISAM、InnoDB 还是其他。 如果你不删除你可以运行这个select auto_increment_column from visits order by auto_increment_column desc Limit 1 【参考方案1】:

我的第一反应是。 . .如果您一次分页记录 10 个,为什么需要超过一百万的总计数?

其次,计算一百万行不应该花费很长时间,除非您的行很宽(很多列或宽列)。如果是这样的话,那么:

select count(id) from t;

可以提供帮助,因为它将显式使用索引。请注意,由于缓存,第一次运行可能比后续运行慢。

如果您决定确实需要精确的行数,那么使用 mysql 加速它的唯一真正选择是创建触发器来维护另一个表中的计数。但是,这会减慢插入和删除的速度,这可能不是一个好主意。

【讨论】:

COUNT(id) 不能比 COUNT(*) 快。前者检查每个id 是否为NULL。而且,在 InnoDB 中,它不允许选择较小的索引。 @RickJames。 . .你知道COUNT(*) 是否会选择最小的索引进行计数吗?我担心 MySQL 可能会读取 COUNT(*) 的数据页,而不仅仅是读取索引的页。 InnoDB 可以为COUNT(*) 的全表扫描选择任何索引。而且,是的,它选择了“最小的”,尽管我不知道它使用什么度量来表示“最小的”。它确实没有考虑当前是否缓存了最小的索引;这可能随着版本 8.0 的变化而改变。 如果 idPRIMARY KEY,则该索引与数据聚集在一起,因此 `COUNT(id) 强制进行全表扫描。 @RickJames。 . . “最小”将是索引页的最少数量。我在想,如果记录很宽,那么count(*) 通常会读取所有记录。但是,count(othercol) 只需读取othercol 的索引即可——这将节省成本。【参考方案2】:

最好的答案是说“大约 1,234,000 次访问”,不是确切的数字。然后每天(或其他)计算它。

但如果你必须有准确的计数,...

如果此表是“只写”,那么有一个解决方案。它涉及将其视为数据仓库中的“事实”表。然后创建并维护一个“汇总表”,例如每小时一行。那么COUNT就变成了:

SELECT SUM(hourly_count) FROM SummaryTable;

这会更快,因为要扫描的内容要少得多。但是,问题在于它不包括最后(部分)小时的计数。但是,如果您使用INSERT ... ON DUPLICATE KEY UPDATE ... 来增加当前小时的计数器或插入带有“1”的新行,则可以解决此问题。

更多信息是here

但是,在我们走得太远之前,请告知我们新的“访问”发生的频率。

【讨论】:

抱歉您的问题回复晚了,至少每秒访问一次,目前平均每秒访问10次 10/秒 = 8M/年。在大多数人看来,这与 5M 或 10M 没有太大区别。此外,在计算和显示确切计数所需的时间中,它会增加 20-30。 (这些都是反对努力获得准确数字的论据。) 另一种方法:每分钟计算一次准确计数并将其存储到单单元格表中以供 UI 使用。显示时四舍五入到最接近的1000;这将为用户提供可能不准确的线索。【参考方案3】:

如果不更改服务器的硬件或添加更多服务器以并行运行它,您无法使查询变得更快。在第二种情况下,最好迁移到 nosql 数据库。

我的方法是减少记录数。您可以通过一些临时表来记录过去一小时/一天的访问日志,然后运行一个删除数据的 cronjob,或将其移动到另一个表以进行日志存储。

【讨论】:

我需要所有关于用户活动的最实时反馈,我不能使用 cronjob。此时更改数据库可能不是我们的最佳选择。【参考方案4】:

您通常不需要知道分页的确切行数

SELECT COUNT(*) FROM
(SELECT TOP 10000 * FROM visits) as v

会告诉你,至少有 1000 页。在大多数情况下,您不需要了解更多。

如果您想要一些合理的估计,您可以将总计数存储在某处并不时更新。如果您需要确切的数字,您可以使用触发器来保持它的真实性。当然,更新的信息越多,价格就越贵。

【讨论】:

这闻起来像 SQL Server 语法,而不是 MySQL。 你说得对,这不统一太可惜了。想象一下 LIMIT。【参考方案5】:

从实际(业务需求)的角度确定限制(比如说,最后 1000 个)。具有 auto_increment 索引 (id) 或时间戳 (createdon)。最多抓取 1000 条记录

select count(*) from (select id from visits order by id desc limit 1000)

或者抓取所有 1000 并在客户端 (php) 上进行分页(就好像你分页 mysql 仍然会遍历这些记录):

select * from visits order by id desc limit 1000

【讨论】:

以上是关于优化 SQL 以获取行数的主要内容,如果未能解决你的问题,请参考以下文章

作业-- 统计文本文件中的字符数单词数行数

python脚本: 双向统计文件字符单词数行数

Android 简单统计文本文件字符数单词数行数Demo

WordCount--统计输入文件的字符数行数单词数(java)--初级功能

linux wc指令(wc命令)(Word count)(统计文件的字节数单词数行数等信息)

linux wc指令(wc命令)(Word count)(统计文件的字节数单词数行数等信息)