mita2 database life

主にMySQLに関するメモです

MySQL アメリカのサマータイムが恒久化されたらやることになる作業

サマータイムが終わらない?

アメリカでサマータイムを恒久化する動きがあるようです。 どれぐらい現実化する可能性があるのかわかりませんが、仮に、決定された場合、どのような作業が必要になるのか調べておきます。

下院を通過してバイデン大統領が署名すると、2023年の春に夏時間になったらずっとそのまま、秋になっても標準時には戻らなくなります。

news.yahoo.co.jp

Linuxタイムゾーンの管理

日本でも(結局、実現はしませんでしたが)「東京オリンピックの期間はサマータイムを採用しよう」という話があったように、時刻の定義は変遷します。 言い換えると、あるタイムゾーンにおける、「UTCからの時差」がタイミングによって変わるということです。

タイムゾーンにおける時刻の定義の履歴は zoneinfo として定義されています。 この定義があるので、タイムゾーン間の時刻の変換が正しくできるわけですね。

https://www.iana.org/time-zones

zoneinfoCentOS であれば、tzdata パッケージで管理されています。

MySQLタイムゾーンの管理

MySQLタイムゾーンを扱う場合、あらかじめ、mysql_tzinfo_to_sql コマンドで zoneinfo をSQLに変換し、DBにロードしておく必要があります。

$  mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root mysql

タイムゾーンの定義を変更する

フィジーで 2021〜2022 年にかけての夏時間の適用が停止される事態があったようです。 これを使って タイムゾーンの更新のテストしてみます。ちなみに、南太平洋の国なので、12月が夏時間になってます。

この夏時間の変更は、tzdata-2021d取り込まれました。 tzdata-2020a には反映されていません。このデータだと、2021年の時刻も他の年と同じ時刻になってます。

$ rpm -qa | grep tzdata
tzdata-2020a-1.el7.noarch

$ date --date='TZ="Pacific/Fiji" 2020-12-25 14:00:00'
2020年 12月 25日 金曜日 10:00:00 JST
$ date --date='TZ="Pacific/Fiji" 2021-12-25 14:00:00'
2021年 12月 25日 土曜日 10:00:00 JST
$ date --date='TZ="Pacific/Fiji" 2022-12-25 14:00:00'
2022年 12月 25日 日曜日 10:00:00 JST
$ date --date='TZ="Pacific/Fiji" 2023-12-25 14:00:00'
2023年 12月 25日 月曜日 10:00:00 JST

tzdata をアップデートします。

$ sudo yum update tzdata
$ rpm -qa | grep tzdata
tzdata-2022a-1.el7.noarch

新しい tzdata-2022a の定義であれば、2021年だけ夏時間が適用されなくなっていることが確認できました。

$ date --date='TZ="Pacific/Fiji" 2020-12-25 14:00:00'
2020年 12月 25日 金曜日 10:00:00 JST
$ date --date='TZ="Pacific/Fiji" 2021-12-25 14:00:00'
2021年 12月 25日 土曜日 11:00:00 JST # 2021年だけ夏時間が停止されてる
$ date --date='TZ="Pacific/Fiji" 2022-12-25 14:00:00'
2022年 12月 25日 日曜日 10:00:00 JST
[$ date --date='TZ="Pacific/Fiji" 2023-12-25 14:00:00'
2023年 12月 25日 月曜日 10:00:00 JST

これをMySQLにも反映します。タイムゾーンの情報はキャッシュされているようで、DBにロードしたあと再起動が必要でした。

$ mysql_tzinfo_to_sql /usr/share/zoneinfo | mysql -u root mysql
$ sudo systemctl restart mysqld
--- 2021
mysql> SELECT CONVERT_TZ('2021-12-25 10:00:00', 'Asia/Tokyo', 'Pacific/Fiji');
+-----------------------------------------------------------------+
| CONVERT_TZ('2021-12-25 10:00:00', 'Asia/Tokyo', 'Pacific/Fiji') |
+-----------------------------------------------------------------+
| 2021-12-25 13:00:00                                             |
+-----------------------------------------------------------------+
1 row in set (0.00 sec)

--- 2022
mysql> SELECT CONVERT_TZ('2022-12-25 10:00:00', 'Asia/Tokyo', 'Pacific/Fiji');
+-----------------------------------------------------------------+
| CONVERT_TZ('2022-12-25 10:00:00', 'Asia/Tokyo', 'Pacific/Fiji') |
+-----------------------------------------------------------------+
| 2022-12-25 14:00:00                                             |
+-----------------------------------------------------------------+
1 row in set (0.00 sec)

まとめ

タイムゾーンの定義が変わったら、tzdata をアップデートして、mysql_tzinfo_to_sql でロードしなおす

MySQL collation_server を変えたつもりが変わってなかった話

character_set_server と collation_server

これらのパラメータは、データベースを作成する際に、何もキャラクタセットや照合順序を明示的に指定しなかった場合に、採用されるキャラクタセット・照合順序です (ちなみにテーブル作成時に何も指定しなかった場合は、テーブルはスキーマのキャラクタセットや照合順序を引き継く)。

mysql> show global variables like 'character_set_server';
+----------------------+---------+
| Variable_name        | Value   |
+----------------------+---------+
| character_set_server | utf8mb4 |
+----------------------+---------+
1 row in set (0.00 sec)

mysql> show global variables like 'collation_server';
+------------------+--------------------+
| Variable_name    | Value              |
+------------------+--------------------+
| collation_server | utf8mb4_general_ci |
+------------------+--------------------+
1 row in set (0.01 sec)

-- キャラクタセット・照合順序を指定しない
mysql> create database d1;
Query OK, 1 row affected (0.01 sec)


-- character_set_server と collation_server の値が引き継がれてる
mysql> show create database d1 \G
*************************** 1. row ***************************
       Database: d1
Create Database: CREATE DATABASE `d1` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci */ /*!80016 DEFAULT ENCRYPTION='N' */
1 row in set (0.00 sec)

collation_server を変えたつもりが変わってなかった...

character_set_servercollation_server の順番でSET GLOBAL 指定すれば問題ありませんが、

mysql> SET GLOBAL character_set_server=utf8mb4, collation_server=utf8mb4_bin;
Query OK, 0 rows affected (0.00 sec)

mysql> SHOW GLOBAL VARIABLES LIKE 'collation_server';
+------------------+-------------+
| Variable_name    | Value       |
+------------------+-------------+
| collation_server | utf8mb4_bin |
+------------------+-------------+
1 row in set (0.00 sec)

逆の順番だと、collation_server の変更がなかったことになってしまいます。

mysql> SET GLOBAL collation_server=utf8mb4_bin, character_set_server=utf8mb4;
Query OK, 0 rows affected (0.00 sec)

-- utf8mb4_bin になってない
mysql> SHOW GLOBAL VARIABLES LIKE 'collation_server';
+------------------+--------------------+
| Variable_name    | Value              |
+------------------+--------------------+
| collation_server | utf8mb4_0900_ai_ci |
+------------------+--------------------+
1 row in set (0.00 sec)

character_set_server を変えると、collation_server が連動して、各キャラクタセットのデフォルトの照合順序にリセットされるみたい。 キャラクタセットと照合順序は使える組み合わせが決まっているため、collation_servercharacter_set_server の値に連動する仕様なんでしょう。

character_set_server を 今と同じ値に SET GLOBAL しても何も起きないよね〜」と思って雑なスクリプトを書いてたりすると、意図せず character_set_server がリセットされる。

mysql> SHOW GLOBAL VARIABLES LIKE 'collation_server';
+------------------+-------------+
| Variable_name    | Value       |
+------------------+-------------+
| collation_server | utf8mb4_bin |
+------------------+-------------+
1 row in set (0.00 sec)

mysql> SET GLOBAL character_set_server=@@character_set_server;
Query OK, 0 rows affected (0.00 sec)

mysql> SHOW GLOBAL VARIABLES LIKE 'collation_server';
+------------------+--------------------+
| Variable_name    | Value              |
+------------------+--------------------+
| collation_server | utf8mb4_0900_ai_ci |
+------------------+--------------------+
1 row in set (0.00 sec)

まとめ

  • character_set_server を変えると、collation_server が連動して、各キャラクタセットのデフォルトの照合順序にリセットされるよ!

MySQL 8.0.28 で performance_schema に CPU_TIME が入った

MySQL 8.0.28 から performance_schemaCPU_TIME記録できるようになりました

これまでも実行時間は performance_schema から確認できていました。ただ、実行時間が長いだけでは、そのクエリが必ずしもCPUに負荷をかけているクエリとは言いきれませんでした。 今回の機能追加で、CPUに負荷をかけているクエリがバシっと performance_schema から確認できるようになったのは嬉しいですね(欲を言うと、CPU_TIME を スロークエリログにも出してほしいなぁ)。

デフォルトではOFFになっています。以下の設定を my.cnf に記述し、有効化します。

performance-schema-consumer-events-statements-cpu=ON

sys.statement_analysis にも cpu_latency が追加され、レスポンスタイムに占めるCPU時間が確認できます。 この例では total_time = cpu_latency になってます。テーブルが小さく、ほとんどIOの発生しないクエリだったため、このような結果になってます。

mysql> select * from sys.statement_analysis limit 1\G
*************************** 1. row ***************************
            query: SELECT SUM ( `salary` ) FROM `salaries`
               db: employees
        full_scan: *
       exec_count: 5
        err_count: 0
       warn_count: 0
    total_latency: 2.12 s
      max_latency: 523.29 ms
      avg_latency: 424.49 ms
     lock_latency: 17.00 us
      cpu_latency: 2.12 s
<snip>
1 row in set (0.00 sec)

total_letency != cpu_latency となりそうな、IOバウンドなクエリで試してみます*17.14 min のうち、cpu_latency15.80 s と IOに時間がかかったことが、しっかり読み取れました。

mysql> SELECT * FROM sbtest.sbtest1 WHERE c = 'X';
Empty set (7 min 5.33 sec)

mysql> select * from  sys.statement_analysis LIMIT 1\G
*************************** 1. row ***************************
            query: SELECT * FROM `sbtest` . `sbtest1` WHERE `c` = ?
               db: NULL
        full_scan: *
       exec_count: 2
        err_count: 1
       warn_count: 0
    total_latency: 7.14 min
      max_latency: 7.09 min
      avg_latency: 3.57 min
     lock_latency: 19.00 us
      cpu_latency: 15.80 s
<snip>

実装

CPU時間は clock_gettime() を利用して取得してる。

mysys/my_rdtsc.cc

/**
  THREAD_CPU timer.
  Expressed in nanoseconds.
*/
ulonglong my_timer_thread_cpu(void) {
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_THREAD_CPUTIME_ID)
  {
    struct timespec tp;
    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp);
    return (ulonglong)tp.tv_sec * 1000000000 + (ulonglong)tp.tv_nsec;
  }

*1:差が顕著に出るようにVMのIO帯域に制限かけました

MySQL Illegal mix of collations エラーが出る・出ないまとめ

Illegal mix of collations

Illegal mix of collations エラーは 異なる照合順序で結合や比較を行った場合に発生するエラーです。

mysql> SELECT CONCAT(_utf8mb4 'A' COLLATE utf8mb4_bin, _utf8mb4 'B' COLLATE utf8mb4_unicode_ci);
ERROR 1267 (HY000): Illegal mix of collations (utf8mb4_bin,EXPLICIT) and (utf8mb4_unicode_ci,EXPLICIT) for operation 'concat'

mysql> SELECT _utf8mb4 'A' COLLATE utf8mb4_bin = _utf8mb4 'B' COLLATE utf8mb4_unicode_ci;
ERROR 1267 (HY000): Illegal mix of collations (utf8mb4_bin,EXPLICIT) and (utf8mb4_unicode_ci,EXPLICIT) for operation '='

エラーが発生しない場合もある

照合順序が異なっていても、エラーにならないケースもあります。 latin1_swedish_ciutf8mb4_unicode_ci と異なる照合順序で比較していますが、エラーなく実行することが出来てます。

mysql> SELECT _latin1 'A' COLLATE latin1_swedish_ci = _utf8mb4 'B' COLLATE utf8mb4_unicode_ci AS r;
+------+
| r    |
+------+
|    0 |
+------+
1 row in set (0.00 sec)

Illegal mix of collations エラーが発生する・しない まとめ

雑なコードを書いて、主な照合順序を総当たりでチェックしてみました*1

name sjis_japanese_ci sjis_bin ujis_japanese_ci ujis_bin utf8_general_ci utf8_unicode_ci utf8_bin utf8mb4_general_ci utf8mb4_unicode_ci utf8mb4_bin latin1_swedish_ci latin1_bin ascii_general_ci ascii_bin
sjis_japanese_ci - ERR ERR ERR OK OK OK OK OK OK ERR ERR ERR ERR
sjis_bin ERR - ERR ERR OK OK OK OK OK OK ERR ERR ERR ERR
ujis_japanese_ci ERR ERR - ERR OK OK OK OK OK OK ERR ERR ERR ERR
ujis_bin ERR ERR ERR - OK OK OK OK OK OK ERR ERR ERR ERR
utf8_general_ci OK OK OK OK - ERR ERR OK OK OK OK OK OK OK
utf8_unicode_ci OK OK OK OK ERR - ERR OK OK OK OK OK OK OK
utf8_bin OK OK OK OK ERR ERR - OK OK OK OK OK OK OK
utf8mb4_general_ci OK OK OK OK OK OK OK - ERR ERR OK OK OK OK
utf8mb4_unicode_ci OK OK OK OK OK OK OK ERR - ERR OK OK OK OK
utf8mb4_bin OK OK OK OK OK OK OK ERR ERR - OK OK OK OK
latin1_swedish_ci ERR ERR ERR ERR OK OK OK OK OK OK - ERR ERR ERR
latin1_bin ERR ERR ERR ERR OK OK OK OK OK OK ERR - ERR ERR
ascii_general_ci ERR ERR ERR ERR OK OK OK OK OK OK ERR ERR - ERR
ascii_bin ERR ERR ERR ERR OK OK OK OK OK OK ERR ERR ERR -

utf8, utf8mb4 は他のキャラクタセットとの比較が可能

mysql> SELECT _utf8mb4"A" COLLATE utf8mb4_general_ci = _sjis"A" COLLATE sjis_japanese_ci;
+----------------------------------------------------------------------------+
| _utf8mb4"A" COLLATE utf8mb4_general_ci = _sjis"A" COLLATE sjis_japanese_ci |
+----------------------------------------------------------------------------+
|                                                                          1 |
+----------------------------------------------------------------------------+
1 row in set (0.00 sec)

ただし、同じ UTF8 の他の照合順序 (例:utf8mb4_general_ci vs utf8mb4_unicode_ci) とは比較できない

mysql> SELECT _utf8mb4"A" COLLATE utf8mb4_general_ci = _utf8mb4"A" COLLATE utf8mb4_unicode_ci;
ERROR 1267 (HY000): Illegal mix of collations (utf8mb4_general_ci,EXPLICIT) and (utf8mb4_unicode_ci,EXPLICIT) for operation '='

一方、uft8, utf8mb4 以外はキャラクタセット・照合順序が一致していないと比較できない。

mysql> SELECT _sjis"A" COLLATE sjis_bin = _sjis"A" COLLATE sjis_japanese_ci;
ERROR 1267 (HY000): Illegal mix of collations (sjis_bin,EXPLICIT) and (sjis_japanese_ci,EXPLICIT) for operation '='

という、法則のようです。

*1:version 5.7.37 で試しました

MySQL Shell のパラレルテーブルインポートの実装がスマートだった件

MySQL Shell のパラレルテーブルインポートの実装が興味深かった

MySQL Shell のパラレルテーブルインポート

MySQL Shell 8.0.17 で導入された MySQL Shell パラレルテーブルインポートユーティリティ util.importTable() は、大規模なデータファイルの MySQL リレーショナルテーブルへの高速データインポートを提供します。 このユーティリティは、入力データファイルを分析してチャンクに配布し、パラレル接続を使用してチャンクをターゲット MySQL サーバーにアップロードします。 このユーティリティは、LOAD DATA ステートメントを使用した標準のシングルスレッドアップロードよりも数回高速に大規模データインポートを完了できます。 https://dev.mysql.com/doc/mysql-shell/8.0/ja/mysql-shell-utilities-parallel-table.html

MySQL Shell にはインポート機能があります。 このインポート機能は1つのファイルを小分けにして(チャンクにして)、並列でテーブルにインポートすることで、高速に動作します。内部の実装には上記の説明にあるとおり、LOAD DATA ステートメントが利用されています。

mysqlsh> util.import_table("products.csv", {"dialect": "csv-unix", "table": "products"})'

どのように実装されているか

パラレル機能はどのように実装されているのでしょうか。ごく単純な実装として、インポート対象のファイルを分割したファイルをまず用意し、それを LOAD DATA で並列にインポートする流れが思いつきます。 しかし、実際は、このような実装になっていません(もうちょっと賢い実装になってる)。

インポート中に流れているSQL

インポート中に実行されているSQLを見ると、同じ LOAD DATA が並列に実行されていました。 LOAD DATA で指定されているファイルは全て同じファイルimport_table で指定したファイル名 /src/data/products.csv ) になっています。

mysql> SHOW FULL PROCESSLIST \G
<snip>
*************************** 4. row ***************************
     Id: 232
   User: root
   Host: localhost
     db: onlinemall
Command: Query
   Time: 5
  State: executing
   Info: LOAD DATA LOCAL INFILE '/src/data/products.csv' INTO TABLE `onlinemall`.`products` FIELDS TERMINATED BY ',' ENCLOSED BY '\"' ESCAPED BY '\\' LINES STARTING BY '' TERMINATED BY '\n'
*************************** 5. row ***************************
     Id: 233
   User: root
   Host: localhost
     db: onlinemall
Command: Query
   Time: 5
  State: executing
   Info: LOAD DATA LOCAL INFILE '/src/data/products.csv' INTO TABLE `onlinemall`.`products` FIELDS TERMINATED BY ',' ENCLOSED BY '\"' ESCAPED BY '\\' LINES STARTING BY '' TERMINATED BY '\n'
*************************** 6. row ***************************
     Id: 234
   User: root
   Host: localhost
     db: onlinemall
Command: Query
   Time: 5
  State: executing
   Info: LOAD DATA LOCAL INFILE '/src/data/products.csv' INTO TABLE `onlinemall`.`products` FIELDS TERMINATED BY ',' ENCLOSED BY '\"' ESCAPED BY '\\' LINES STARTING BY '' TERMINATED BY '\n'

通常であれば、これはまったく意味のない行為です。重複エラーになるでしょうし、パフォーマンス面でもメリットはありません。 しかし、MySQL Shell は LOAD DATAプロトコルの仕様をうまく活用して、実際は高速なデータロードを実現しています。

LOAD DATA のフロー

LOAD DATAのフローは特殊です。サーバは LOAD DATA を受け取ると、クライアントにファイルの内容を送るようリクエストします。 INSERTUPDATE と違って、クエリにデータが含まれていませんから、クライアントにファイルの中身を別途、送ってもらう必要があるわけです。

このあたりは、@tmtms さんの解説が詳しいです。

blog.tmtms.net

https://cdn-ak.f.st-hatena.com/images/fotolife/t/tmtms/20171130/20171130224205.png

@tmtms さんのブログから図をお借りしました

パラレルテーブルインポートの実装

MySQL Shell は上記の仕様をうまく使って、該当のコネクションが担当する範囲(チャンク)のみをサーバに返しています。 つまり、各コネクションは同じファイルをサーバ側から読むようにリクエストされたにも関わらず、実際には異なる内容をレスポンスしています。

f:id:mita2db:20211026234515p:plain

圧縮ファイルの扱いも同じ

このインポートユーティリティは圧縮されたファイルも扱えます。

mysqlsh> util.import_table("products.csv.gz", {"dialect": "csv-unix", "table": "products"})'

同様の流れで、解凍した内容をサーバに返すことで、本来は LOAD DATA がサポートしていない、圧縮ファイルを LOAD DATA できるように見せかけてます。 賢いですね〜。

-- 普通はエラーになる
mysql> LOAD DATA LOCAL INFILE '/src/data/products.csv.gz' INTO TABLE `onlinemall`.`products` FIELDS TERMINATED BY ',' ENCLOSED BY '\"' ESCAPED BY '\\' LINES STARTING BY '' TERMINATED BY '\n';
ERROR 1300 (HY000): Invalid utf8mb4 character string: ''
-- MySQL Shell からだと実行できているように見える
<snip>
*************************** 4. row ***************************
     Id: 225
   User: root
   Host: localhost
     db: onlinemall
Command: Query
   Time: 117
  State: executing
   Info: LOAD DATA LOCAL INFILE '/src/data/products.csv.gz' INTO TABLE `onlinemall`.`products` FIELDS TERMINATED BY ',' ENCLOSED BY '\"' ESCAPED BY '\\' LINES STARTING BY '' TERMINATED BY '\n'
4 rows in set (0.00 sec)

まとめ

  • LOAD DATA のファイル名は飾り

MySQL mysql コマンドからシェルを呼び出せなくする小技

mysql コマンドでは !system によって、シェルを呼び出すことができます。

mysql> \! uname
Linux

mysql> system uname
Linux

実装はシンプルで、標準ライブラリの system 関数が利用されています。

static int
com_shell(String *buffer MY_ATTRIBUTE((unused)),
          char *line MY_ATTRIBUTE((unused)))
{
  char *shell_cmd;

  /* Skip space from line begin */
  while (my_isspace(charset_info, *line))
    line++;
  if (!(shell_cmd = strchr(line, ' ')))
  {
    put_info("Usage: \\! shell-command", INFO_ERROR);
    return -1;
  }
  /*
    The output of the shell command does not
    get directed to the pager or the outfile
  */
  if (system(shell_cmd) == -1)
  {
    put_info(strerror(errno), INFO_ERROR, errno);
    return -1;
  }
  return 0;
}

system を使えなくする

LD_PRELOAD を使って、標準ライブラリの system 関数を差し替えてしまいましょう。 ダミーの system 関数を作ります。

#include <stdlib.h>
#include <stdio.h>

int system(const char *command) {
    fprintf(stderr, "system command is prohibited\n");
    return 0;
}
$ gcc -g -Wall  -fPIC -shared -o nosystem.so nosystem.c
$ LD_PRELOAD=$PWD/nosystem.so mysql
mysql> \! uname
system command is prohibited

mysql> system uname
system command is prohibited

使えなくなりました。めでたしめでたし(LD_PRELOAD を強制する方法は、省略。。。)

MySQL 8.0 で謎のEXPLAIN結果が出なくなってた

時々、頭の中でMySQLの気持ちになって考えた実行計画と違うものが出力されるときがあるんですよね。 まぁ、実用上、問題になることはなかったので、「MySQL ヨクワカランなー」と思って、スルーしてました。

ところが、最近、MySQL 8.0 をいじってたら、イメージしている実行計画が表示されてることに気づきました。ワイワイ!

MySQL 5.7 の謎の Index Condition Pushdown 表示

今回のお題のテーブルはこちら(Employees Sample DBに 適当にカラム c1 を追加したもの)

mysql> SHOW CREATE TABLE salaries \G
*************************** 1. row ***************************
       Table: salaries
Create Table: CREATE TABLE `salaries` (
  `emp_no` int(11) NOT NULL,
  `salary` int(11) NOT NULL,
  `from_date` date NOT NULL,
  `to_date` date NOT NULL,
  `c1` int(11) DEFAULT NULL,
  PRIMARY KEY (`emp_no`,`from_date`),
  KEY `idx1` (`to_date`,`c1`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)

to_datec1 にインデックスを貼ってあります。

説明のため、ベースとなる実行計画を示します。 idx1 インデックスから to_datec1 の条件にマッチするレコードを引き当ててます。ExtraNULL です。 これは違和感のない実行計画です。

mysql> EXPLAIN SELECT * FROM salaries 
  WHERE to_date = '2010-10-10' AND c1 = 0 \G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: salaries
   partitions: NULL
         type: ref
possible_keys: idx1
          key: idx1
      key_len: 8
          ref: const,const
         rows: 1
     filtered: 100.00
        Extra: NULL
1 row in set, 1 warning (0.00 sec)

このクエリに ORDER BY salary をつけると、ExtraUsing index condition が出現します。

mysql> EXPLAIN SELECT * FROM salaries
   WHERE to_date = '2010-10-10' AND c1 = 0 ORDER BY salary\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: salaries
   partitions: NULL
         type: ref
possible_keys: idx1
          key: idx1
      key_len: 8
          ref: const,const
         rows: 1
     filtered: 100.00
        Extra: Using index condition; Using filesort
1 row in set, 1 warning (0.00 sec)

to_date = '2010-10-10' AND c1 = 0 は、等価比較なので、インデックスコンディションプッシュダウン で解決する必要はありません。 その証拠にベースのクエリでは、Using index condtion は出てない。

ベースのクエリにソートが加わるのみなので、ExtraUsing filesort だけが追加されるんじゃないの・・・?

MySQL 8.0 の実行計画

同じ条件で、MySQL 8.0 の実行計画を見てみます。

mysql> EXPLAIN SELECT * FROM salaries
    ->    WHERE to_date = '2010-10-10' AND c1 = 0 ORDER BY salary\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: salaries
   partitions: NULL
         type: ref
possible_keys: idx1
          key: idx1
      key_len: 8
          ref: const,const
         rows: 1
     filtered: 100.00
        Extra: Using filesort
1 row in set, 1 warning (0.00 sec)

謎の Using index condtion がなくなったぞー👏👏👏

ちなみに、実行速度の変化は、未確認です。そもそも、EXPLAIN表示上のだけの問題だった可能性もありそう。