【问题标题】:SQL Query Slow? Should it be?SQL查询慢?应该是吗?
【发布时间】:2010-09-12 05:06:43
【问题描述】:

使用 SQLite,得到一个大约 10 列的表。有大约 2500 万行。

该表在“sid、uid、区域、类型”上有一个索引。

我像这样运行一个选择:

SELECT sid from actions where uid=1234 and area=1 and type=2

这会返回 1571 个结果,并且需要 4 分钟 才能完成。

这样还好吗?

我远不是 SQL 专家,所以希望有人能补充我所缺少的内容。为什么索引所有内容可能需要 4 分钟以上的时间?

有什么推荐的资源来学习如何实现高 SQL 性能?我觉得很多 Google 搜索结果只是给了我意见或轶事,我不介意一本可靠的书。

【问题讨论】:

    标签: sql performance sqlite indexing


    【解决方案1】:

    改为创建uid+area+type 索引,或uid+area+type+sid

    【讨论】:

      【解决方案2】:

      由于索引从 sid 列开始,它必须对索引或表进行扫描(从开头开始,读取到结尾)以查找与其他 3 列匹配的数据。这意味着它必须读取所有 2500 万行才能找到答案。即使它只是读取索引的行而不是表,这也是很多工作。

      想象一下大纽约都会区的电话簿,按姓氏、名字(带有“索引”)组织。

      你提交SELECT [Last Name] FROM NewYorkPhoneBook WHERE [First Name] = 'Thelma'

      它必须阅读所有 2500 万条条目才能找到所有这些 Thelmas。除非您指定姓氏,然后可以直接转到该姓氏第一个出现的页面(搜索),或者有一个按名字组织的索引(在索引上搜索,然后在表上搜索,也就是“书签查找”),没有办法。

      您为加快查询速度而创建的索引位于uid, area, type。您可以包含 sid,但如果 sid 是主键的一部分,则将其省略。

      注意:表通常有多个索引。请注意,索引越多,写入性能越慢。不必要的索引会降低整体性能,有时甚至会从根本上降低。测试和最终的经验将帮助指导你。此外,将其推理为现实世界的问题(例如我的电话簿示例)确实会有所帮助。如果它对电话簿(和单独的电话簿索引)没有意义,那么它在数据库中可能没有意义。

      还有一件事:即使您在这些列上设置了索引,如果您的查询最终会拉取主表中很大比例的行,那么扫描表仍然比执行书签查找(查找索引,然后查找找到的每一行的表)。是通过搜索进行书签查找还是进行表扫描的确切“临界点”不是我可以告诉你的,但它基于可靠的数学。

      【讨论】:

        【解决方案3】:
        • 索引并不是真正有用,因为它确实以错误的字段开头...这意味着表扫描。

        • 看起来您那里有一台普通的计算机,而不是为数据库设计的。我在我的低端数据库服务器上运行表扫描大约一分钟内超过 6.5 亿行,但这意味着每秒从磁盘读取大约 1 GB,这是 10k RM 磁盘的 RAID - RAID 10。基本上就是这么说。 .. 数据库喜欢 IO,而且在某种程度上你从未见过。基本上较大的数据库服务器有许多磁盘来满足 IOPS(每秒 IO)要求。我见过一个有 190 个磁盘的服务器。

        因此,您有两个选择:提高您的 IOPS 能力(意味着花钱),或设置因“合适”而被使用的索引。

        正确的意思是:索引只有在它包含的字段从左到右使用时才有用。不一定按照相同的顺序...但是如果缺少某个字段,则 SQL 系统可能会认为不值得追求索引,而是进行表扫描(如您的情况)。

        【讨论】:

        • 这里有很多很棒的回复,谢谢大家。所以我觉得我的眼睛真的睁得大大的——那么说有时表有多个索引是否公平?似乎单个索引代表了一种常见的搜索方法。如果我有时按 sid 搜索,有时按 uid+area+type 搜索,那么将它们作为 2 个单独的索引对我来说是个好主意吗?谢谢
        • 是的,有时一个表会有很多索引——完全正常。最后的后备方案是拥有单个字段索引并拥有足够智能的服务器来有效地处理它(shiqh SqlLite 可能没有)。
        【解决方案4】:

        当您在 uid、area 和 type 上创建新索引时,您还应该在每个索引上执行 select distinct 以确定哪个具有最少的不同条目,然后创建索引,使差异越少越早显示在索引定义中。

        【讨论】:

        • 这是为什么呢?如果 uid、area 和 type 始终一起包含在 where 或 join 子句中,则没有区别。如果某个子集是常用的,那么最重要的是把那个子集的列放在第一位,而不是根据选择性来排列东西。最后,如果其他情况相同(假设所有三列都被用作单一标准),您是否不想将最具选择性的列放在第一位,而不是按照您的建议将选择性最差的列放在第一位?
        猜你喜欢
        • 2021-05-26
        • 1970-01-01
        • 2018-11-27
        • 2023-03-15
        • 2016-01-25
        • 2014-08-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多