TL;DR クイックフィックス
MySQLがERROR 1038 (HY001): Out of sort memory, consider increasing server sort buffer sizeをスローしたのは、クエリごとに割り当てられるソートバッファが小さすぎるためです。MySQLセッションで以下を実行してください — 再起動は不要です:
SET SESSION sort_buffer_size = 8 * 1024 * 1024; -- 8 MB
クイックフィックスはこれだけです。再起動後も設定を維持するには、/etc/mysql/mysql.conf.d/mysqld.cnf(またはディストリビューションによっては/etc/my.cnf)に設定を追加し、MySQLを再起動してください。
このエラーの原因
MySQLのすべてのORDER BYクエリは、行をソートするための一時メモリを必要とします。そのメモリはsort_buffer_sizeから供給されますが、デフォルト値は256 KBです — これは2000年代中頃に設定された値で、当時は結果セットがはるかに小さく、誰も気にしませんでした。
よくある3つのトリガー:
- カバリングインデックスなしで数十万行をソートする場合
- ソート負荷の高いクエリを同時実行する接続が多い場合 — 各接続が専用のソートバッファを持つため、メモリ使用量が急速に増加します
GROUP BY、DISTINCT、UNIONを使用する場合 — これらはすべて内部的にソートバッファを使用します
現在の設定を確認する:
SHOW VARIABLES LIKE 'sort_buffer_size';
+------------------+--------+
| Variable_name | Value |
+------------------+--------+
| sort_buffer_size | 262144 |
+------------------+--------+
262144バイト = 256 KB。このデフォルト値は現代のワークロードには適していません。
修正1:セッション単位でsort_buffer_sizeを増やす
サーバー設定を変更せずに素早く修正したい場合は、セッションレベルで設定します:
-- この接続にのみ適用される
SET SESSION sort_buffer_size = 8 * 1024 * 1024;
-- 元のクエリを実行する
SELECT * FROM orders ORDER BY created_at DESC LIMIT 1000;
MySQLの再起動が選択肢にないマイグレーションスクリプトや単発ジョブで効果的です。
修正2:my.cnfで恒久的に設定する
MySQLの設定ファイルを見つけます。パスはOSによって異なります:
- Ubuntu/Debian:
/etc/mysql/mysql.conf.d/mysqld.cnf - CentOS/RHEL:
/etc/my.cnfまたは/etc/mysql/my.cnf - Windows:
C:\ProgramData\MySQL\MySQL Server 8.0\my.ini
[mysqld]セクションの下に値を追加または更新します:
[mysqld]
sort_buffer_size = 4M
その後、再起動します:
# Ubuntu/Debian
sudo systemctl restart mysql
# CentOS/RHEL
sudo systemctl restart mysqld
どのくらいに設定すべきか?
ワークロードによって異なりますが、以下が合理的な出発点です:
- 4 MB:ほとんどのアプリに適した安全なデフォルト値
- 8〜16 MB:大規模なソート操作を伴うレポート中心のデータベース
- グローバルは64 MBを上限に — この設定は接続ごとの値で、共有メモリではありません。100接続 × 64 MB = ソートバッファだけで6.4 GB のRAMを消費します。すぐに積み上がります。
たまに発生する重いクエリには、グローバルではなくセッション単位で増やしましょう:
SET SESSION sort_buffer_size = 32 * 1024 * 1024; -- このクエリのために増加
SELECT ... ORDER BY ... ;
SET SESSION sort_buffer_size = 4 * 1024 * 1024; -- その後リセット
修正3:インデックスを追加する(正しい長期的解決策)
ソートバッファを増やすのは一時しのぎです。可能であれば、ソートに使用するカラムにインデックスを付けるのが本来の解決策です。適切なインデックスがあれば、MySQLはソートバッファを一切使わずにソート済みの順序で行を読み取ります。
遅いクエリが次のような場合:
SELECT id, name, created_at FROM users ORDER BY created_at DESC LIMIT 50;
インデックスを追加します:
ALTER TABLE users ADD INDEX idx_created_at (created_at);
MySQLが実際にそれを使用していることを確認します:
EXPLAIN SELECT id, name, created_at FROM users ORDER BY created_at DESC LIMIT 50;
Extra列を確認してください。空白かUsing indexが表示されているのが理想です。それでもUsing filesortと表示されている場合、MySQLはインメモリソートを行っています — 通常、クエリが多すぎるカラムを取得しているか、インデックスがカバーできない複雑な条件があるためです。
修正4:ソートする行数を減らす
クエリ自体が問題のこともあります。200万行のフルテーブルスキャンをソートすれば、バッファをどれだけ大きくしても使い果たしてしまいます。
以下の代わりに:
SELECT * FROM events ORDER BY event_time DESC;
先にフィルタリングしてからソートします:
SELECT * FROM events
WHERE event_time >= NOW() - INTERVAL 7 DAY
ORDER BY event_time DESC
LIMIT 100;
event_timeのインデックスと組み合わせると、ソートバッファの問題が完全に解消されることがよくあります。
修正が機能したことを確認する
sort_buffer_sizeが更新されていることを確認します:
SHOW VARIABLES LIKE 'sort_buffer_size';
元のクエリを再実行してください — ERROR 1038 は解消されているはずです。サーバー全体のソートアクティビティを監視するには:
SHOW STATUS LIKE 'Sort%';
+-------------------+-------+
| Variable_name | Value |
+-------------------+-------+
| Sort_merge_passes | 0 |
| Sort_range | 5 |
| Sort_rows | 12430 |
| Sort_scan | 3 |
+-------------------+-------+
Sort_merge_passesを監視してください。ゼロが理想です。この数値が増え続けている場合、MySQLはソートデータを繰り返しディスクに書き出しています — バッファがまだ小さすぎるか、クエリの見直しが必要です。
まとめ
- セッションのクイックフィックス:
SET SESSION sort_buffer_size = 8 * 1024 * 1024; - 恒久的な修正:
my.cnfの[mysqld]セクションにsort_buffer_size = 4Mを設定する - グローバル値を高く設定しすぎない — 接続ごとの値であり、共有メモリではありません
ORDER BYカラムにインデックスを付けて、ソートバッファを完全に回避するSort_merge_passesを監視する — ゼロでなく増加している場合、バッファはまだ不足しています

