尽可能快地获取我的 MySQL 的第一行(也在大表上)
Posted
技术标签:
【中文标题】尽可能快地获取我的 MySQL 的第一行(也在大表上)【英文标题】:Get top row my MySQL as fast as possible (also on large tables) 【发布时间】:2020-05-03 22:04:06 【问题描述】:我有一个用于工业机器的简单软件,它使用 mysql 作为数据存储。任何时候数据库上只有一个连接和一个用户。
我的表很简单:
id data fetched
int varchar boolean
1 KDINNALSKDGJ 0
2 F34LNALNLIJA 0
等等。 id 总是连续的,并且有一个索引。
我需要的是始终获取第一个“数据”(具有最低 id),其中 fetched 为 0。然后将“fetched”更新为“1”,因为我收到了数据。
我使用类似的东西
SELECT id, data FROM mytable WHERE fetched=0 LIMIT 0,1
这可行,但每次调用都会按顺序变慢。这是我真正的问题。我可以忍受前 100 秒左右的估计 0,005 秒,但在 50.000+ 时,我会遇到 0.3 秒。
我认为这是因为数据库每次查找第一个匹配项时都会从顶部搜索。
按数字约束索引要快得多:
SELECT id, data FROM mytable WHERE id> :myLastID and fetched=0 LIMIT 0,1
.. 但这在 40.000 左右之后也会变慢,在 80.000 处我大约是 20 毫秒(第一次大约是 6 毫秒)
我的最终数据库可能在数百万范围内,但通常可能在 2-500.000 左右
有什么方法可以让 MySQL 更快地返回“下一条记录”?使用 MySQL 中的 CURSOR 吗?
我将使用 Delphi 来连接 MySQL。我尝试了存储过程并使用 2 个查询来选择 /update。结果几乎一样。
【问题讨论】:
你试过'TOP'吗?我不知道它是否适用于 MySQL,但它应该。 w3schools.com/sql/sql_top.asp 请注意,没有 ORDER BY 的 LIMIT 是毫无意义的 看来您需要的是 UPDATE,而不是 SELECT @P2000 我必须保留数据,因为我稍后会做报告。数据的处理不仅仅是“获取” - 也可以是“处理”和“验证”。我想我最终会得到“查询大块”,然后用事务批量更新。在断电时当然会冒着不一致的风险,我试图用单一查询的想法来防止这种情况。 是的,如果您缓存在易失性内存上,电源中断是一个问题,当然,这就是 S/W 结构/变量所在的位置。您可以在数据库表中镜像加载的缓存/块“数组”。如果镜像在并发线程中,则可以最大限度地减少延迟问题。我猜你知道这一点:如果操作系统仍在内存中缓冲 R/W,即使 DB 也可能不安全。电源中断最好在 H/W 级别使用 UPS 和断电中断来处理,以退出已知和安全的状态(服务器、医疗设备、机械、军事设备......) 【参考方案1】:首先:您的查询需要一个order by
子句,否则实际上未定义首先返回哪一行(不能保证它将是具有最小id
的行)。
所以你应该这样表述:
select id, data
from mytable
where fetched = 0
order by id
limit 1
然后为了性能,我建议添加以下索引:
create index myindex on mytable(fetched, id, data)
逻辑是:
索引的第一列fetched
匹配where
子句中的谓词
第二列是排序列(id
)
第三列是select
子句中的剩余列(data
)
这为您提供了一个覆盖索引:MySQL 应该能够通过仅查看索引来执行整个查询(即不查看数据本身)。
【讨论】:
感谢您的建议。我确实实现了它们,但奇怪的是性能(每次查询的时间)大致相同。我的猜测是,如果不查看不同的策略,我无法获得所需的“selectUpdate”性能。我的 SQL 服务器上的时间增加到大约 50 毫秒。不过欢迎您的建议。 我会避免将data
添加到索引中。首先,它会使索引变得更大,其次,由于您需要更新记录,因此无论如何您都需要访问数据。
@MyICQ 您应该使用EXPLAIN
来检查 MySQL 如何执行查询。如果使用正确的索引应该很快。
我的印象是“data”是一个唯一的token,所以“id”和“data”是独立唯一的。如果是这样,这意味着索引只需要跨越“id”和“fetched”。包含“数据”会抵消索引的好处,这可能是 OP 正在观察的。
@P2000: 不 - 这是索引中的最后一列,所以即使它是唯一的也不是问题(如果它在索引中是第一列)。这仍然为您提供了一个可用且覆盖的索引 - 这是我们在这里要加快查询速度。【参考方案2】:
如果只有一个连接,我们是否也可以假设也只有一个“用户”?
如果是这样,为什么不简单地将 id 值保存在程序的内存中,并通过简单地询问“下一个”记录来询问第一个下一个数字。例如。 SELECT data FROM table WHERE id = @x AND fetched = 0
?如果没有返回任何内容,那么您就知道之前已经获取了该值并且某些内容不同步(或者表中的记录已用完)。这应该是异常的,我猜你必须恢复到旧查询才能回到正轨,但同样,它应该是异常的。
不需要任何其他更改(当您说“索引”时,我假设 id 是 PK)
PS:既然你没有提到,那么 InnoDB 和 MyISAM 有区别吗? (从我所读到的内容中,我更喜欢前者,但TBH我几乎没有任何实践经验)
哦,是的,正如其他人已经提到的,没有ORDR BY
的LIMIT 1
几乎会给你“随机结果”。这可能是故意的,但这种情况很少见,大多数情况下更喜欢在重新运行时获得相同的结果。
【讨论】:
以上是关于尽可能快地获取我的 MySQL 的第一行(也在大表上)的主要内容,如果未能解决你的问题,请参考以下文章
蟒蛇:MYSQLdb。如何在不执行 select * 在大表中获取列名?
在大表执行DDL的过程中,若临时中断,会发生什么状况,需要特别处理吗?