建立多實例的 MariaDB

配置 mysqld_multi

若沒有安裝,先安裝 MySQL, 另外 MySQL root 的 plugin 需採用 mysql_native_password

為了同一台機器上管理兩個 MySQL 實例,需要使用 mysqld_multi。 在 /etc/mysql/mariadb.conf.d/50-server.cnf 文件中建立兩組實例組態。

# 在您修改 50-server.cnf 之前要 停止 MySQL 非常重要!
sudo systemctl stop mysql

# 需要修改 50-server.cnf,可先備份。
mkdir ~/bak
cp /etc/mysql/mariadb.conf.d/50-server.cnf ~/bak
50-server.cnf 檔案位置
Linux

/etc/mysql/mariadb.conf.d/50-server.cnf

Windows

\\%host%\root\etc\mysql\mariadb.conf.d\50-server.cnf

在取代之前,說明一下原始檔案
# this is only for the mysqld standalone daemon
[mysqld]

#
# * Basic Settings
#
user		= mysql
pid-file	= /var/run/mysqld/mysqld.pid (1)
socket		= /var/run/mysqld/mysqld.sock
port		= 3306
basedir		= /usr
datadir		= /var/lib/mysql (2)
1 執行時建立的檔案。注意! 確認該行是否跟您安裝的 MariaDB 一樣。
2 資料庫資料夾
將下列 (6 行)
# this is only for the mysqld standalone daemon
[mysqld]

#
# * Basic Settings
#
取代 (6 行) 成
# Multiple MySQL server configuration
[mysqld_multi]
mysqld     = /usr/bin/mysqld_safe
mysqladmin = /usr/bin/mysqladmin
log        = /var/log/mysqld_multi.log
user       = multi_admin
password   = 'multipass'(1)

[mysqld2](2)
server-id = 2
innodb_flush_method = O_DIRECT
user		= mysql
pid-file	= /var/run/mysqld/mysqld2.pid
socket		= /var/run/mysqld/mysqld2.sock
port		= 3307
basedir		= /usr
datadir		= /var/lib/mysql2
tmpdir		= /tmp
lc-messages-dir	= /usr/share/mysql
skip-external-locking
bind-address		= 127.0.0.1
key_buffer_size		= 16M
max_allowed_packet	= 16M
thread_stack		= 192K
thread_cache_size       = 8
myisam-recover         = BACKUP
query_cache_limit	= 1M
query_cache_size        = 16M
# log_error = /var/log/mysql/error.log
expire_logs_days	= 10
max_binlog_size   = 100M
character-set-server  = utf8mb4
collation-server      = utf8mb4_general_ci

# this is only for the mysqld standalone daemon
[mysqld1](3)
server-id = 1
innodb_flush_method = O_DIRECT
innodb_flush_log_at_trx_commit = 1
sync_binlog = 0
binlog-format = ROW
binlog_do_db = mybb

#
# * Basic Settings
#
1 multipass 改為您要的密碼,建議不要改。
2 增加 [mysqld2] 區段 (其實是複製原始的 [mysqld] 再改資料庫位置)。
3 原始的 [mysqld] 變成 [mysqld1]。這裡會混淆,[mysqld] 區段碼覆已被覆蓋成 [mysqld1] 區段碼,原始的 [mysqld] 內容並沒有變化。

最後,把 [mysqld1] (原本的 [mysqld]) 內的 log_error 註解成 # log_error

...
# this is only for the mysqld standalone daemon
[mysqld1]
server-id = 1
...
#
# Error log - should be very few entries.
#

# log_error = /var/log/mysql/error.log (1)
1 註解本行。

為什麼? 因為在 /var/log/mysqld_multi.log 會有下列訊息,該訊息已說明要把 log-error 的組態關閉。

200925 18:46:07 mysqld_safe Can’t log to error log and syslog at the same time. Remove all --log-error configuration options for --syslog to take effect.

安裝第二個 MySQL2 實例

建立 mysqld2 (第二個 MySQL 資料庫實例)
sudo mysql_install_db --user=mysql --datadir=/var/lib/mysql2 --force
啟動 mysqld_multi 資料庫
sudo mysqld_multi start
檢查 mysqld_multi 資料庫狀態
顯示 mysqld_multi 資料庫狀態:
sudo mysqld_multi report

Reporting MariaDB servers
MariaDB server from group: mysqld2 is running
MariaDB server from group: mysqld1 is running (1)
1 如果不是上述訊息表示有錯,查一下 tail /var/log/mysqld_multi.log
檢查 MySQL 行程及通訊情況
檢查 MySQL 行程情況:

ps -e | grep "mysql" (1)

1475 ?        00:00:00 mysqld_safe
1622 ?        00:00:07 mysqld
1889 pts/4    00:00:00 mysqld_safe
2282 pts/4    00:00:02 mysqld

檢查 MySQL 通訊情況:
sudo netstat -tlpn | grep mysql (1)

tcp        0      0 127.0.0.1:3306          0.0.0.0:*               LISTEN      1622/mysqld
tcp        0      0 127.0.0.1:3307          0.0.0.0:*               LISTEN      2282/mysqld

檢查 3306 3307 通訊情況:
sudo netstat -nlt | grep 330

tcp        0      0 127.0.0.1:3306          0.0.0.0:*               LISTEN
tcp        0      0 127.0.0.1:3307          0.0.0.0:*               LISTEN
1 如果 netstat 命令不存在,執行 sudo apt install net-tools -y 安裝。

mysqld2 改為原生密碼認證

設定密碼變數 mysqlpw
mysqlpw='MySQL root 密碼'
登錄 mysqld2
sudo mysql --host=localhost --port=3307 --protocol=tcp mysql
-- 先顯示 root 用戶的遠端帳號 (在 mysql 內):
select user, host from user where user='root' and host <> 'localhost';

-- 刪除 root 除了 localhost 之外的其他遠端帳號:
delete from user where user='root' and host <> 'localhost';

-- 原生密碼認證需同時設定密碼及 plugin:
update mysql.user set password=password('$mysqlpw') where user='root';
update user set plugin='mysql_native_password' where user='root';
flush privileges;

-- 退出 mysql
exit;
以密碼認證登錄 mysql
mysql -uroot -p$mysqlpw --host=localhost --port=3307 --protocol=tcp mysql
調整時區 (在 mysql 內)
set global time_zone = '+8:00';
set time_zone = '+8:00';
檢查調整後的情況:
select timediff(now(), utc_timestamp);

結果如下:
+--------------------------------+
| timediff(now(), utc_timestamp) |
+--------------------------------+
| 08:00:00                       |
+--------------------------------+

mysqld_multi 關閉及啟動

登錄 mysql 指定 port 時必須加上 protocol=tcp 才有作用
先不指定 protocol:
mysql -uroot -p$mysqlpw --host=localhost --port=3307 -N \
-e "show variables where variable_name = 'port';"

port    3306
登錄 3307 但實際卻為 3306,顯然不正確。

指定 protocol=tcp:
mysql -uroot -p$mysqlpw --host=localhost --port=3307 --protocol=tcp -N \
-e "show variables where variable_name = 'port';"

port    3307
登錄參數加上 protocol=tcp 才正確。
授予 multi_admin 關閉資料庫權限
# mysqld1 授予 multi_admin 關閉資料庫權限
mysql -uroot -p$mysqlpw --host=localhost --port=3306 --protocol=tcp<<EOF
grant shutdown on *.* to 'multi_admin'@'localhost' identified by 'multipass'; (1)
flush privileges;
EOF

#mysqld2 授予 multi_admin 關閉資料庫權限
mysql -uroot -p$mysqlpw --host=localhost --port=3307 --protocol=tcp<<EOF
grant shutdown on *.* to 'multi_admin'@'localhost' identified by 'multipass'; (1)
flush privileges;
EOF
1 要跟 50-server.cnf 內的 password='multipass' 一致。
再來測試 mysqld_multi 是否運作正確。
停止 mysqld_multi:
sudo mysqld_multi stop

顯示 mysqld_multi 狀態:
sudo mysqld_multi report

Reporting MariaDB servers
MariaDB server from group: mysqld2 is not running (1)
MariaDB server from group: mysqld1 is not running

啟動 mysqld_multi:
sudo mysqld_multi start

顯示 mysqld_multi 狀態:
sudo mysqld_multi report

Reporting MariaDB servers
MariaDB server from group: mysqld2 is running
MariaDB server from group: mysqld1 is running

檢查 mysqld_multi.log 是否異常:
tail /var/log/mysqld_multi.log -n20
1 如果不是上述訊息表示有錯, 檢查一下前面的 multi_admin 關閉資料庫權限。
修改 50-server.cnf 應重啟 mysqld_multi (別一起貼, 個別執行)
sudo mysqld_multi stop
sudo mysqld_multi start
sudo mysqld_multi report
(務必要) 停止自動啟動 mysql 服務:
sudo systemctl disable mysql (1)
1 mysql 的服務狀態可能失效;為正常現像。
顯示 mysql 服務狀態:
systemctl status mysql

結果如下:
Status: "MariaDB server is down"
Active: inactive (dead)
開機自動啟動 mysqld_multi
sudo bash -c 'cat > /etc/init.d/mysqld_multi' << "EOF"
#!/bin/sh

### BEGIN INIT INFO
# Provides:       scriptname
# Required-Start: $remote_fs $syslog
# Required-Stop:  $remote_fs $syslog
# Default-Start:  2 3 4 5
# Default-Stop:   0 1 6
# Short-Description: Start daemon at boot time
# Description:    Enable service provided by daemon.
### END INIT INFO

bindir=/usr/bin

if [ -x $bindir/mysqld_multi ]; then(1)
  mysqld_multi="$bindir/mysqld_multi";
else
  echo "Can't execute $bindir/mysqld_multi";
  exit;
fi

bindir=/usr/bin

if test -x $bindir/mysqld_multi
then
  mysqld_multi="$bindir/mysqld_multi";
else
  echo "Can't execute $bindir/mysqld_multi";
  exit;
fi

case "$1" in
  'start' )
    "$mysqld_multi" start $2(2)
   ;;
  'stop' )
    "$mysqld_multi" stop $2
   ;;
  'report' )
    "$mysqld_multi" report $2 >&2
  ;;
  'restart' )
    "$mysqld_multi" stop $2
    "$mysqld_multi" start $2
  ;;
  'status' )
    "$mysqld_multi" report >&2
    status=4
    if [ -f /var/run/mysqld/mysqld.pid ]; then
      if [ -f /var/run/mysqld/mysqld2.pid ]; then
        status=0
      fi
    elif [ ! -f /var/run/mysqld/mysqld2.pid ]; then
      status=3
    fi
    if [ $status -eq 4 ]; then
      echo 'Usage: Make the service show question mark' >&2(3)
    fi
    exit $status
  ;;
  *)
   echo "Usage: $0 {start|stop|report|restart|status}" >&2
  ;;
esac
EOF

sudo chmod +x /etc/init.d/mysqld_multi
sudo update-rc.d mysqld_multi defaults
sudo update-rc.d mysqld_multi enable
1 啟動 mysqld_multi 服務是執行 MySQL 已經寫好的 /usr/bin/mysqld_multi (參閱: Manage Multiple MySQL Servers )。
2 雖然有實作第二個參數,但如果執行 service mysqld_multi [start | stop | restart] 將忽略第 2 個參數, 另外 service (systemctl) 只會判斷主要實例作為是否真正需要執行啟動、停止或重啟,如果主從資料庫狀態不一致其運作將會異常。 啟動資料庫使用 mysqld_multi 即可,不應採用 service
3 讓 service 顯示成未知狀態 ?,參考: service --status-all 為什麼會出現問號
檢查 mysqld_multi 服務狀態
sudo service mysqld_multi status

主從複製

說明 /etc/mysql/mariadb.conf.d/50-server.cnf
[mysqld1] (1)
server-id = 1
innodb_flush_method = O_DIRECT
innodb_flush_log_at_trx_commit = 1
sync_binlog = 1
binlog-format = ROW (2)
binlog_do_db = mybb (3)
log_bin      = /var/log/mysql/mysql-bin.log (4)
expire_logs_days = 10 (5)
1 [mysqld1] 的設定值;即為原始檔案的 [mysqld],下列變數已事先在前文加入。
2 binlog-format 採用 ROW。
3 log_do_db 同步的 database,如果多個 database 那麼寫成多行。
binlog_do_db = db1
binlog_do_db = db2
4 本行預設為前置註解 (#),把本行的註解取消。
5 [mysqld] 的原始值 (沒有改變),自動刪除10 天前 Binary Log (binlog) 檔案。

磁碟策略參數說明

innodb_flush_method=O_DIRECT,設定刷新磁碟策略

設定值為 fsync(預設值)、O_DSYNCO_DIRECT,最好的方式是去測試。

測試環境
顯示 mysql 版本:
mysql --version

mysql  Ver 15.1 Distrib 10.3.27-MariaDB

查詢 innodb_buffer_pool_size :
mysql -uroot -p$mysqlpw --host=localhost --port=3306 --protocol=tcp -N \
-e 'SELECT @@innodb_buffer_pool_size;'

134217728 (預設值 128M)
在沒有修改 innodb_flush_method 預設值為 fsync
NAS 測試 drupal 執行 cron:
time drupal cron:execute

fsync 的結果:
real    2m27.843s
user    0m51.667s
sys     0m8.643s
在 50-server.cnf 中的 [mysqld1] 區段中加入 innodb_flush_method = O_DIRECT
NAS 測試 drupal 執行 cron:
time drupal cron:execute

O_DIRECT 的結果:
real    2m23.766s
user    0m51.200s
sys     0m8.595s

上述是 NAS 內的 LXC 的測試結果。

桌上型虛擬器內 LXC 測試結果。
桌上型測試 drupal 執行 cron:
time drupal cron:execute

fsync 的結果:
real    1m20.441s
user    0m6.535s
sys     0m21.448s

O_DIRECT 的結果
real    1m15.751s
user    0m6.801s
sys     0m20.653s
innodb_flush_log_at_trx_commit = 1,設定 Commit 寫入磁碟策略。

可將 MySQL 寫入到磁碟中流程當做三個實體為 MySQL_logbuff (MySQL 日誌緩衝) OS_buff (作業系統緩衝) Disk (磁碟)

設定值 0

MySQL_logbuff 每隔 1 秒 → OS_buff → Disk

速度最快,如果 MySQL 當掉,日誌緩衝 (記憶體) 內的事務 (Transaction) 全部會消失,不過也沒有比 2 快多少, 不知道為什麼寫入 OS_buff 後要即時寫入 Disk,如果交由 OS 去管理,速度應該可以更快 (當然風險更高)。

設定值 1

MySQL_logbuff → OS_buff → Disk

即時寫入,最安全也是速度最慢的策略。

設定值 2

MySQL_logbuff → OS_buff 每隔 1 秒 → Disk

MySQL 當掉前,事務已經交給 OS_buff,如果作業系統正常,那麼事務不會流失,但什麼情況 MySQL 當掉但 OS 卻是正常?

sync_binlog = 1

設定 MySQL 二進位制日誌 (Binary log)同步到磁碟的頻率。

  • 0 當事務提交之後,MySQL 不做磁碟同步讓 OS 自行決定,效能最好。

  • 1 當每進行 1 次事務提交之後,MySQL 將進行磁碟同步強制寫入磁碟。

  • n 每進行 n 次事務提交之後,MySQL 將進行磁碟同步強制寫入磁碟。

innodb_flush_log_at_trx_commitsync_binlog 依實際情況決定,如果有不斷電或者 Slave 只作為備份,兩者可設為 0。

主從狀態同步

在主實例中建立復製用戶 (replication):
mysql -uroot -p$mysqlpw --host=localhost --port=3306 --protocol=tcp<<EOF
create user 'replication'@'localhost' identified by 'replication';
grant replication slave on *.* to 'replication'@'localhost';
flush privileges;
EOF
執行以下命令將 mybb (要同步的其一資料庫) 轉存:
mysqldump -uroot -p$mysqlpw --host=localhost --port=3306 --protocol=tcp -B \
--master-data=1 mybb > repl-1.sql  (1) (2)
1 如果出錯 mysqldump: Error: Binlogging on server not active可能是沒有取消 log_bin 註解
2 使用選項 --master-data=1 以便備份的檔案 (repl-1.sql) 包含 CHANGE MASTER 的語句。

master-data=1 會將 CHANGE MASTER 語句寫入 Dump 檔案中,在匯入從屬資料後不需要配置同步的檔案及位置。

repl-1.sql 的內容:
CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000007', MASTER_LOG_POS=843487;
將 repl-1.sql 匯入到從屬實例:
mysql -uroot -p$mysqlpw --host=localhost --port=3307 --protocol=tcp < repl-1.sql (1)
1 若已經啟用「從屬模式」,則會出現下列訊息,並提示要停止從屬模式
ERROR 1198 (HY000) at line 22: This operation cannot be performed as you have a running slave ''; run STOP SLAVE '' first
停止「從屬模式」
mysql -uroot -p$mysqlpw --host=localhost --port=3307 --protocol=tcp \
-e 'stop slave;'

停止「從屬模式」後再次匯入。

啟動「從屬模式」
mysql -uroot -p$mysqlpw --host=localhost --port=3307 --protocol=tcp \
-e 'start slave;'

手動同步「主從狀態」

--master-data=2 選項將 mybb 轉存:
mysqldump -uroot -p$mysqlpw --host=localhost --port=3306 --protocol=tcp -B \
--master-data=2 mybb > repl-2.sql
repl-2.sql 的內容
-- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000007', MASTER_LOG_POS=843487; (1)
1 master-data=1 的差別只在於一個是註解。
將 repl-2.sql 導入到從屬實例中:
mysql -uroot -p$mysqlpw --host=localhost --port=3307 --protocol=tcp < repl-2.sql
查出 Master status
mysql -uroot -p$mysqlpw --host=localhost --port=3306 --protocol=tcp -e \
'show master status;'
Master status 結果
+------------------+----------+--------------+------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000007 |   843487 | mybb,mybb    |                  | (1)
+------------------+----------+--------------+------------------+
1 Slave 按回傳值同步。
進入 Slave 同步 Master 狀態
登錄 Slave:
mysql -uroot -p$mysqlpw --host=localhost --port=3307 --protocol=tcp

在 mysql 內執行下列:
stop slave;

change master to
  master_host='localhost',
  master_user='replication',
  master_password='replication',
  master_log_file='mysql-bin.000007', (1)
  master_log_pos=843487; (1)

start slave;
1 別照抄按 Master status 的結果修改。

檢查 Slave 同步狀態是否正確
mysql -uroot -p$mysqlpw --host=localhost --port=3307 --protocol=tcp \
-e 'show slave status \G'
檢查結果
Slave_IO_State: Waiting for master to send event
    Master_Host: localhost
    Master_User: replication
    Master_Port: 3306
  Connect_Retry: 60
Master_Log_File: mysql-bin.000007 (1)
Read_Master_Log_Pos: 843487 (1)
 Relay_Log_File: ...
  Relay_Log_Pos: ...
Relay_Master_Log_File: mysql-bin.000007 (1)
...
Exec_Master_Log_Pos: 843487 (1)
...
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error:
               Last_SQL_Errno: 0
               Last_SQL_Error:
...
Slave_SQL_Running_State: Slave has read all relay log; waiting for the slave I/O thread to update it
...
1 若需要可比對 Master status

phpMyAdmin 設定 Slave Database

config.inc.php 檔案位置
Linux

/usr/share/phpmyadmin/config.inc.php

Windows

\\%host%\root\usr\share\phpmyadmin\config.inc.php

將下列 (3 行)
/**
 * phpMyAdmin configuration storage settings.
 */
取代 (3 行) 成
 /**
  * Master Server
  */

$cfg['Servers'][$i]['verbose'] = 'Master';

/**
 * Slave server
 */

$i++;
$cfg['Servers'][$i]['auth_type'] = 'cookie';
$cfg['Servers'][$i]['verbose'] = 'Slave';
$cfg['Servers'][$i]['host'] = '127.0.0.1';
$cfg['Servers'][$i]['port'] = '3307';
$cfg['Servers'][$i]['compress'] = false;
$cfg['Servers'][$i]['AllowNoPassword'] = false;

/**
 * phpMyAdmin configuration storage settings.
 */

昇級 MariaDB 10.x 至 10.3

檢查 MariaDB(mysql) 版本
mysql --version
備份說明
mysqldump --help

-x, --lock-all-tables 鎖定所有表格
-A, --all-databases   所有資料庫
備份資料庫
備份 (單例及多實例主要資料庫):
mysqldump -uroot -p$mysqlpw --host=localhost --port=3306 --protocol=tcp -x -A > db1.sql


備份多實例從屬資料庫 (單例不用執行):
mysqldump -uroot -p$mysqlpw --host=localhost --port=3307 --protocol=tcp -x -A > db2.sql
停止資料庫服務
停止多實例資料庫服務:
sudo mysqld_multi stop

停止單例資料庫服務:
sudo systemctl stop mysql
在加入 Repository 之前,先列出 sources.list 或備份,如果不小心加錯,可用回復:
cat /etc/apt/sources.list
cp /etc/apt/sources.list ~/sources.list.bak

可從官方 MariaDB 的 Repository 安裝指定的版本,在 Download MariaDB Server - MariaDB.org 選擇 Linux 版本、MariaDB 版本和存儲庫 (repository) 鏡像。
例如選擇 18.04 LTS "bionic"、MariaDB 10.3: https://mariadb.org/download/?t=repo-config&d=18.04+LTS+"bionic"&v=10.3&r_m=ossplanet

在網頁中有說明 Repository 的位置
# MariaDB 10.3 repository list - created 2023-02-02 12:47 UTC
# https://mariadb.org/download/
deb https://ftp.ubuntu-tw.org/mirror/mariadb/repo/10.3/ubuntu bionic main
# deb-src https://ftp.ubuntu-tw.org/mirror/mariadb/repo/10.3/ubuntu bionic main
執行下列命令將 MariaDB 10.3 加入存儲庫 (Repository)
sudo apt install software-properties-common
sudo apt-key adv --fetch-keys 'https://mariadb.org/mariadb_release_signing_key.asc'
sudo add-apt-repository 'deb https://ftp.ubuntu-tw.org/mirror/mariadb/repo/10.3/ubuntu bionic main' (1)
1 Ubuntu 20.04 TLS,MariaDB 10.4 為 https://ftp.ubuntu-tw.org/mirror/mariadb/repo/10.4/ubuntu focal main
更新套件及安裝 MariaDB server.
sudo apt update
sudo apt install mariadb-server -y
啟動資料庫服務
啟動多實例資料庫服務:
sudo mysqld_multi start

顯示多實例資料庫服務狀態:
sudo mysqld_multi report

Reporting MariaDB servers
MariaDB server from group: mysqld2 is running
MariaDB server from group: mysqld1 is running

啟動單例資料庫服務:
sudo systemctl start mariadb
更新資料庫
更新資料庫 (單例及多實例主要資料庫):
sudo mysql_upgrade --port=3306 --protocol=tcp -uroot -p$mysqlpw

更新多實例從屬資料庫 (單例不用執行):
sudo mysql_upgrade --port=3307 --protocol=tcp -uroot -p$mysqlpw

多實例從屬資料庫檢查 Slave 同步狀態是否正確,如果正確則不需要重新同步。