GA

2023/11/08

SHOW PROCESSLISTとinformation_schema.processlistとperformance_schema.processlistと

TL;DR

呼び出し元エントリーポイントリスト関数
SHOW PROCESSLIST (option=OFF)mysqld_list_processesGlobal_THD_manager::do_for_all_thd_copy
information_schema.processlistfill_schema_processlistGlobal_THD_manager::do_for_all_thd_copy
performance_schema.processlisttable_processlist::make_rowtable_processlist::read_row_values
SHOW PROCESSLIST (option=ON)table_processlist::make_rowtable_processlist::read_row_values

MySQL 8.2.0で information_schema.processlist がdeprecatedになり、今までの SHOW PROCESSLIST も performance_schema.processlist からのSELECTに書き換えられる予定だというリリースノートがあった。

https://dev.mysql.com/doc/relnotes/mysql/8.2/en/news-8-2-0.html

前からオラクル青山のセミナーとかで「SHOW PROCESSLISTやinformation_schema.processlistはmutex取るからオススメしない、performance_schemaのthreadsやprocesslistがオススメ」って言われていた(現地で聞いた気がするのでたぶん2020以前だと思うんだけど)

MySQL 5.7.39とそれ以降から performance_schema_show_processlist=ONにすると SHOW PROCESSLIST は performance_schema.processlist からのSELECTに内部的にディスパッチされる。

(8.2.0でそもそもinformation_schema.processlistが非推奨でperformance_schema.processlistが当然のデフォルトになるようなので、この変数自体も8.2.0とそれ以降では非推奨)

ネイティブな ( デフォルトの performance_schema_show_processlist=OFF のままの) SHOW PROCESSLIST

(gdb) bt
+bt
#0  mysqld_list_processes(THD*, char const*, bool, bool) () at /home/yoku0825/mysql-8.0.35/sql/sql_show.cc:2961
#1  0x0000000000e41602 in Sql_cmd_show_processlist::execute_inner(THD*) () at /home/yoku0825/mysql-8.0.35/sql/sql_class.h:1276
#2  0x0000000000e200c4 in Sql_cmd_dml::execute(THD*) () at /home/yoku0825/mysql-8.0.35/sql/sql_select.cc:793
#3  0x0000000000dc63a1 in mysql_execute_command(THD*, bool) () at /home/yoku0825/mysql-8.0.35/sql/sql_parse.cc:4719
#4  0x0000000000dca030 in dispatch_sql_command(THD*, Parser_state*) () at /home/yoku0825/mysql-8.0.35/sql/sql_parse.cc:5368
#5  0x0000000000dcb39e in dispatch_command(THD*, COM_DATA const*, enum_server_command) () at /home/yoku0825/mysql-8.0.35/sql/sql_parse.cc:2054
#6  0x0000000000dcd727 in do_command (thd=thd@entry=0x7fec74000d20) at /home/yoku0825/mysql-8.0.35/sql/sql_parse.cc:1439
#7  0x0000000000f24500 in handle_connection (arg=arg@entry=0x6cc4830) at /home/yoku0825/mysql-8.0.35/sql/conn_handler/connection_handler_per_thread.cc:302
#8  0x00000000024f91b5 in pfs_spawn_thread (arg=0x6be2d20) at /home/yoku0825/mysql-8.0.35/storage/perfschema/pfs.cc:3042
#9  0x00007fecd4b28ea5 in start_thread (arg=0x7fecc0657700) at pthread_create.c:307
#10 0x00007fecd3142b0d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:111

SELECT * FROM information_schema.processlist

(gdb) bt
+bt
#0  fill_schema_processlist (thd=0x7fec74000d20, tables=0x7fec7400d630) at /home/yoku0825/mysql-8.0.35/sql/sql_show.cc:3212
#1  0x0000000000e38db5 in do_fill_information_schema_table(THD*, Table_ref*, Item*) () at /home/yoku0825/mysql-8.0.35/sql/sql_show.cc:4904
#2  0x0000000001305a4d in MaterializeInformationSchemaTableIterator::Init (this=0x7fec74020118) at /home/yoku0825/mysql-8.0.35/sql/iterators/row_iterator.h:227
#3  0x0000000000e9f65c in Query_expression::ExecuteIteratorQuery(THD*) () at /opt/rh/devtoolset-11/root/usr/include/c++/11/bits/unique_ptr.h:421
#4  0x0000000000e9fa7c in Query_expression::execute(THD*) () at /home/yoku0825/mysql-8.0.35/sql/sql_union.cc:1823
#5  0x0000000000e200c4 in Sql_cmd_dml::execute(THD*) () at /home/yoku0825/mysql-8.0.35/sql/sql_select.cc:793
#6  0x0000000000dc63a1 in mysql_execute_command(THD*, bool) () at /home/yoku0825/mysql-8.0.35/sql/sql_parse.cc:4719
#7  0x0000000000dca030 in dispatch_sql_command(THD*, Parser_state*) () at /home/yoku0825/mysql-8.0.35/sql/sql_parse.cc:5368
#8  0x0000000000dcb39e in dispatch_command(THD*, COM_DATA const*, enum_server_command) () at /home/yoku0825/mysql-8.0.35/sql/sql_parse.cc:2054
#9  0x0000000000dcd727 in do_command (thd=thd@entry=0x7fec74000d20) at /home/yoku0825/mysql-8.0.35/sql/sql_parse.cc:1439
#10 0x0000000000f24500 in handle_connection (arg=arg@entry=0x6cc4830) at /home/yoku0825/mysql-8.0.35/sql/conn_handler/connection_handler_per_thread.cc:302
#11 0x00000000024f91b5 in pfs_spawn_thread (arg=0x6be2d20) at /home/yoku0825/mysql-8.0.35/storage/perfschema/pfs.cc:3042
#12 0x00007fecd4b28ea5 in start_thread (arg=0x7fecc0657700) at pthread_create.c:307
#13 0x00007fecd3142b0d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:111

SELECT * FROM performance_schema.processlist

(gdb) bt
+bt
#0  table_processlist::make_row(PFS_thread*) () at /home/yoku0825/mysql-8.0.35/storage/perfschema/table_processlist.cc:157
#1  0x00000000024ef861 in rnd_next (buf=0x7fec740b9480 "\377", this=0x7fec740b7de0) at /home/yoku0825/mysql-8.0.35/storage/perfschema/ha_perfschema.cc:1722
#2  ha_perfschema::rnd_next (this=0x7fec740b7de0, buf=0x7fec740b9480 "\377") at /home/yoku0825/mysql-8.0.35/storage/perfschema/ha_perfschema.cc:1713
#3  0x0000000001040b2c in handler::ha_rnd_next (this=0x7fec740b7de0, buf=0x7fec740b9480 "\377") at /home/yoku0825/mysql-8.0.35/sql/handler.cc:2971
#4  0x000000000119467b in TableScanIterator::Read (this=0x7fec740a83f8) at /home/yoku0825/mysql-8.0.35/sql/iterators/row_iterator.h:245
#5  0x0000000000e9f6e3 in Query_expression::ExecuteIteratorQuery(THD*) () at /opt/rh/devtoolset-11/root/usr/include/c++/11/bits/unique_ptr.h:421
#6  0x0000000000e9fa7c in Query_expression::execute(THD*) () at /home/yoku0825/mysql-8.0.35/sql/sql_union.cc:1823
#7  0x0000000000e200c4 in Sql_cmd_dml::execute(THD*) () at /home/yoku0825/mysql-8.0.35/sql/sql_select.cc:793
#8  0x0000000000dc63a1 in mysql_execute_command(THD*, bool) () at /home/yoku0825/mysql-8.0.35/sql/sql_parse.cc:4719
#9  0x0000000000dca030 in dispatch_sql_command(THD*, Parser_state*) () at /home/yoku0825/mysql-8.0.35/sql/sql_parse.cc:5368
#10 0x0000000000dcb39e in dispatch_command(THD*, COM_DATA const*, enum_server_command) () at /home/yoku0825/mysql-8.0.35/sql/sql_parse.cc:2054
#11 0x0000000000dcd727 in do_command (thd=thd@entry=0x7fec74000d20) at /home/yoku0825/mysql-8.0.35/sql/sql_parse.cc:1439
#12 0x0000000000f24500 in handle_connection (arg=arg@entry=0x6cc4830) at /home/yoku0825/mysql-8.0.35/sql/conn_handler/connection_handler_per_thread.cc:302
#13 0x00000000024f91b5 in pfs_spawn_thread (arg=0x6be2d20) at /home/yoku0825/mysql-8.0.35/storage/perfschema/pfs.cc:3042
#14 0x00007fecd4b28ea5 in start_thread (arg=0x7fecc0657700) at pthread_create.c:307
#15 0x00007fecd3142b0d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:111

performance_schema_show_processlist=ON にした SHOW PROCESSLIST

(gdb) bt
+bt
#0  table_processlist::make_row(PFS_thread*) () at /home/yoku0825/mysql-8.0.35/storage/perfschema/table_processlist.cc:157
#1  0x00000000024ef861 in rnd_next (buf=0x7fec740b9480 "\377", this=0x7fec740b7de0) at /home/yoku0825/mysql-8.0.35/storage/perfschema/ha_perfschema.cc:1722
#2  ha_perfschema::rnd_next (this=0x7fec740b7de0, buf=0x7fec740b9480 "\377") at /home/yoku0825/mysql-8.0.35/storage/perfschema/ha_perfschema.cc:1713
#3  0x0000000001040b2c in handler::ha_rnd_next (this=0x7fec740b7de0, buf=0x7fec740b9480 "\377") at /home/yoku0825/mysql-8.0.35/sql/handler.cc:2971
#4  0x000000000119467b in TableScanIterator::Read (this=0x7fec740c39c0) at /home/yoku0825/mysql-8.0.35/sql/iterators/row_iterator.h:245
#5  0x0000000001309f12 in MaterializeIterator<DummyIteratorProfiler>::MaterializeQueryBlock (this=this@entry=0x7fec740c3aa0, query_block=
      Python Exception <class 'gdb.error'> There is no member or method named _M_head_impl.:
@0x7fec740c3a50: {subquery_iterator = , select_number = 2, join = 0x7fec740c1fe0, disable_deduplication_by_hash_field = false, copy_items = true, m_total_operands = 0, m_operand_idx = 0, m_first_distinct = 0, temp_table_param = 0x7fec740c2150, is_recursive_reference = false, recursive_reader = 0x0}, stored_rows=stored_rows@entry=0x7fecc0654880)
    at /opt/rh/devtoolset-11/root/usr/include/c++/11/bits/unique_ptr.h:421
#6  0x000000000130af13 in MaterializeIterator<DummyIteratorProfiler>::Init (this=0x7fec740c3aa0) at /home/yoku0825/mysql-8.0.35/sql/iterators/composite_iterators.cc:928
#7  0x0000000000e9f65c in Query_expression::ExecuteIteratorQuery(THD*) () at /opt/rh/devtoolset-11/root/usr/include/c++/11/bits/unique_ptr.h:421
#8  0x0000000000e9fa7c in Query_expression::execute(THD*) () at /home/yoku0825/mysql-8.0.35/sql/sql_union.cc:1823
#9  0x0000000000e200c4 in Sql_cmd_dml::execute(THD*) () at /home/yoku0825/mysql-8.0.35/sql/sql_select.cc:793
#10 0x0000000000dc63a1 in mysql_execute_command(THD*, bool) () at /home/yoku0825/mysql-8.0.35/sql/sql_parse.cc:4719
#11 0x0000000000dca030 in dispatch_sql_command(THD*, Parser_state*) () at /home/yoku0825/mysql-8.0.35/sql/sql_parse.cc:5368
#12 0x0000000000dcb39e in dispatch_command(THD*, COM_DATA const*, enum_server_command) () at /home/yoku0825/mysql-8.0.35/sql/sql_parse.cc:2054
#13 0x0000000000dcd727 in do_command (thd=thd@entry=0x7fec74000d20) at /home/yoku0825/mysql-8.0.35/sql/sql_parse.cc:1439
#14 0x0000000000f24500 in handle_connection (arg=arg@entry=0x6cc4830) at /home/yoku0825/mysql-8.0.35/sql/conn_handler/connection_handler_per_thread.cc:302
#15 0x00000000024f91b5 in pfs_spawn_thread (arg=0x6be2d20) at /home/yoku0825/mysql-8.0.35/storage/perfschema/pfs.cc:3042
#16 0x00007fecd4b28ea5 in start_thread (arg=0x7fecc0657700) at pthread_create.c:307
#17 0x00007fecd3142b0d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:111

2023/11/06

ソースビルドのmysqldでthdがgdbで掴めない (MINIMAL_RELWITHDEBINFO=ON)

TL;DR

  • 8.0.29とそれ以降でソースビルドをしてる人だけだと思う
  • cmake する時に -DMINIMAL_RELWITHDEBINFO=OFF する

gdbでmysqldにアタッチした時に色々見つからなくて困った。thdとか全然見えない。

$ gdb -p $(pidof mysqld)
(gdb) b PT_show_processlist::make_cmd
+b PT_show_processlist::make_cmd
Breakpoint 1 at 0x122eaec: file /home/yoku0825/mysql-8.0.35/sql/parse_tree_nodes.cc, line 2722.
(gdb) c
+c
Continuing.
[Switching to Thread 0x7fe5dc2e9700 (LWP 13041)]

Breakpoint 1, PT_show_processlist::make_cmd () at /home/yoku0825/mysql-8.0.35/sql/parse_tree_nodes.cc:2722
2722  lex->sql_command = m_sql_command;
(gdb) n
+n
2725  bool use_pfs = pfs_processlist_enabled;
(gdb) n
+n
2727  m_sql_cmd.set_use_pfs(use_pfs);
(gdb) p use_pfs
+p use_pfs
No symbol "use_pfs" in current context.
(gdb) n
+n
2728  if (use_pfs) {
(gdb) p use_pfs
+p use_pfs
No symbol "use_pfs" in current context.
(gdb) n
+n
2729    if (build_processlist_query(m_pos, thd, m_sql_cmd.verbose()))
(gdb) n
+n
2733  return &m_sql_cmd;
(gdb) p m_sql_cmd
+p m_sql_cmd
No symbol "m_sql_cmd" in current context.
(gdb) p thd->s m_sql_cmd
+p thd->m_sql_cmd
No symbol "thd" in current context.

おかしいなあと思ってたけど、何気なく cmake3 -LA | grep -i relwith ってやったら MINIMAL_RELWITHDEBINFO なんていかにも怪しいオプションがONになっているのを見つけた。

$ cmake3 -LA | grep -i relwith
..
CMAKE_ASM_FLAGS_RELWITHDEBINFO:STRING=-O2 -g -DNDEBUG
CMAKE_BUILD_TYPE:STRING=RelWithDebInfo
CMAKE_CXX_FLAGS_RELWITHDEBINFO:STRING=-O2 -g -DNDEBUG
CMAKE_C_FLAGS_RELWITHDEBINFO:STRING=-O2 -g -DNDEBUG
CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO:STRING=
CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO:STRING=
CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO:STRING=
CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO:STRING=
MINIMAL_RELWITHDEBINFO:BOOL=ON

OFFにしてコンパイルし直す。

$ cmake3 -DMINIMAL_RELWITHDEBINFO=OFF .
$ make

見えるようになった。

(gdb) b PT_show_processlist::make_cmd
+b PT_show_processlist::make_cmd
Breakpoint 1 at 0x122eaf9: file /home/yoku0825/mysql-8.0.35/sql/parse_tree_nodes.cc, line 2725.
(gdb) c
+c
Continuing.
[Switching to Thread 0x7fce486f6700 (LWP 29941)]

Breakpoint 1, PT_show_processlist::make_cmd (this=0x7fce0c00b000, thd=0x7fce0c010580) at /home/yoku0825/mysql-8.0.35/sql/parse_tree_nodes.cc:2725
2725      bool use_pfs = pfs_processlist_enabled;
(gdb) bt
+bt
#0  PT_show_processlist::make_cmd (this=0x7fce0c00b000, thd=0x7fce0c010580) at /home/yoku0825/mysql-8.0.35/sql/parse_tree_nodes.cc:2725
#1  0x0000000000d9c5a7 in LEX::make_sql_cmd (this=0x7fce0c013990, parse_tree=<optimized out>) at /home/yoku0825/mysql-8.0.35/sql/sql_lex.cc:4949
#2  0x0000000000d3618a in THD::sql_parser (this=this@entry=0x7fce0c010580) at /home/yoku0825/mysql-8.0.35/sql/sql_class.cc:3066
#3  0x0000000000dc50b5 in parse_sql (thd=thd@entry=0x7fce0c010580, parser_state=parser_state@entry=0x7fce486f4ae0, creation_ctx=creation_ctx@entry=0x0) at /home/yoku0825/mysql-8.0.35/sql/sql_parse.cc:7132
#4  0x0000000000dc9d5d in dispatch_sql_command(THD*, Parser_state*) () at /home/yoku0825/mysql-8.0.35/sql/sql_parse.cc:5265
#5  0x0000000000dcb39e in dispatch_command(THD*, COM_DATA const*, enum_server_command) () at /home/yoku0825/mysql-8.0.35/sql/sql_parse.cc:2054
#6  0x0000000000dcd727 in do_command (thd=thd@entry=0x7fce0c010580) at /home/yoku0825/mysql-8.0.35/sql/sql_parse.cc:1439
#7  0x0000000000f24500 in handle_connection (arg=arg@entry=0x73d11f0) at /home/yoku0825/mysql-8.0.35/sql/conn_handler/connection_handler_per_thread.cc:302
#8  0x00000000024f91b5 in pfs_spawn_thread (arg=0x74b0b10) at /home/yoku0825/mysql-8.0.35/storage/perfschema/pfs.cc:3042
#9  0x00007fce78163ea5 in start_thread () from /lib64/libpthread.so.0
#10 0x00007fce7677db0d in clone () from /lib64/libc.so.6
(gdb) p use_pfs
+p use_pfs
$1 = <optimized out>
(gdb) p thd
+p thd
$2 = (THD *) 0x7fce0c010580

2023/10/30

MySQLの user@'%' にまつわる謎仕様

TL;DR

  • 昔から user@'%'user@'xxx'両方があって 認証が ‘xxx’ の方で行われた場合 、そのセッションは user@'%'user@'xxx' の両方の権限を持つ謎仕様があった
  • MySQL 8.2.0のリリースノートにある In addition, the treatment of %by the server as a synonym for localhost when checking privileges (that is, privileges granted to 'myuser'@'%' are also granted to 'myuser'@'localhost') is now also deprecated as of MySQL 8.2.0 and thus also subject to removal in a future version of MySQL. (WL #14280, WL #15676) はこれを指しているような気がする

yoku0825@'%'yoku0825@192.168.122.1 で比較してみる。
yoku0825@'%' アカウントには percent スキーマに(だけ)、 yoku0825@192.168.122.1 アカウントには ipaddr スキーマに(だけ) 権限を足す。

mysql82 9> CREATE USER yoku0825@'%';
Query OK, 0 rows affected (0.01 sec)

mysql82 9> CREATE USER yoku0825@192.168.122.1;
Query OK, 0 rows affected (0.00 sec)

mysql82 9> GRANT ALL ON percent.* TO yoku0825@'%';
Query OK, 0 rows affected (0.01 sec)

mysql82 9> GRANT ALL ON ipaddr.* TO yoku0825@192.168.122.1;
Query OK, 0 rows affected (0.01 sec)

192.168.122.1の方のアカウントを使うようにログインする。

$ mysql82 -h192.168.122.1 -uyoku0825

mysql82 12> SHOW GRANTS;
+------------------------------------------------------------------+
| Grants for yoku0825@192.168.122.1                                |
+------------------------------------------------------------------+
| GRANT USAGE ON *.* TO `yoku0825`@`192.168.122.1`                 |
| GRANT ALL PRIVILEGES ON `ipaddr`.* TO `yoku0825`@`192.168.122.1` |
+------------------------------------------------------------------+
2 rows in set (0.00 sec)

見た目上は192.168.122.1を使っていて、 ipaddr スキーマにしか権限はない。ところがどっこい

mysql82 12> CREATE DATABASE percent;
Query OK, 1 row affected (0.01 sec)

mysql82 12> CREATE TABLE percent.t1 (num INT);
Query OK, 0 rows affected (0.03 sec)

mysql82 12> DROP DATABASE percent;
Query OK, 1 row affected (0.02 sec)

mysql82 12> SHOW GRANTS;
+------------------------------------------------------------------+
| Grants for yoku0825@192.168.122.1                                |
+------------------------------------------------------------------+
| GRANT USAGE ON *.* TO `yoku0825`@`192.168.122.1`                 |
| GRANT ALL PRIVILEGES ON `ipaddr`.* TO `yoku0825`@`192.168.122.1` |
+------------------------------------------------------------------+
2 rows in set (0.00 sec)

percent スキーマにALL相当の権限が当たっている。 SHOW GRANTS は何も表示しない。

ちなみにこれ yoku0825@127.0.0.1 (= 存在しないので ‘%’の方のアカウント ) を使おうとすると

$ mysql82 -h127.0.0.1 -uyoku0825

mysql82 13> SHOW GRANTS;
+-------------------------------------------------------+
| Grants for yoku0825@%                                 |
+-------------------------------------------------------+
| GRANT USAGE ON *.* TO `yoku0825`@`%`                  |
| GRANT ALL PRIVILEGES ON `percent`.* TO `yoku0825`@`%` |
+-------------------------------------------------------+
2 rows in set (0.00 sec)

mysql82 13> CREATE DATABASE ipaddr;
ERROR 1044 (42000): Access denied for user 'yoku0825'@'%' to database 'ipaddr'

想像した通り ( yoku0825@'%'yoku0825@192.168.122.1 は別人として ) 扱われる。
ホスト部分が 192.168.% みたいなやつとどう干渉するのかは試していない。

で、これがなくなるのかしら。。


前にsh2さんがレポートしてたよなって思ったら俺も登場していた。

MySQL Bugs: #68436: user@127.0.0.1 is authorized partly as user@localhost.

2023/08/01

gtid_mode=OFFの移行元MySQLからGroupReplicationにマイグレーションするはなし

  • origin側はgtid_mode=OFF, binlog_format=MIXEDでこれを変えてはいけない
  • MySQLはこのケースに限り 8.0.28 以外でも良い
  • メンテナンスには入れられる。ただし、メンテナンスウィンドウ内でMyDumperをかけられるほどデータは小さくない
  • なおGroupReplication ≠ InnoDB Clusterとした。つまりMySQL Shellの支援とMySQL Routerプロセスは使わない
  • シングルプライマリーモード。MySQL Routerは使わないけど到達性の問題は 俺以外の誰か が何とかするものとする(実際、何とかしてくれた)

まずはフツーに空っぽの状態でGroupReplicationを組む。

これはほぼGetting Startedの通りにいった気がする(3か月くらい前なので既にやや記憶が曖昧)

深く考えずにデータを引っこ抜いて、GroupReplicationのPRIMARYノードに入れる。

Non-GTID環境からGTID環境への非同期レプリケーションは8.0.23でサポートされていたので、遠慮なくそれを使う。

GroupReplication側から

CHANGE REPLICATION SOURCE TO source_host = 'origin_replication_source', source_log_file= .., source_log_pos = .., ASSIGN_GTIDS_TO_ANONYMOUS_TRANSACTION = local FOR CHANNEL 'migration' ;

本当は(?) gtid_executedをすっきりさせたかったので group_replication_replication_name を割り当てたかったんだけど、それをやろうとするとError 4021で怒られた。

ERROR 4021 (HY000): CHANGE MASTER TO ASSIGN_GTIDS_TO_ANONYMOUS_TRANSACTIONS = <UUID> cannot be executed because the UUID value is equal to the group_replication_group_name.

local だと自分(GroupReplication PRIMARY)の server_uuid を割り当ててくれる。まあそのへんのマシンで uuidgen して持ってきても大して変わらない。

ともあれこれでレプリケーションを開始…

ERROR 3098 (HY000): The table does not comply with the requirements by external plugin

早速レプリケーション止まった…(´・ω・`)

原因は2つ、HealthCheckに使っていたMEMORYストレージエンジン(GroupReplicationはInnoDBのテーブル以外は更新できない)と、Primary KeyのないInnoDBテーブル(GroupReplicationはPKもしくはPKE.. Every table that is to be replicated by the group must have a defined primary key, or primary key equivalent where the equivalent is a non-null unique key. .. つまり全カラムがNOT NULLかつUNIQUE制約がついているインデックスが無いとエラーで更新を拒否する。

なおこの2つは同じエラーを返すので、実際はエラーになったSQLスレッドのエラーを見てどっちが原因かを判断する。

MySQL error code MY-003098 (ER_BEFORE_DML_VALIDATION_ERROR): The table does not comply with the requirements by an external plugin.

この2つ(Non-InnoDBテーブルをすべてGR側だけでもInnoDB化し、PKが無いテーブルにすべてPKをつける)が終わればようやくレプリケーションが開始できるが、今回は残念ながら新たにPKを足すという選択肢が無かったので

MySQL :: MySQL 8.0 Reference Manual :: 13.1.20.11 Generated Invisible Primary Keys

MySQL 8.0.30とそれ以降のコイツを使わせてもらうことにした。
基本的にはGR側にデータを突っ込む時だけで良いはずだけれど、過渡期(Asyncレプリケーションを組んだままの期間)が長くてorigin側にPKのないテーブルを追加で作られてしまいそうな場合は CHANGE REPLICATION SOURCE TO に一ひねり加える。

CHANGE REPLICATION SOURCE TO .., REQUIRE_TABLE_PRIMARY_KEY_CHECK = GENERATE;

REQUIRE_TABLE_PRIMARY_KEY_CHECK = GENERATEにして初めて、「originではPKが無かったものをレプリケーションの途中で横取りしてGIPKを足す」になる。これを忘れるとPKが無いままGR側にやってきてまた3098の洗礼を食らうので注意。

(なお、過渡期の間にそんなテーブルをCREATEするな、というネゴシエーションがとれるならこれは別に要らない。正直検証はしたけど、その期間にPKなしでCREATE TABLEされたテーブルなどなかった)

で、取り敢えず頭の図の状態には持って行けたが、もうひと悶着あったのでそれは次回にでも。

2023/05/08

MySQL 8.0でtx_isolationを使わせるようにするパッチ

TL;DR

  • ちょっと試してみただけなのでフツーに使うことはまずない。自分ですら使おうと思っていない。

パッチはこれだけ。

$ diff ./sql/sys_vars.cc.orig ./sql/sys_vars.cc
5196a5197,5204
> // NO_CMD_LINE - different name of the option
> static Sys_var_transaction_isolation Sys_tx_isolation(
>        "tx_isolation", "Default transaction isolation level."
>        "This variable is deprecated and will be removed in a future release.",
>        UNTRACKED_DEFAULT SESSION_VAR(transaction_isolation), NO_CMD_LINE,
>        tx_isolation_names, DEFAULT(ISO_REPEATABLE_READ), NO_MUTEX_GUARD,
>        NOT_IN_BINLOG, ON_CHECK(check_transaction_isolation));
>

両方使える ( tx_isolation がdeprecatedで transaction_isolation が推奨 ) MySQL 5.7.42の記述はこんな感じ。

https://github.com/mysql/mysql-server/blob/mysql-5.7.42/sql/sys_vars.cc#L4221-L4238

型としては Sys_var_tx_isolation で受けて、他のコードでトランザクション分離レベルを参照する時も thd->tx_isolation とか thd->variables.tx_isolation で参照している。

MySQL 8.0ではこれを逆に tx_isolationSys_var_transaction_isolation 型に受けるようにすれば良さそうな気がした。

https://github.com/mysql/mysql-server/blob/mysql-8.0.33/sql/sys_vars.cc#L5197-L5202

言うても thd->tx_isolationtx_isolation のままっぽい。

https://github.com/mysql/mysql-server/blob/mysql-8.0.33/sql/sql_class.cc#L1087

一方で、 substitute の属性はなくなっているのでdeprecated warningとかを出すのはそのままでは無理な気がするけど試してないし気にしていない。

https://github.com/mysql/mysql-server/blob/mysql-5.7.42/sql/sys_vars.h#L2100-L2116

https://github.com/mysql/mysql-server/blob/mysql-8.0.33/sql/sys_vars.h#L2343-L2354


というわけで

mysql80 9> SELECT @@version, @@tx_isolation;
+--------------+-----------------+
| @@version    | @@tx_isolation  |
+--------------+-----------------+
| 8.0.33-debug | REPEATABLE-READ |
+--------------+-----------------+
1 row in set (0.00 sec)


出来上がりはしたけど、使わずに済むことを祈る。

2023/05/04

DATETIME型と現在時刻の差分が秒数でほしい時に"-"で比較してはいけない

TL;DR

  • TIMESTAMPDIFF を使う
  • 知ってたはずなのにやらかしたので自戒を込めてメモ

mysql80 65> CREATE TABLE t11 (dt DATETIME);
Query OK, 0 rows affected (0.14 sec)

mysql80 65> INSERT INTO t11 VALUES (NOW());
Query OK, 1 row affected (0.05 sec)

mysql80 65> SELECT * FROM t11;
+---------------------+
| dt                  |
+---------------------+
| 2023-05-04 06:23:00 |
+---------------------+
1 row in set (0.01 sec)

この時刻との差が(整数の)秒が欲しいからと言って、

mysql80 65> SELECT NOW(), dt, NOW() - dt FROM t11;
+---------------------+---------------------+------------+
| NOW()               | dt                  | NOW() - dt |
+---------------------+---------------------+------------+
| 2023-05-04 06:23:10 | 2023-05-04 06:23:00 |         10 |
+---------------------+---------------------+------------+
1 row in set (0.00 sec)

こうしてはいけない。

mysql80 65> SELECT NOW(), dt, NOW() - dt FROM t11;  -- 期待したのは70
+---------------------+---------------------+------------+
| NOW()               | dt                  | NOW() - dt |
+---------------------+---------------------+------------+
| 2023-05-04 06:24:10 | 2023-05-04 06:23:00 |        110 |
+---------------------+---------------------+------------+
1 row in set (0.01 sec)

NOW() - dtCAST(NOW() AS SIGNED) - CAST(dt AS SIGNED) に変換されるので、 20230504062410 - 20230504062300 になるのでMySQL的には整数の110を返す。

基本、秒単位でしか離れないところで、分をまたいだ時だけ値がジャンプしてて変だなと思うまで思い出さなかった。反省。

正しくはTIMESTAMPDIFFでこう(名前からして、引数をUNIXTIMEにしないといけないかと思って雑にやったのが良くない…)

mysql80 65> SELECT NOW(), dt, NOW() - dt, TIMESTAMPDIFF(SECOND, dt, NOW()) FROM t11;
+---------------------+---------------------+------------+----------------------------------+
| NOW()               | dt                  | NOW() - dt | TIMESTAMPDIFF(SECOND, dt, NOW()) |
+---------------------+---------------------+------------+----------------------------------+
| 2023-05-04 06:26:51 | 2023-05-04 06:23:00 |        351 |                              231 |
+---------------------+---------------------+------------+----------------------------------+
1 row in set (0.00 sec)