优化 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 的变化而改变。
如果 id
是 PRIMARY 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 以获取行数的主要内容,如果未能解决你的问题,请参考以下文章
WordCount--统计输入文件的字符数行数单词数(java)--初级功能