GA

2026/10/26

MySQL 26.10.0で2番目に気になる機能、MySQL REST Service(諦めた)

TL;DR



MySQL 26.10.0 Early Access Is Here | mysql ではリリースノートしか出てなかったので、前提条件とか色々わからずドキュメントが出てやっと真面目に試せた機能。

特にこの MySQL :: MySQL 26.10 Reference Manual :: 7.5.10.1 Prerequisites が全然わからなかったので途中で投げ出していた。

なおこのPrerequisitesを読んでアカウントを作り始めると詰まるので、 MySQL :: MySQL 26.10 Reference Manual :: 7.5.10.2 Installing MySQL REST Service Component でやるのが良さげ。


### 「MySQL Shellで」ログインしてから

 MySQL  localhost  SQL > CONFIGURE REST METADATA;

Query OK, 0 rows affected (1.6131 sec)

REST metadata configured successfully.

### ここから先はmysqlコマンドラインクライアントでもそのままmysqlshでもOKっぽい
mysql2610 19> INSTALL COMPONENT 'file://component_mysql_rest_service';
Query OK, 0 rows affected (0.001 sec)

 MySQL  localhost  SQL > SHOW GLOBAL STATUS LIKE 'mysql_rest_service.%';
+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Variable_name                                | Value                                                                                                                                                                                                                                                                                                                         |
+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| mysql_rest_service.current_http_port         | 33443                                                                                                                                                                                                                                                                                                                         |
| mysql_rest_service.current_io_worker_threads | 1                                                                                                                                                                                                                                                                                                                             |
| mysql_rest_service.current_tls_cert          | /usr/mysql/26.10.0/data/server-cert.pem                                                                                                                                                                                                                                                                                       |
| mysql_rest_service.current_tls_cipher        | ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_CCM_SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-CCM:ECDHE-ECDSA-AES128-CCM |
| mysql_rest_service.current_tls_key           | /usr/mysql/26.10.0/data/server-key.pem                                                                                                                                                                                                                                                                                        |
| mysql_rest_service.state                     | READY                                                                                                                                                                                                                                                                                                                         |
+----------------------------------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
6 rows in set (0.0008 sec)

mysql2610 19> SELECT user, host, authentication_string, plugin, account_locked FROM mysql.user;
+----------------------------------+-----------+------------------------------------------------------------------------+-----------------------+----------------+
| user                             | host      | authentication_string                                                  | plugin                | account_locked |
+----------------------------------+-----------+------------------------------------------------------------------------+-----------------------+----------------+
| mysql_rest_service_admin         | %         |                                                                        | caching_sha2_password | Y              |
| mysql_rest_service_data_provider | %         |                                                                        | caching_sha2_password | Y              |
| mysql_rest_service_dev           | %         |                                                                        | caching_sha2_password | Y              |
| mysql_rest_service_meta_provider | %         |                                                                        | caching_sha2_password | Y              |
| mysql_rest_service_schema_admin  | %         |                                                                        | caching_sha2_password | Y              |
| mysql_rest_service_user          | %         |                                                                        | caching_sha2_password | Y              |
| mysql_task_admin                 | %         |                                                                        | caching_sha2_password | Y              |
| mysql_task_user                  | %         |                                                                        | caching_sha2_password | Y              |
| mysql.infoschema                 | localhost | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED | caching_sha2_password | Y              |
| mysql.session                    | localhost | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED | caching_sha2_password | Y              |
| mysql.sys                        | localhost | $A$005$THISISACOMBINATIONOFINVALIDSALTANDPASSWORDTHATMUSTNEVERBRBEUSED | caching_sha2_password | Y              |
| mysql_mrs_data                   | localhost |                                                                        | caching_sha2_password | Y              |
| mysql_mrs_metadata               | localhost |                                                                        | caching_sha2_password | Y              |
| root                             | localhost |                                                                        | caching_sha2_password | N              |
+----------------------------------+-----------+------------------------------------------------------------------------+-----------------------+----------------+
14 rows in set (0.000 sec)

mysql2610 19> SHOW DATABASES;
+-----------------------------+
| Database                    |
+-----------------------------+
| information_schema          |
| mysql                       |
| mysql_rest_service_metadata |
| mysql_tasks                 |
| performance_schema          |
| sys                         |
+-----------------------------+
6 rows in set (0.001 sec)

CREATE USERもCREATE ROLEも全部勝手にやってくれる。authentication_stringが空なので空パスワードかよと思ったけれどアカウントロックされてた。
俺のroot@localhostが空パスワードなのは仕様です()

さてこれで

$ ss -ltpn | grep mysqld
LISTEN 0      128          0.0.0.0:33443      0.0.0.0:*    users:(("mysqld",pid=3383608,fd=77))
LISTEN 0      4096       127.0.0.1:63610      0.0.0.0:*    users:(("mysqld",pid=3383608,fd=18))
LISTEN 0      4096               *:62610            *:*    users:(("mysqld",pid=3383608,fd=20))

33443がどうやらhttps用に空けているポートっぽい。

$ curl localhost:33443
curl: (52) Empty reply from server

そのままでは何も返ってこず。
どうやらエンドポイントとスキーマの紐づけをしないといけないので再びMySQL Shellへ。

 MySQL  localhost  SQL > CREATE REST SERVICE /yoku0825;
Query OK, 0 rows affected (0.0067 sec)

REST SERVICE `/yoku0825` created successfully.

 MySQL  localhost  SQL > SHOW CREATE REST SERVICE /yoku0825\G
*************************** 1. row ***************************
CREATE REST SERVICE: CREATE OR REPLACE REST SERVICE /yoku0825
    OPTIONS {
        "http": {
            "allowedOrigin": "auto"
        },
        "headers": {
            "Access-Control-Allow-Headers": "Content-Type, Authorization, X-Requested-With, Origin, X-Auth-Token",
            "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
            "Access-Control-Allow-Credentials": "true"
        },
        "logging": {
            "request": {
                "body": true,
                "headers": true
            },
            "response": {
                "body": true,
                "headers": true
            },
            "exceptions": true
        },
        "includeLinksInResults": false,
        "returnInternalErrorDetails": true
    };
1 row in set (0.0063 sec)

 MySQL  localhost  SQL > USE REST SERVICE /yoku0825
                      -> ;
Query OK, 0 rows affected (0.0000 sec)

Now using REST SERVICE `/yoku0825`.
 MySQL  localhost  SQL > CREATE REST SCHEMA /yoku0826 FROM d1;
Query OK, 0 rows affected (0.0066 sec)

REST SCHEMA `/yoku0825/yoku0826` created successfully.

 MySQL  localhost  SQL > SHOW CREATE REST SCHEMA /yoku0826\G
*************************** 1. row ***************************
CREATE REST SCHEMA : CREATE OR REPLACE REST SCHEMA /yoku0826 ON SERVICE /yoku0825
    FROM `d1`
    AUTHENTICATION NOT REQUIRED;
1 row in set (0.0068 sec)

 MySQL  localhost  SQL > CREATE REST VIEW /t1 ON SCHEMA /yoku0826 AS t1;
Query OK, 0 rows affected (0.0082 sec)

REST VIEW `/yoku0825/yoku0826/t1` created successfully.

 MySQL  localhost  SQL > SHOW CREATE REST VIEW /t1\G
*************************** 1. row ***************************
CREATE REST VIEW: CREATE OR REPLACE REST VIEW /t1
    ON SERVICE /yoku0825 SCHEMA /yoku0826
    AS `d1`.`t1` CLASS Yoku0825Yoku0826T1 {
        num: num @KEY,
        val: val
    }
    AUTHENTICATION REQUIRED;
1 row in set (0.0066 sec)

 MySQL  localhost  SQL > ALTER REST SERVICE /yoku0825 PUBLISHED;
Query OK, 1 row affected (0.0141 sec)

publishすると /RESTサービス名/RESTスキーマ名/REST VIEW名 でデータが取れるようになりそう。

$ curl -sk https://localhost:33443/yoku0825/yoku0826/t1 | jq .
{
  "message": "Unauthorized",
  "status": 401
}

$ curl -sk 'https://localhost:33443/yoku0825/yoku0826/metadata-catalog/'  | jq .
{
  "items": [
    {
      "name": "/t1",
      "links": [
        {
          "rel": "describes",
          "href": "/yoku0825/yoku0826/t1"
        },
        {
          "rel": "canonical",
          "href": "/yoku0825/yoku0826/metadata-catalog/t1"
        }
      ]
    }
  ],
  "limit": 25,
  "offset": 0,
  "hasMore": false,
  "count": 1,
  "links": [
    {
      "rel": "self",
      "href": "/yoku0825/yoku0826/metadata-catalog/"
    }
  ]
}

SHOW CREATE REST VIEW の結果で AUTHENTICATION REQUIRED になってるからかなと思ってDROPして作り直そうとか色々したんだけど、 CREATE REST VIEW の時点で AUTHENTICATION NOT REQUIRED みたいなのは最初から指定できないみたい。

なので足すだけ足す。

 MySQL  localhost  SQL > CREATE REST ROLE read_only;
Query OK, 0 rows affected (0.0085 sec)

REST ROLE `read_only` created successfully.
 MySQL  localhost  SQL > GRANT REST READ ON SCHEMA /yoku0825 TO read_only;
Query OK, 0 rows affected (0.0074 sec)

GRANT to `read_only` added successfully.

 MySQL  localhost  SQL > CREATE REST AUTH APP aaa VENDOR MRS;
Query OK, 0 rows affected (0.0036 sec)

REST AUTH APP `aaa` created successfully.

 MySQL  localhost  SQL > CREATE REST USER 'bbb'@'aaa' IDENTIFIED BY 'VeryVerySecret1!';
Query OK, 0 rows affected (0.0102 sec)

REST USER `:"bbb"@"aaa"` created successfully.

 MySQL  localhost  SQL > GRANT REST ROLE read_only TO 'bbb'@'aaa';
Query OK, 0 rows affected (0.0075 sec)

GRANT ROLE to `bbb`@`aaa` added successfully.

 MySQL  localhost  SQL > ALTER REST SERVICE /yoku0825 ADD AUTH APP 'aaa';
Query OK, 1 row affected (0.0083 sec)

AUTH APP, USERはどうやらかっ飛ばせないっぽい(AUTH APPなしのUSER作ろうとすると怒られる)
最後の ADD AUTH APP 'xx' に気が付かなくてかなりハマった。

作ったアカウントでどうcurlに繋げるかはこうっぽい

$ curl -sk -X POST -d '{"username":"bbb", "nonce":"blahblahblah", "authApp": "aaa", "sessionType": "bearer"}' 'https://localhost:33443/yoku0825/authentication/login'
{"session":"7055341b-134a-4b61-3fc4-c0bbc0bb3fc4","iterations":5000,"nonce":"blahblahblah^F}:E36vt9bR","salt":[74,140,150,53,78,92,144,180,235,196,144,64,103,163,232,252,227,49,109,170]}

が、俺にはそんな認証を頑張るアレはないのでここで力尽きたのであった。

​

2026/10/22

MySQL 26.10.0で一番好きな機能、Health Monitor

TL;DR


MySQL 26.10.0で 商用版MySQL MySQL HeatWave(クラウド版)からバックポートされた機能。
インストール自体は簡単。

mysql2610 10> INSTALL COMPONENT 'file://component_health_monitor';
Query OK, 0 rows affected (0.402 sec)

これだけで 日々の覚書: HeatWave MySQLがどれくらいVanilla MySQL GPL版と同じくらいか考える旅 / performance_schema編 に列挙していたようなモニター系のテーブルが有効になる。

設定できそうなパラメータ群はこれ。

mysql2610 11> SELECT variable_name, SUBSTR(variable_value, 1, 15) FROM performance_schema.global_variables WHERE variable_name LIKE 'health%';
+----------------------------------------------+-------------------------------+
| variable_name                                | SUBSTR(variable_value, 1, 15) |
+----------------------------------------------+-------------------------------+
| health_monitor.disk_fallback_enable          | OFF                           |
| health_monitor.disk_fallback_force           | OFF                           |
| health_monitor.disk_forecast_report_interval | 240                           |
| health_monitor.disk_forecast_run_mode        | NORMAL                        |
| health_monitor.disk_forecast_running         | ON                            |
| health_monitor.disk_forecast_sample_interval | 60                            |
| health_monitor.disk_forecast_test_filename   |                               |
| health_monitor.disk_low_space_critical_level | 2000                          |
| health_monitor.disk_low_space_duration       | 300                           |
| health_monitor.disk_low_space_level          | 5000                          |
| health_monitor.disk_low_space_percent        | 4                             |
| health_monitor.disk_monitored                |                               |
| health_monitor.disk_recovery_enable          | ON                            |
| health_monitor.disk_recovery_increment       | 300                           |
| health_monitor.disk_recovery_level           | 5                             |
| health_monitor.disk_recovery_max_cycles      | 3                             |
| health_monitor.disk_recovery_time_1          | 300                           |
| health_monitor.disk_recovery_time_2          | 300                           |
| health_monitor.disk_recovery_window          | 86400                         |
| health_monitor.disk_retention                | 86400                         |
| health_monitor.disk_running                  | ON                            |
| health_monitor.disk_sample_rate              | 60                            |
| health_monitor.disk_usage_warning_level_1    | 20                            |
| health_monitor.disk_usage_warning_level_2    | 10                            |
| health_monitor.disk_usage_warning_level_3    | 5                             |
| health_monitor.memory_report_definition      | [{  "title" : "               |
| health_monitor.memory_reporting              | ON                            |
| health_monitor.memory_retention              | 86400                         |
| health_monitor.memory_running                | ON                            |
| health_monitor.memory_sample_rate            | 60                            |
| health_monitor.memory_usage_warning_level_1  | 1024                          |
| health_monitor.memory_usage_warning_level_2  | 500                           |
| health_monitor.memory_usage_warning_level_3  | 100                           |
| health_monitor.periodic_report_definition    | [{   "title" :                |
| health_monitor.periodic_reporting            | OFF                           |
| health_monitor.status_interval               | 10                            |
| health_monitor.system_cpu_stats_history      | ON                            |
| health_monitor.system_disk_stats_history     | ON                            |
| health_monitor.system_memory_stats_history   | ON                            |
| health_monitor.system_process_stats_history  | ON                            |
| health_monitor.system_retention              | 86400                         |
| health_monitor.system_running                | ON                            |
| health_monitor.system_sample_rate            | 60                            |
+----------------------------------------------+-------------------------------+
43 rows in set (0.001 sec)

中でも凶悪(?) なのが health_monitor.disk_fallback_enable で、デフォルトはもちろんOFFだけどDiskの残量が指定した閾値を下回ると強制的に SET GLOBAL offline_mode = ON, super_read_only = ON が走るところ。

( ´-`).oO(迂闊にこのへんの権限をいじるアカウントが無いと、Diskを拡張するしかなくて、MySQL HeatWaveだったらまだしもオンプレでこれが着火したらそれは落ちているのとほぼ一緒なのでは…

ドキュメント にももちろんあるけど、日本語話者なら(英語版もあるだろうけど) Oracle CloudのMySQL HeatWaveのヘルスモニターに関する記述 が「何が起こるのか何を起こせるのか」がまとまっていて読みやすい。

グラフはPMMとかで見ればいいけど、やっぱりSQLでもゴニョりたいとかそういう人向け。
なおヘルスモニターの出力は log_error_verbosity が3ならエラーログにも吐いてくれる。当然 performance_schema.error_log からSELECTもできる(5MB以内なら)

2026-10-22T03:14:17.330992-00:00 10 [Note] [MY-013694] [Health] Health Monitor: Registered with option usage tracker
2026-10-22T03:14:17.331004-00:00 10 [Note] [MY-013694] [Health] Health Monitor: Started
2026-10-22T03:14:27.000393-00:00 0 [Note] [MY-013694] [Health] Disk Collector: New Volume Detected (499.8G): running=ON, volume monitored='/usr/mysql/26.10.0/data/', base interval=10s, sample interval=60s, retention=86400s, max samples=1440, status interval=10, low level=5000 MiB, low percent=4%%, critical level=2000 MiB, warning levels=20/10/5%%, fallback (enable=OFF, force=OFF), low space duration=300s, recovery (enable=ON, level=5%%, time 1=300s, time 2=300s, max cycles=3, increment=300, window=86400)
2026-10-22T03:14:27.000447-00:00 0 [Warning] [MY-013695] [Health] Disk: Warning Level 1 (103.9G): volume='/usr/mysql/26.10.0/data/', avail=94.3G, total=499.8G, used=81.1%%, reserve=4.9G, critical=2.0G, recovery=29.6G, warnings=103.9G/54.4G/29.6G
2026-10-22T03:14:37.000345-00:00 0 [Warning] [MY-013695] [Health] Disk: Warning Level 1 (103.9G): volume='/usr/mysql/26.10.0/data/', avail=94.3G, total=499.8G, used=81.1%%, reserve=4.9G, critical=2.0G, recovery=29.6G, warnings=103.9G/54.4G/29.6G
2026-10-22T03:15:17.000448-00:00 0 [Note] [MY-013694] [Health] Memory: avail=13.3G, total=15.3G, used=13.1%%, mysqld=381M, warnings=1.0G/500M/100M
2026-10-22T03:15:27.000124-00:00 0 [Note] [MY-013694] [Health] Disk: Not enough data points for forecasting: Needed: 2880 Current: 1: running=ON, volume='/usr/mysql/26.10.0/data/', used=405.5G, total=499.8G, used=81.1%%, metrics=1, last forecast=n/a
2026-10-22T03:24:17.000446-00:00 0 [Note] [MY-013694] [Health] Memory: avail=13.3G, total=15.3G, used=13.0%%, mysqld=388M, warnings=1.0G/500M/100M
2026-10-22T03:24:17.000453-00:00 0 [Warning] [MY-013695] [Health] Disk: Warning Level 1 (103.9G): volume='/usr/mysql/26.10.0/data/', avail=94.3G, total=499.8G, used=81.1%%, reserve=4.9G, critical=2.0G, recovery=29.6G, warnings=103.9G/54.4G/29.6G

久々に楽しい。新機能(?) でした。俺はこれ好き。

​

2026/10/20

caching_sha2_passwordのauthentication_stringを計算してもらう

TL;DR


mysql84 149071> CREATE USER yoku0825 IDENTIFIED WITH caching_sha2_password BY 'test';
Query OK, 0 rows affected (0.00 sec)

mysql84 149071> CREATE USER yoku0826 IDENTIFIED WITH caching_sha2_password BY 'test';
Query OK, 0 rows affected (0.01 sec)

mysql84 149071> SELECT user, host, authentication_string FROM mysql.user WHERE user LIKE 'yoku%';  -- 制御文字が入ることがあって表示が変になることあるある
+----------+------+------------------------------------------------------------------------+
| user     | host | authentication_string                                                  |
+----------+------+------------------------------------------------------------------------+
| yoku0825 | %    | $A$005$2thQcC;,Av1@ IY~BihHGrWskcdpi.zGu1kMoFuqtQJ9eMjyRCkaRos93557 |
| yoku0826 | %    | $A$005Hv%b6
.&N%-JBo^3uqAgcrXEygcbJVXEdUorl/y4RVg0JEte08LGQmVJi2 |
+----------+------+------------------------------------------------------------------------+
2 rows in set (0.00 sec)

mysql84 149071> SELECT user, host, HEX(authentication_string) FROM mysql.user WHERE user LIKE 'yoku%';  -- HEXで取ると安全
+----------+------+----------------------------------------------------------------------------------------------------------------------------------------------+
| user     | host | HEX(authentication_string)                                                                                                                   |
+----------+------+----------------------------------------------------------------------------------------------------------------------------------------------+
| yoku0825 | %    | 24412430303524327468105163433B2C41763140010949591C7E42696848477257736B636470692E7A4775316B4D6F46757174514A39654D6A7952436B61526F733933353537 |
| yoku0826 | %    | 24412430303524084876031A256201360C2E264E252D4A426F155E337571416763725845796763624A56584564556F726C2F7934525667304A45746530384C47516D564A6932 |
+----------+------+----------------------------------------------------------------------------------------------------------------------------------------------+
2 rows in set (0.00 sec)

生の authentication_string の $ はデリミタで、 $A$005$ が SHA256ダイジェストで5000回ストレッチ を表すらしい ( ITERATION_MULTIPLIER は9.7.2現在 1000で固定 )。
その後、 SALT_LENGTH = 20 バイトのソルトと STORED_SHA256_DIGEST_LENGTH = 43 バイトのダイジェストがデリミタなしでくっつく。

認証としては「同じソルトで同じ回数ストレッチしたら同じダイジェストにたどり着く」だけで十分なので、「一度作ったアカウントのauthentication_stringはパスワードが同じなら使い回せる」のは mysql_native_password の時と同じ。

mysql84 149226> CREATE USER yoku0827 IDENTIFIED WITH caching_sha2_password AS x'24412430303524084876031A256201360C2E264E252D4A426F155E337571416763725845796763624A56584564556F726C2F7934525667304A45746530384C47516D564A6932'; -- yoku0826のauthentication_stringをHEXにしたもの。 x'..' は UNHEX('..') と同じだけどUNHEX関数を噛ませると構文的にエラーになるので x'..' (これはリテラル扱い) で食わせる
Query OK, 0 rows affected (0.01 sec)

$ mysql84 -uyoku0827 -ptest -e "SHOW GRANTS"
mysql: [Warning] Using a password on the command line interface can be insecure.
+--------------------------------------+
| Grants for yoku0827@%                |
+--------------------------------------+
| GRANT USAGE ON *.* TO `yoku0827`@`%` |
+--------------------------------------+

なのでソルトを自分で指定すればこの43バイトダイジェストの部分作れるのでは? と思ってはいたものの、 Caching_sha2_password::generate_crypt5 からの my_crypt_genhash を見てもopensslをちゃんと使わずに手元で計算するの無理だと思っていたが、世はAI時代でPythonの形で出してもらった。便利な世の中になったものだ…。

Kimi-K2.7-Codeに書いてもらったソルト固定でcaching_sha2_passwordの値を計算してもらうスクリプト


余談。

たまに驚かれるけれど、 mysql_native_password のハッシュは一般的なLinuxのコマンドだけでも作れたりする。

$ echo -n "mypass" | sha1sum | awk '{print $1}' | xxd -r -p | sha1sum | awk '{print toupper("*"$1)}'
*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4
  
$ mysql -h 172.17.0.6 -e "SELECT PASSWORD('mypass')"
+-------------------------------------------+
| PASSWORD('mypass')                        |
+-------------------------------------------+
| *6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4 |
+-------------------------------------------+

2026/09/25

ALTER DATABASE .. READ ONLY = 1の使いどころ

MySQL 8.0.22とそれ以降で使える ALTER DATABASE .. READ ONLY = 1 、1mysqldプロセスに1スキーマで運用することが多かったのであんまりメリットを感じてなかった( SET GLOBAL super_read_only = ON でいいじゃん ) けどなんか使いどころに出会ったのでメモ。


大前提として、 1mysqldプロセスに1スキーマ でないこと(当たり前)がある。


これ個人的には「ベストプラクティスは1mysqldプロセスに1スキーマ」だと思っていて、その方が権限の管理もやりやすい(MySQLの権限には「グローバルスコープ」「データベース(スキーマ)スコープ」「テーブルスコープ」(ほぼ例外としての「カラムスコープ」)があるけど、 denylistっぽいこと はグローバルスコープを持っている時にしか効かないし、それ以外は allowlistでしか記述できないのでテーブルスコープで列挙するのは(ROLEで楽になったとはいえ)面倒だし自動化しても下手すると事故りそう。グローバルスコープで権限付けるのは論外)し何かあった時の影響範囲も推測しやすいし調べやすい。

ただ、水平シャードを噛ませた時に1プロセス1スキーマでもスキーマ名を分けておくと将来Multi Source Replicationで集約する時にやりやすい(少なくともJOINできるようになるのでPK / UKの重複を観測して手が考えられる。同じスキーマ名だとレプリケーション組んで壊れるまで重複はわからないし、重複していたら試行錯誤のたびにレプリケーションを組み直す必要があって手間) これはチャンネル単位の CHANGE REPLICATION SOURCE TO REPLICATE_REWRITE_DB で解消しているような気がする。


個人の感想は良いとして、こんな論理レプリケーションを使ったアップグレードパスを考えた時に

これまではmysqld単位で SET GLOBAL read_only = ON でバツっと切り替えるしかなかったものが

スキーマ単位でカナリアリリースできるようになる。

レプリケーションの前方互換性があってgtid_mode = ON なら、切り戻す時の手順も全部バツっと切り替える時とそんなに変わらないはず( SET GLOBAL read_only = ? + レプリケーションの逆流だったものが ALTER DATABASE ? READ ONLY = ? に変わるだけで )

なおフツーの read_only とほぼ同じだけどSuper権限でも書き込めず、レプリケーションソースの binlog_format=ROW ならトリガーぶんもちゃんと READ ONLY SCHEMAの中で更新された(FKのCASCADEは試してない)

俺の使用用途には十分な気がする。


【2026/09/25 18:31】

そういえば  information_schema.schemata でREAD ONLYが見えないの不便だなって思ってたけど、

The READ ONLY option, if enabled, is displayed in the INFORMATION_SCHEMA SCHEMATA_EXTENSIONS table. See Section 28.3.32, “The INFORMATION_SCHEMA SCHEMATA_EXTENSIONS Table”.

https://dev.mysql.com/doc/refman/8.4/en/alter-database.html#alter-database-read-only

危うくFeature Requestを出してしまうところだった…