在我们进入过早优化模式之前,查看以下查询模板可能会很有用。如果不出意外,这可以用作衡量可能优化的有效性的基准。
SELECT T.Tagid, TagInfo.TagName, COUNT(*)
FROM Items I
JOIN Tags TagInfo ON TagInfo.TagId = T.TagId
JOIN ItemTagMap T ON I.ItemId = T.ItemId
--JOIN ItemTagMap T1 ON I.ItemId = T1.ItemId
WHERE I.ItemId IN
(
SELECT ItemId
FROM Items
WHERE -- Some typical initial search criteria
Title LIKE 'Bug Report%' -- Or some fulltext filter instead...
AND ItemDate > '02/22/2008'
AND Status = 'C'
)
--AND T1.TagId = 'MySql'
GROUP BY T.TagId, TagInfo.TagName
ORDER BY COUNT(*) DESC
子查询是“驱动查询”,即对应于最终用户的初始标准的查询。(see below for details on how this query, required multiple times may fit in an overall optimized flow) Commented is the JOIN on T1 (and possibly T2, T3, when several tags are selected), and, with the WHERE clause, the associated标准。当用户选择特定标签时需要这些,无论是作为初始搜索的一部分还是通过细化。(将这些连接和 where 子句放在子查询中可能更有效;下面将详细介绍)
讨论......
“驱动查询”或其变体需要用于两个不同的目的:
1 提供枚举所有关联标签所需的 ItemId的完整列表。
2提供前N个ItemId值(N为显示页面大小),用于在Item表中查找Item详细信息。
请注意,不需要对完整列表进行排序(或者它可能会受益于以不同的顺序排序),因此第二个列表需要根据用户的选择进行排序(例如按日期,降序或按标题,按字母顺序升序)。另请注意,如果需要任何排序顺序,查询的成本将意味着处理完整列表(SQL 本身的奇怪优化和/或一些非规范化,SQL 需要“查看”该列表中的最后一条记录,以防它们属于顶部,排序方式)。
后一个事实有利于为这两个目的使用相同的查询,相应的列表可以存储在临时表中。一般流程是快速查找前 N 项记录及其详细信息,并立即将其返回给应用程序。然后应用程序可以获取 ajax-fashion 的标签列表以进行细化。该列表将使用与上述类似的查询生成,其中子查询被“select * from temporaryTable”替换。SQL 优化器决定对这个列表进行排序(在某些情况下)的可能性很大,让我们让它这样做,而不是再次猜测它并显式排序。
要考虑的另一点是可能将 ItemTagMap 表上的连接带入“驱动查询”中,而不是如上所示。这样做可能是最好的,这既是为了性能,也是因为它会为#2 目的(显示一页项目)生成正确的列表。
上述查询/流程可能会很好地扩展,即使在相对适中的硬件上也是如此;暂时进入 1/2 百万+ 项,持续的用户搜索可能高达每秒 10 次。关键因素之一是初始搜索标准的选择性。
优化思路
- [取决于典型的搜索案例和数据统计] 通过将某些项目的字段(实际上是复制)引入 ItemTagMap 表来进行非规范化可能是有意义的。特别是短字段可能在那里“受欢迎”。
- 随着数据以百万以上的方式增长,我们可以利用一些标签通常具有很强的相关性(例如:在 SO 中,PHP 通常带有 MySql,顺便说一句,通常没有充分的理由......),并使用各种技巧。例如,“多标签”TagIds 的引入可能会使输入逻辑更加复杂,但也可以显着减小 Map 的大小。
——'不多说了!--
应根据实际需求和有效的数据统计概况选择合适的架构和优化...