关于全面禁止直接在数据库内使用rand()进行随机读取的公告

管理员 zsx 2015-10-6 9354

很多开发者喜欢用类似以下的代码来取得随机文章:
$array = $zbp->GetArticleList(array('*'),$where,array('rand()'=>' '),array($numm),'');
这样做有以下问题:
1. 不支持MySQL之外的数据库;
2. 数据量上万即奇慢无比。

所以,除非特殊情况,我们今后不再允许代码含直接在数据库内rand()的应用通过审核。当然,不包含在PHP里用rand()或mt_rand()函数,也不包含Math.random()。

为什么会慢?
我这里尝试用非计算机专业术语来解释。
我们把一本书比作数据库。一般的数据库查询,比如查一条ID为1的记录,一般会直接根据索引来得到结果。索引的用途,相当于一本书的目录。直接查目录得到结果,自然会比一页一页翻来得快。
然而,ORDER BY RAND()呢?它会先给你这本书的每一页都随机写一个数字,这就要翻过每一页了。接着,再排序比大小,把每一页的数字都排序排一遍,这就又要把书翻一遍。再取出数字最小的那几条记录。所以,这就是慢的原因。(当然,MySQL的内部实现我没看过,这两个任务不一定是互相独立的。)
如果要比较专业解释的话,请参看MySQL的官方文档:

https://dev.mysql.com/doc/refman/5.0/en/mathematical-functions.html#function_rand
You cannot use a column with RAND() values in an ORDER BY clause, because ORDER BY would evaluate the column multiple times. However, you can retrieve rows in random order like this:

mysql> SELECT * FROM tbl_name ORDER BY RAND();
ORDER BY RAND() combined with LIMIT is useful for selecting a random sample from a set of rows:

mysql> SELECT * FROM table1, table2 WHERE a=b AND c<d -> ORDER BY RAND() LIMIT 1000;
RAND() is not meant to be a perfect random generator. It is a fast way to generate random numbers on demand that is portable between platforms for the same MySQL version.

如何解决?
非对数据库有一定研究的开发者,请直接去除随机文章功能。
否则,请参考以下链接对你的程序进行优化。另外,请务必确保你的代码对SQLite等数据库有效,或干脆对这些数据库不提供相关功能。
在 MySQL 中,从 10 万条主键不连续的数据里随机取 3000 条,如何做到高效?

MySQL select 10 random rows from 600K rows fast


其它:我的测试数据库是六十万,大小3.1GB。



不理世事

返回