当前位置: 首页 > 科技观察

【免精】面试官:如何高效地从MySQL中随机查询一条记录?

时间:2023-03-16 01:00:58 科技观察

作者个人研发在高并发场景下提供了一个简单、稳定、可扩展的延迟消息队列框架,具有精准的定时任务和延迟队列处理功能。开源半年多以来,已成功为十几家中小企业提供精准定时调度解决方案,经受住了生产环境的考验。为了造福更多童鞋,这里提供一个开源框架地址:https://github.com/sunshinelyz/mykit-delay前面写到MySQL数据库在互联网行业应用广泛,一些小伙伴可能会觉得MySQL数据库比较小,不能存储很多数据。其实这些朋友是真的不懂MySQL。MySQL的小并不是说MySQL存储的数据小,而是体积小,比较轻量级。使用MySQL可以存储上千亿的数据。后面的文章会和小伙伴们分享如何使用MySQL存储千亿以上的数据。或者你可以提前预订我的新书《MySQL技术大全:开发、优化与运维实战》。好了,说了这么多,今天给大家分享一道关于MySQL的经典面试题:如何从MySQL中查询一条随机记录效率最高?面试题如何从MySQL数据表中随机查询一条记录,同时保证效率最大化。从这个题目来看,其实包含了两个需求。第一个需求是:从MySQL数据表中随机查询一条记录。第二个要求是确保最高效率。接下来,我们尝试使用各种方法从MySQL数据表中查询数据。方法一这是最原始、最直观的语法,如下:SELECT*FROMfooORDERBYRAND()LIMIT1这种方法在数据表的数据量较小时可行。但是当数据量达到一定程度时,比如100万条数据或者更多,就会出现很大的性能问题。如果通过EXPLAIN来分析这条语句,你会发现,虽然MySQL是通过创建临时表来进行排序的,但是由于ORDERBY和LIMIT本身的特点,在排序完成之前,我们仍然无法通过LIMIT获取到需要的记录。也就是你有多少条记录,首先要对数据进行排序。方法2对于数据量大的随机抽取,性能的症结似乎在于ORDERBY,那么如何避免呢?方法2提供了一种解决方案。首先获取数据表中所有记录的个数:SELECTcount(*)ASnum_rowsFROMfoo然后通过相应的后台程序记录总的记录数(假设为num_rows)。然后执行:SELECT*FROMfooLIMIT[一个0到num_rows之间的随机数],1以上的随机数可以通过后台程序获取。这种方法的前提是表的ID是连续的或者自增的。该方法成功避免了ORDERBY的产生。方法三,不使用ORDERBY,是否可以用SQL语句实现方法二?对,就是用JOIN。SELECT*FROMBarBJOIN(SELECTCEIL(MAX(ID)*RAND())ASIDFROMBar)ASmONB.ID>=m.IDLIMIT1;这种方法达到了我们的目的,同时,在数据量很大的情况下,也避免了ORDERBY导致的所有记录的排序过程,因为JOIN中的SELECT语句实际上只执行了一次,不是N次(N等于方法2中的num_rows)。而且,我们可以在过滤语句中加入一个“大于”符号,也可以避免因为ID不连续而导致记录为空的现象。MySQL查询5条不重复数据,使用:SELECT*FROM`table`ORDERBYRAND()LIMIT5。但是经过实测,发现效率很低。一个15万多条的库,查询5条数据,谷歌搜索居然要8秒多,基本是网上查询max(id)*rand()随机获取数据。SELECT*FROM`table`AST1JOIN(SELECTROUND(RAND()*(SELECTMAX(id)FROM`table`))ASid)ASt2WHEREt1.id>=t2.idORDERBYt1.idASCLIMIT5;但这会产生5个连续的记录。解决办法只能是一次查询一个,查询5次。即便如此,也是值得的,因为对于一个有150,000个条目的表,查询只需要不到0.01秒。上面的语句使用了JOIN。mysql论坛上有人用SELECT*FROM`table`WHEREid>=(SELECTFLOOR(MAX(id)*RAND())FROM`table`)ORDERBYidLIMIT1;我测试了一下,耗时0.5秒,速度也不错,但是和上面的说法还是有很大的差距。总觉得有些不正常。所以我改写了这句话。SELECT*FROM`table`WHEREid>=(SELECTfloor(RAND()*(SELECTMAX(id)FROM`table`)))ORDERBYidLIMIT1;现在,效率又提升了,查询时间只有0.01秒最后改进语句,加上MIN(id)的判断。刚开始测试的时候是因为没有加上MIN(id)的判断,结果有一半时间总是查询表的前几行。完整的查询语句为:SELECT*FROM`table`WHEREid>=(SELECTfloor(RAND()*((SELECTMAX(id)FROM`table`)-(SELECTMIN(id)FROM`table`))+(SELECTMIN(id)FROM`table`)))ORDERBYidLIMIT1;SELECT*FROM`table`ASt1JOIN(SELECTROUND(RAND()*((SELECTMAX(id)FROM`table`)-(SELECTMIN(id)FROM`table`))+(SELECTMIN(id)FROM`table`))ASid)ASt2WHEREt1.id>=t2.idORDERBYt1.idLIMIT1;最后将两条语句分别查询10次,前者耗时0.147433秒,后者耗时0.015130秒。看起来使用JOIN语法比直接在WHERE中使用函数要高效得多。本文转载自微信公众号“银禾科技”,可通过以下二维码关注。转载本文请联系冰川科技公众号。