質問
タイトルの通りなのですが、
MariaDBにおいてORDER BY RAND()
が非常に遅いため改善を試みましたが、
上手くいかなかった為、
「どうすれば結果取得までの時間を短縮し、CPU使用率を低くできるか」を質問させてください。
やりたいこと
accounts
テーブルからランダムに10個name
を取得したい。
条件
del_flg
が0のみ取得。creation_time
が毎日AM 00:00より前のものを取得
※クエリは多くて1秒間に10回程度実行される場合があります。
試したこと
その1
処理時間 11.469秒
非常に重い。
DBサーバのCPU使用率が跳ね上がるため利用できない。
SQL
1SELECT 2 name 3FROM 4 accounts 5WHERE 6 del_flg = 0 AND 7 creation_time < '2016/10/29 00:00' 8ORDER BY 9 RAND() 10LIMIT 10;
その2
こちらを参考にしてSQLを変更しました。
処理時間 28.422秒
悪化しました。
SQL
1SELECT 2 name 3FROM 4 accounts AS tbl, 5 ( 6 SELECT 7 id 8 FROM 9 accounts 10 WHERE 11 del_flg = 0 AND 12 creation_time < '2016/10/29 00:00' 13 ORDER BY 14 RAND() 15 LIMIT 10 16 ) AS tmp_tbl 17WHERE 18 tbl.id = tmp_tbl.id;
その3
こちらを参考にしました。
処理時間 0.015秒
INNER JOIN ON id >= id
と書かれている項目のものを利用してSQLを作りました。
しかし、user_id
がほぼ乱数に近い飛び飛びな値(700,000,000~999,999,999でランダム)の為、
SELECT CEIL(RAND() * (SELECT MAX(`user_id`) FROM `accounts`))
で得られる結果は700,000,000
以上になることはほぼ無く、
ランダム性能が悪く使い物になりませんでした。
何十回実行しても連続で結果が同じ場合がありました。
SQLは省略します。
その4
WHEREの条件がマズいので、場合によっては10件以下が取得される場合があります。
10件以下を取得してしまう可能性があるので使えませんが、自分が色々試した中で最速でした。
処理時間 1.328秒
SQL
1SELECT 2 name 3FROM 4 accounts, 5 (SELECT id FROM accounts ORDER BY RAND() LIMIT 10) AS tmp_tbl 6WHERE 7 accounts.creation_time < '2016/10/29 00:00' AND 8 accounts.del_flg = 0 AND 9 accounts.id = tmp_tbl.id;
対象テーブル
- データ数300万件(随時増えています)
user_id
は値が飛び飛び(700,000,000~999,999,999でランダム)name
はユーザー名del_flg
は0が削除されていない。1が削除済み。creation_time
はaccounts
テーブルにデータが挿入された日時
SQL
1CREATE TABLE `accounts` ( 2 `user_id` INT(11) NOT NULL, 3 `name` VARCHAR(36) NOT NULL, 4 `del_flg` TINYINT(1) NOT NULL DEFAULT '0', 5 `creation_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, 6 PRIMARY KEY (`user_id`), 7 INDEX `name` (`name`), 8 INDEX `del_flg` (`del_flg`), 9 INDEX `creation_time` (`creation_time`) 10) 11COLLATE='utf8_general_ci' 12ENGINE=InnoDB;
DBサーバ
- CentOS 7.2.1511
- MariaDB Version 15.1

バッドをするには、ログインかつ
こちらの条件を満たす必要があります。