Шел 2022 год… Я в своём познании Oracle EE и нахождении багов настолько преисполнился, что мне он стал скучен и не интересен. И взор пал на MySQL.
Я в качестве Oracle EE DBA
Все версии MySQL Server, MySQL InnoDB Cluster, включая последнюю 8.0.29, имеют проблемы целостности данных.
Вкратце, MySQL Server при старте не проверяет целостность данных. И если реплика кластера потеряла файл данных пользовательской базы данных или весь каталог пользовательской базы данных, то роль PRIMARY может быть назначена этой реплике.
Первая большая проблема в том, что если нарушена целостность данных сервера, то вы никогда не узнаете об этом, пока не обратитесь к этим данным. При этом не важно как мы потеряли данные: удалили файл таблицы руками или это сделал puppet по какой-нибудь маске или как-то еще. Очевидно, если при запуске MySQL Server нет проверки соответствия физического расположения файлов метаданным сервера, то нет никакой проверки повреждения файлов данных.
Вторая большая проблема в том, что механизм групповой репликации не работает должным образом. Он замечает отсутствие файлов на реплике и даже запускает механизм клонирования, но оно не работает.
Третья большая проблема вытекает из двух предыдущих. Вы можете назначить роль PRIMARY реплике с нарушенной целостностью данных.
1 мая 2022 года Oracle подтвердил 2 бага, которые связаны с данными проблемами.
Bug #34126233 Cluster Node working but Tablespace is missing for table (incremental recovery)
Bug #34126241 Clone does not work if folder manually removed from destination
Далее, я расскажу как воспроизвести проблему.
У нас есть MySQL Commercial InnoDB Cluster 8.0.29, состоящий из 3 нод:
MySQL node1:33060+ ssl JS > cluster.status()
{
"clusterName": "testCluster29",
"defaultReplicaSet": {
"name": "default",
"primary": "node1:3306",
"ssl": "REQUIRED",
"status": "OK",
"statusText": "Cluster is ONLINE and can tolerate up to ONE failure.",
"topology": {
"node1:3306": {
"address": "node1:3306",
"memberRole": "PRIMARY",
"mode": "R/W",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.29"
},
"node2:3306": {
"address": "node2:3306",
"memberRole": "SECONDARY",
"mode": "R/O",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.29"
},
"node3:3306": {
"address": "node3:3306",
"memberRole": "SECONDARY",
"mode": "R/O",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.29"
}
},
"topologyMode": "Single-Primary"
},
"groupInformationSourceMember": "node1:3306"
}
Чтобы воспроизвести проблему, нужно:
- Создать новую базу данных и таблицу в ней.
- Остановить MySQL Server на реплике.
- Удалить на реплике файл таблицы или каталог базы данных в директории данных сервера MySQL (datadir в my.cnf).
- Запустить MySQL Server.
- Переключить PRIMARY на реплику, в которой мы удалили файл данных или базу данных.
- Обратиться к удаленным данным.
1. Создаем БД CONSISTENCY и таблицу data в ней.
MySQL node1:33060+ ssl SQL > CREATE DATABASE CONSISTENCY;
Query OK, 1 row affected (0.0145 sec)
MySQL node1:33060+ ssl SQL > USE CONSISTENCY;
Default schema set to `CONSISTENCY`.
Fetching table and column names from `CONSISTENCY` for auto-completion... Press ^C to stop.
MySQL node1:33060+ ssl CONSISTENCY SQL > CREATE TABLE data (name VARCHAR(20), owner VARCHAR(20), access VARCHAR(20));
Query OK, 0 rows affected (0.0362 sec)
2. Останавливаем MySQL Server на node2
[root@node2 ~]# systemctl stop mysqld
3. Удаляем файл таблицы data.
[root@node2 ~]# cd /var/lib/mysql/CONSISTENCY/
[root@node2 CONSISTENCY]# rm data.ibd
rm: remove regular file ‘data.ibd’? y
4. Запускаем MySQL Server
[root@node2 CONSISTENCY]# systemctl start mysqld
5. Проверяем статус кластера и видим, что идет восстановление на node2
MySQL node1:33060+ ssl CONSISTENCY JS > cluster.status()
{
"clusterName": "testCluster29",
"defaultReplicaSet": {
"name": "default",
"primary": "node1:3306",
"ssl": "REQUIRED",
"status": "OK",
"statusText": "Cluster is ONLINE and can tolerate up to ONE failure. 1 member is not active.",
"topology": {
"node1:3306": {
"address": "node1:3306",
"memberRole": "PRIMARY",
"mode": "R/W",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.29"
},
"node2:3306": {
"address": "node2:3306",
"memberRole": "SECONDARY",
"mode": "R/O",
"readReplicas": {},
"recoveryStatusText": "Recovery in progress",
"role": "HA",
"status": "RECOVERING",
"version": "8.0.29"
},
"node3:3306": {
"address": "node3:3306",
"memberRole": "SECONDARY",
"mode": "R/O",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.29"
}
},
"topologyMode": "Single-Primary"
},
"groupInformationSourceMember": "node1:3306"
}
6. Проверяем еще раз и node2 становится доступной
MySQL node1:33060+ ssl CONSISTENCY JS > cluster.status()
{
"clusterName": "testCluster29",
"defaultReplicaSet": {
"name": "default",
"primary": "node1:3306",
"ssl": "REQUIRED",
"status": "OK",
"statusText": "Cluster is ONLINE and can tolerate up to ONE failure.",
"topology": {
"node1:3306": {
"address": "node1:3306",
"memberRole": "PRIMARY",
"mode": "R/W",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.29"
},
"node2:3306": {
"address": "node2:3306",
"memberRole": "SECONDARY",
"mode": "R/O",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.29"
},
"node3:3306": {
"address": "node3:3306",
"memberRole": "SECONDARY",
"mode": "R/O",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.29"
}
},
"topologyMode": "Single-Primary"
},
"groupInformationSourceMember": "node1:3306"
}
7. Проверим появился ли удаленный файл на node2. Файла нет, но нода ONLINE
[root@node2 ~]# ls -la /var/lib/mysql/CONSISTENCY/
total 4
drwxr-x--- 2 mysql mysql 6 May 3 12:39 .
drwxr-x--x. 11 mysql mysql 4096 May 3 12:40 ..
8. Сделаем node2 PRIMARY
MySQL node1:33060+ ssl CONSISTENCY JS > cluster.setPrimaryInstance('root@node2')
Setting instance 'node2' as the primary instance of cluster 'testCluster29'...
Instance 'node1:3306' was switched from PRIMARY to SECONDARY.
Instance 'node2:3306' was switched from SECONDARY to PRIMARY.
Instance 'node3:3306' remains SECONDARY.
WARNING: The cluster internal session is not the primary member anymore. For cluster management operations please obtain a fresh cluster handle using dba.getCluster().
The instance 'node2' was successfully elected as primary.
9. Проверим состояние кластера. node2 стала PRIMARY
MySQL node1:33060+ ssl CONSISTENCY JS > cluster.status()
{
"clusterName": "testCluster29",
"defaultReplicaSet": {
"name": "default",
"primary": "node2:3306",
"ssl": "REQUIRED",
"status": "OK",
"statusText": "Cluster is ONLINE and can tolerate up to ONE failure.",
"topology": {
"node1:3306": {
"address": "node1:3306",
"memberRole": "SECONDARY",
"mode": "R/O",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.29"
},
"node2:3306": {
"address": "node2:3306",
"memberRole": "PRIMARY",
"mode": "R/W",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.29"
},
"node3:3306": {
"address": "node3:3306",
"memberRole": "SECONDARY",
"mode": "R/O",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.29"
}
},
"topologyMode": "Single-Primary"
},
"groupInformationSourceMember": "node1:3306"
}
10. Подключимся к кластеру с node2 и попробуем обратиться к удаленным файлам.
[ivanov1@node2 ~]$ mysqlsh
MySQL Shell 8.0.29-commercial
Copyright (c) 2016, 2022, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its affiliates.
Other names may be trademarks of their respective owners.
Type '\help' or '\?' for help; '\quit' to exit.
MySQL JS > \c root@node2
Creating a session to 'root@node2'
Fetching schema names for autocompletion... Press ^C to stop.
Your MySQL connection id is 44 (X protocol)
Server version: 8.0.29-commercial MySQL Enterprise Server - Commercial
No default schema selected; type \use <schema> to set one.
MySQL node2:33060+ ssl JS > cluster = dba.getCluster()
<Cluster:testCluster29>
MySQL node2:33060+ ssl JS > \sql
Switching to SQL mode... Commands end with ;
MySQL node2:33060+ ssl SQL > select * from CONSISTENCY.data;
ERROR: 1812: Tablespace is missing for table `CONSISTENCY`.`data`.
MySQL node2:33060+ ssl SQL >
11. Проверим состояние кластера. Кластер в порядке, хотя на PRIMARY мы получили ошибку "ERROR: 1812: Tablespace is missing for table `CONSISTENCY`.`data`."
MySQL node2:33060+ ssl JS > cluster.status()
{
"clusterName": "testCluster29",
"defaultReplicaSet": {
"name": "default",
"primary": "node2:3306",
"ssl": "REQUIRED",
"status": "OK",
"statusText": "Cluster is ONLINE and can tolerate up to ONE failure.",
"topology": {
"node1:3306": {
"address": "node1:3306",
"memberRole": "SECONDARY",
"mode": "R/O",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.29"
},
"node2:3306": {
"address": "node2:3306",
"memberRole": "PRIMARY",
"mode": "R/W",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.29"
},
"node3:3306": {
"address": "node3:3306",
"memberRole": "SECONDARY",
"mode": "R/O",
"readReplicas": {},
"replicationLag": null,
"role": "HA",
"status": "ONLINE",
"version": "8.0.29"
}
},
"topologyMode": "Single-Primary"
},
"groupInformationSourceMember": "node2:3306"
}
После Oracle EE наличие подобных проблем в MySQL EE InnoDB Cluster для меня — шок и нонсенс. Уверен, что Galera Cluster for MySQL и Percona XtraDB Cluster имеют те же проблемы.
Я никоим образом не хотел сказать, что MySQL плохой или не надежный. MySQL — такой, какой есть, со всеми его багами и крутыми особенностями. В каждом, тем более таком большом, продукте есть место багам. Главное их закрывать, а нам, пользователям, обходить их стороной до исправления.
Я всей душой с MySQL, я влюблен во всю команду Oracle MySQL, и особенно в команду поддержки Oracle MySQL (но, поддержку Oracle EE я презираю всем своим нутром, гребанные ублюдки). От багов никто не застрахован, главное, чтобы баги не переходили в разряд фич.