我使用innodb表来存储联系信息。 我显示分页数据。第一页的查询如下所示:
SELECT name, email FROM contacts WHERE userid = 1 LIMIT 0,50
带有userid的复合列电子邮件是主键,因此默认情况下结果按其排序。 第二页的查询如下所示;
SELECT name, email FROM contacts WHERE userid = 1 LIMIT 50,50
现在我的问题:依靠innodb的默认排序顺序是否安全(我知道我可以简单地添加ORDER BY email
以确保)?
换句话说,是否可能在第一页和第二页上显示相同的联系人?这将取决于什么?
答案 0 :(得分:2)
不,依靠InnoDB的任何“默认排序”并不“安全”。
但这实际上取决于你如何定义“安全”。如果没有ORDER BY
子句,MySQL会不保证以任何特定顺序返回行。 (我们可能会观察到可重复的行为似乎是可靠的,但这不是的保证。所以,我不认为它是安全的。
换句话说,第二个查询(使用LIMIT 50,50)可以返回与第一个查询(LIMIT 0,50)完全相同的行集并且在规范内。 (显然,要实现这一点,需要至少有100行满足查询谓词。)
考虑当DBA升级MySQL或InnoDB插件时会发生什么,并且行为不一样。考虑一下,如果DBA将存储引擎从InnoDB更改为其他更新,更快的存储引擎,会发生什么。考虑如果DBA将主键从一个候选键更改为另一个候选键,会发生什么。
如果您依赖于按特定顺序返回的行,那么在ORDER BY
子句中明确指定它是“安全的”。当明确指定时,解密代码的穷人更可能会认识到您的代码期望以特定顺序返回行。查看代码,他是否知道引用的表正在使用InnoDB存储引擎,并且您依赖于某些隐式假设的行为?
-
问:第一页和第二页可能会显示相同的联系人吗?这将取决于什么?
答:是的,这是可能的。如果不做一些改变,你不可能看到它发生。考虑在第一次查询之后以及第二次查询之前执行DELETE操作(例如通过某个其他会话)会发生什么。您的查询模式可能会返回相同的行。如果插入行,也有可能跳过某些行。有改进的“下一页”查询模式。我使用的是使用ORDER BY
唯一键(或一组非唯一列,包括或包含唯一键。保存“ORDER BY”表达式中的所有值上一个查询的最后一行,“下一页”查询包括谓词(WHERE子句),它只返回“跟随”最后检索到的行的行。
<强>更新强>
假设id
是主键或唯一键,对于第一页:
SELECT c.name
, c.email
, c.id
FROM contacts c
WHERE c.userid = 1
ORDER BY c.id
LIMIT 50
保存上次检索到的行中id
列的值,并将其传回“下一页”请求。对于下一页查询,
SELECT c.name
, c.email
, c.id
FROM contacts c
WHERE c.userid = 1
AND c.id > :last_retrieved_value
ORDER BY c.id
LIMIT 50
如果您按非独特的方式进行排序,则查询谓词只会稍微复杂一些。例如,如果您按name
订购,则按id
首页查询大致相同:
SELECT c.name
, c.email
, c.id
FROM contacts c
WHERE c.userid = 1
ORDER
BY c.name
, c.id
LIMIT 50
保存上次提取的行的name
和id
中的值。下一页查询使用这些值:
SELECT c.name
, c.email
, c.id
FROM contacts c
WHERE c.userid = 1
AND c.name >= :last_fetched_name
AND NOT ( c.name = :last_fetched_name AND c.id <= :last_fetched_id )
ORDER
BY c.name
, c.id
LIMIT 50
如果你想变得更加漂亮,你可以增强它来检查你是否获取了最后一行。将查询更改为LIMIT 51
,仅使用前50行(保存第50行的值),并检查是否有第51行。如果你没有得到一行,那么你就在行的末尾,而且不需要启用“下一页”按钮。
答案 1 :(得分:0)
(我假设用户ID不是PK,否则查询没有意义)。
不,这不安全。 InnoDB按照它读取的索引顺序返回行。
比如说,您有索引idx_a (userid, field_a)
和idx_b (userid, field_b)
。出于某种原因,优化器决定对第一个查询使用idx_a
,对第二个查询使用idx_b
。显然你会得到不同的记录集。
即使有索引idx (userid)
MySQL也可能决定从PRIMARY读取(当userid = 1是表记录的大部分时)