Maison base de données tutoriel mysql [MySQL优化案例]系列 — RAND()优化_MySQL

[MySQL优化案例]系列 — RAND()优化_MySQL

May 31, 2016 am 08:48 AM

众所周知,在MySQL中,如果直接 ORDER BY RAND() 的话,效率非常差,因为会多次执行。事实上,如果等值查询也是用 RAND() 的话也如此,我们先来看看下面这几个SQL的不同执行计划和执行耗时。首先,看下建表DDL,这是一个没有显式自增主键的InnoDB表:

[yejr@imysql]> show create table t_innodb_random/G*************************** 1. row ***************************Table: t_innodb_randomCreate Table: CREATE TABLE `t_innodb_random` (`id` int(10) unsigned NOT NULL,`user` varchar(64) NOT NULL DEFAULT '',KEY `idx_id` (`id`)) ENGINE=InnoDB DEFAULT CHARSET=latin1
Copier après la connexion

往这个表里灌入一些测试数据,至少10万以上, id 字段也是乱序的。

[yejr@imysql]> select count(*) from t_innodb_random/G*************************** 1. row ***************************count(*): 393216
Copier après la connexion

1、常量等值检索:

[yejr@imysql]> explain select id from t_innodb_random where id = 13412/G*************************** 1. row ***************************id: 1select_type: SIMPLEtable: t_innodb_randomtype: refpossible_keys: idx_idkey: idx_idkey_len: 4<strong>ref: constrows: 1Extra: Using index</strong>[yejr@imysql]> select id from t_innodb_random where id = 13412;1 row in set (0.00 sec)
Copier après la connexion

可以看到执行计划很不错,是常量等值查询,速度非常快。

2、使用RAND()函数乘以常量,求得随机数后检索:

[yejr@imysql]> explain select id from t_innodb_random where id = round(rand()*13241324)/G*************************** 1. row ***************************id: 1select_type: SIMPLEtable: t_innodb_randomtype: indexpossible_keys: NULLkey: idx_idkey_len: 4<strong>ref: NULLrows: 393345Extra: Using where; Using index</strong>[yejr@imysql]> select id from t_innodb_random where id = round(rand()*13241324)/GEmpty set (0.26 sec)
Copier après la connexion

可以看到执行计划很糟糕,虽然是只扫描索引,但是做了全索引扫描,效率非常差。因为WHERE条件中包含了RAND(),使得MySQL把它当做变量来处理,无法用常量等值的方式查询,效率很低。

我们把常量改成取t_innodb_random表的最大id值,再乘以RAND()求得随机数后检索看看什么情况:

[yejr@imysql]> explain select id from t_innodb_random where id = round(rand()*(select max(id) from t_innodb_random))/G*************************** 1. row ***************************id: 1select_type: PRIMARYtable: t_innodb_randomtype: indexpossible_keys: NULLkey: idx_idkey_len: 4<strong>ref: NULLrows: 393345Extra: Using where; Using index</strong>*************************** 2. row ***************************id: 2select_type: SUBQUERYtable: NULLtype: NULLpossible_keys: NULLkey: NULLkey_len: NULLref: NULLrows: NULLExtra: Select tables optimized away[yejr@imysql]> select id from t_innodb_random where id = round(rand()*(select max(id) from t_innodb_random))/GEmpty set (0.27 sec)
Copier après la connexion

可以看到,执行计划依然是全索引扫描,执行耗时也基本相当。

3、改造成普通子查询模式 ,这里有两次子查询

<strong>[yejr@imysql]> explain select id from t_innodb_random where id = (select round(rand()*(select max(id) from t_innodb_random)) as nid)/G*************************** 1. row ***************************id: 1select_type: PRIMARYtable: t_innodb_randomtype: indexpossible_keys: NULLkey: idx_idkey_len: 4ref: NULLrows: 393345Extra: Using where; Using index*************************** 2. row ***************************id: 3select_type: SUBQUERYtable: NULLtype: NULLpossible_keys: NULLkey: NULLkey_len: NULLref: NULLrows: NULLExtra: Select tables optimized away[yejr@imysql]> select id from t_innodb_random where id = (select round(rand()*(select max(id) from t_innodb_random)) as nid)/GEmpty set (0.27 sec)</strong>
Copier après la connexion

可以看到,执行计划也不好,执行耗时较慢。

4、改造成JOIN关联查询,不过最大值还是用常量表示

[yejr@imysql]> explain select id from t_innodb_random t1 join (select round(rand()*13241324) as id2) as t2 where t1.id = t2.id2/G*************************** 1. row ***************************id: 1select_type: PRIMARYtable: <derived2>type: systempossible_keys: NULLkey: NULLkey_len: NULL<strong>ref: NULLrows: 1Extra:</strong>*************************** 2. row ***************************id: 1select_type: PRIMARYtable: t1type: refpossible_keys: idx_idkey: idx_idkey_len: 4<strong>ref: constrows: 1Extra: Using where; Using index</strong>*************************** 3. row ***************************id: 2select_type: DERIVEDtable: NULLtype: NULLpossible_keys: NULLkey: NULLkey_len: NULLref: NULLrows: NULLExtra: No tables used[yejr@imysql]> select id from t_innodb_random t1 join (select round(rand()*13241324) as id2) as t2 where t1.id = t2.id2/GEmpty set (0.00 sec)</derived2>
Copier après la connexion

这时候执行计划就非常完美了,和最开始的常量等值查询是一样的了,执行耗时也非常之快。这种方法虽然很好,但是有可能查询不到记录,改造范围查找,但结果LIMIT 1就可以了:

[yejr@imysql]> explain select id from t_innodb_random where id > (select round(rand()*(select max(id) from t_innodb_random)) as nid) limit 1/G*************************** 1. row ***************************id: 1select_type: PRIMARYtable: t_innodb_randomtype: indexpossible_keys: NULLkey: idx_idkey_len: 4<strong>ref: NULLrows: 393345Extra: Using where; Using index</strong>*************************** 2. row ***************************id: 3select_type: SUBQUERYtable: NULLtype: NULLpossible_keys: NULLkey: NULLkey_len: NULLref: NULLrows: NULLExtra: Select tables optimized away[yejr@imysql]> select id from t_innodb_random where id > (select round(rand()*(select max(id) from t_innodb_random)) as nid) limit 1/G*************************** 1. row ***************************id: 13011 row in set (0.00 sec)
Copier après la connexion

可以看到,虽然执行计划也是全索引扫描,但是因为有了LIMIT 1,只需要找到一条记录,即可终止扫描,所以效率还是很快的。

小结:从数据库中随机取一条记录时,可以把RAND()生成随机数放在JOIN子查询中以提高效率。

5、再来看看用ORDRR BY RAND()方式一次取得多个随机值的方式:

[yejr@imysql]> explain select id from t_innodb_random order by rand() limit 1000/G*************************** 1. row ***************************id: 1select_type: SIMPLEtable: t_innodb_randomtype: indexpossible_keys: NULLkey: idx_idkey_len: 4<strong>ref: NULLrows: 393345Extra: Using index; Using temporary; Using filesort</strong>[yejr@imysql]> select id from t_innodb_random order by rand() limit 1000;1000 rows in set (0.41 sec)
Copier après la connexion

全索引扫描,生成排序临时表,太差太慢了。

6、把随机数放在子查询里看看:

[yejr@imysql]> explain select id from t_innodb_random where id > (select rand() * (select max(id) from t_innodb_random) as nid) limit 1000/G*************************** 1. row ***************************id: 1select_type: PRIMARYtable: t_innodb_randomtype: indexpossible_keys: NULLkey: idx_idkey_len: 4<strong>ref: NULLrows: 393345Extra: Using where; Using index</strong>*************************** 2. row ***************************id: 3select_type: SUBQUERYtable: NULLtype: NULLpossible_keys: NULLkey: NULLkey_len: NULLref: NULLrows: NULLExtra: Select tables optimized away[yejr@imysql]> select id from t_innodb_random where id > (select rand() * (select max(id) from t_innodb_random) as nid) limit 1000/G1000 rows in set (0.04 sec)
Copier après la connexion

嗯,提速了不少,这个看起来还不赖:)

7、仿照上面的方法,改成JOIN和随机数子查询关联

[yejr@imysql]> explain select id from t_innodb_random t1 join (select rand() * (select max(id) from t_innodb_random) as nid) t2 on t1.id > t2.nid limit 1000/G*************************** 1. row ***************************id: 1select_type: PRIMARYtable: <derived2>type: systempossible_keys: NULLkey: NULLkey_len: NULL<strong>ref: NULLrows: 1Extra:</strong>*************************** 2. row ***************************id: 1select_type: PRIMARYtable: t1type: rangepossible_keys: idx_idkey: idx_idkey_len: 4<strong>ref: NULLrows: 196672Extra: Using where; Using index</strong>*************************** 3. row ***************************id: 2select_type: DERIVEDtable: NULLtype: NULLpossible_keys: NULLkey: NULLkey_len: NULLref: NULLrows: NULLExtra: No tables used*************************** 4. row ***************************id: 3select_type: SUBQUERYtable: NULLtype: NULLpossible_keys: NULLkey: NULLkey_len: NULLref: NULLrows: NULLExtra: Select tables optimized away[yejr@imysql]> select id from t_innodb_random t1 join (select rand() * (select max(id) from t_innodb_random) as nid) t2 on t1.id > t2.nid limit 1000/G1000 rows in set (0.00 sec)</derived2>
Copier après la connexion

可以看到,全索引检索,发现符合记录的条件后,直接取得1000行,这个方法是最快的。

综上,想从MySQL数据库中随机取一条或者N条记录时,最好把RAND()生成随机数放在JOIN子查询中以提高效率。上面说了那么多的废话,最后简单说下,就是把下面这个SQL:

SELECT id FROM table ORDER BY RAND() LIMIT n;
Copier après la connexion

改造成下面这个:

SELECT id FROM table t1, JOIN (SELECT RAND() * (SELECT MAX(id) FROM table) AS nid) t2 ON t1.id > t2.nid LIMIT n;
Copier après la connexion

就可以享受在SQL中直接取得随机数了,不用再在程序中构造一串随机数去检索了。

Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn

Outils d'IA chauds

Undresser.AI Undress

Undresser.AI Undress

Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover

AI Clothes Remover

Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool

Undress AI Tool

Images de déshabillage gratuites

Clothoff.io

Clothoff.io

Dissolvant de vêtements AI

AI Hentai Generator

AI Hentai Generator

Générez AI Hentai gratuitement.

Article chaud

R.E.P.O. Crystals d'énergie expliqués et ce qu'ils font (cristal jaune)
2 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Meilleurs paramètres graphiques
2 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌

Outils chauds

Bloc-notes++7.3.1

Bloc-notes++7.3.1

Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise

SublimeText3 version chinoise

Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1

Envoyer Studio 13.0.1

Puissant environnement de développement intégré PHP

Dreamweaver CS6

Dreamweaver CS6

Outils de développement Web visuel

SublimeText3 version Mac

SublimeText3 version Mac

Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Comment modifier une table dans MySQL en utilisant l'instruction ALTER TABLE? Comment modifier une table dans MySQL en utilisant l'instruction ALTER TABLE? Mar 19, 2025 pm 03:51 PM

L'article discute de l'utilisation de l'instruction ALTER TABLE de MySQL pour modifier les tables, notamment en ajoutant / abandon les colonnes, en renommant des tables / colonnes et en modifiant les types de données de colonne.

Comment configurer le cryptage SSL / TLS pour les connexions MySQL? Comment configurer le cryptage SSL / TLS pour les connexions MySQL? Mar 18, 2025 pm 12:01 PM

L'article discute de la configuration du cryptage SSL / TLS pour MySQL, y compris la génération et la vérification de certificat. Le problème principal est d'utiliser les implications de sécurité des certificats auto-signés. [Compte de caractère: 159]

Comment gérez-vous les grands ensembles de données dans MySQL? Comment gérez-vous les grands ensembles de données dans MySQL? Mar 21, 2025 pm 12:15 PM

L'article traite des stratégies pour gérer de grands ensembles de données dans MySQL, y compris le partitionnement, la rupture, l'indexation et l'optimisation des requêtes.

Quels sont les outils de GUI MySQL populaires (par exemple, MySQL Workbench, PhpMyAdmin)? Quels sont les outils de GUI MySQL populaires (par exemple, MySQL Workbench, PhpMyAdmin)? Mar 21, 2025 pm 06:28 PM

L'article traite des outils de GUI MySQL populaires comme MySQL Workbench et PhpMyAdmin, en comparant leurs fonctionnalités et leur pertinence pour les débutants et les utilisateurs avancés. [159 caractères]

Comment déposez-vous une table dans MySQL à l'aide de l'instruction TABLE DROP? Comment déposez-vous une table dans MySQL à l'aide de l'instruction TABLE DROP? Mar 19, 2025 pm 03:52 PM

L'article discute de la suppression des tables dans MySQL en utilisant l'instruction TABLE DROP, mettant l'accent sur les précautions et les risques. Il souligne que l'action est irréversible sans sauvegardes, détaillant les méthodes de récupération et les risques potentiels de l'environnement de production.

Comment représentez-vous des relations en utilisant des clés étrangères? Comment représentez-vous des relations en utilisant des clés étrangères? Mar 19, 2025 pm 03:48 PM

L'article discute de l'utilisation de clés étrangères pour représenter les relations dans les bases de données, en se concentrant sur les meilleures pratiques, l'intégrité des données et les pièges communs à éviter.

Comment sécuriser MySQL contre les vulnérabilités communes (injection SQL, attaques par force brute)? Comment sécuriser MySQL contre les vulnérabilités communes (injection SQL, attaques par force brute)? Mar 18, 2025 pm 12:00 PM

L'article discute de la sécurisation MySQL contre l'injection SQL et les attaques brutales à l'aide de déclarations préparées, de validation des entrées et de politiques de mot de passe solides (159 caractères)

Comment créez-vous des index sur les colonnes JSON? Comment créez-vous des index sur les colonnes JSON? Mar 21, 2025 pm 12:13 PM

L'article discute de la création d'index sur les colonnes JSON dans diverses bases de données comme PostgreSQL, MySQL et MongoDB pour améliorer les performances de la requête. Il explique la syntaxe et les avantages de l'indexation des chemins JSON spécifiques et répertorie les systèmes de base de données pris en charge.

See all articles