记录一次数据库迁移

1月20日,梦日记Wiki得到了中文论坛的友链。以后网站将会进入公众视野。

这使网站的一些问题必须重新关注,例如数据库种类和安全性。


梦日记Wiki一直使用的数据库是SQLite。轻量级,操作简单,无需过多配置。但SQLite不适合较高负载的生产环境—如果是以前内测期的梦日记Wiki,使用它没问题,但现在不行。况且一个重要的扩展,TimedMediaHandler,不支持SQLite。它用于嵌入音频与视频,一些音频(如游戏BGM)是梦日记Wiki所需要的。此外,MediaWiki对SQLite的支持尚在起步阶段,无法预料长期使用SQLite带来的风险。

安全性是另外一个问题。内测期间梦日记Wiki可以随意编辑,无需任何确认。现在,这样的设置会给网站带来潜在的安全风险。另外,MediaWiki是1.40.1版本,这不是一个长期支持版,且将很快结束生命周期。

一些历史遗留问题也值得关注,虽然它们一般不会造成问题。梦日记Wiki安装在/wiki路径下,词条目录与脚本目录重合,给SEO优化带来不便。


这些问题表明,梦日记Wiki需要进行一次大型维护,操作包括数据库的迁移,MediaWiki软件的重装和服务器rewrite。

经过考虑,我决定将梦日记Wiki迁移到MariaDB。这是维基媒体基金会推荐使用的数据库,与MediaWiki兼容性最好,而且WordPress(这个blog)也支持它,这意味着不需要再运行另一个数据库。

然而,服务器上已经运行了MySQL,并在为这个Blog提供支持。同时运行两个数据库会带来较大的内存负担,于是将这个Blog的数据库备份下来,准备迁移到MariaDB再还原:

$ mysqldump --all-databases --user=root --password --master-data > backupdb.sql

我可能遇到了真正意义上的万恶之源:此后我走了几个小时的弯路。因为“–all-databases”。

Ubuntu 22.04 LTS上的MySQL版本是8.0,MariaDB版本是10.6。很明显,“–all-databases”意味着备份全部数据库,而其中有几个数据库是MySQL或MariaDB本身使用的,用于储存数据库用户和权限等信息。这些数据库根本没有必要备份,而强行将MySQL备份导入到MariaDB会导致各种问题。真正需要备份的,只有WordPress使用的数据库。

但我当时并不清楚,把这些备份直接还原了,如下所示:

$ apt purge mysql-server mysql-client mysql-common
$ rm -rf /var/lib/mysql
$ rm -rf /etc/mysql
$ apt install mariadb-server
$ mysql -u root -p < backupdb.sql

于是,我遇到了第一个错误:

Unknown collation: 'utf8mb4_0900_ai_ci'

字符编码不对,无法导入。

于是我将所有字符串替换成支持的

$ sed -i 's/utf8mb4_0900_ai_ci/utf8_unicode_ci/g' backupdb.sql 
$ sed -i 's/utf8mb4/utf8/g' backupdb.sql
$ sed -i 's/utf8_unicode_520_ci/utf8_unicode_ci/g' backupdb.sql

嗯,字符编码没问题了。

但紧接着,又因为另一个问题无法导入:

Table 'user' already exists

查了一下,哦,原来是不同版本储存表“user”的方式不一样。

在备份文件头加入:

DROP TABLE IF EXISTS `mysql`.`global_priv`;
DROP VIEW IF EXISTS `mysql`.`user`;

再次导入,成功了!

但紧接着,第二个问题出现。当我输入mariadb命令,准备打开Shell时,它报错了:

"ERROR 1524 (HY000): Plugin 'auth_socket' is not loaded"

这时运行systemctl status mariadb时,里面已经是一堆黄字warning,我感觉事情有点不对劲了。

解决方法是,在/etc/mysql/my.cnf的[mysqld]中添加:

plugin-load-add = auth_socket.so

成功进入Shell了。

直到第三个问题出现:当我运行mysql_secure_installation时,如果我选择Y,它总是报错:

ERROR 1146 (42S02) at line 1: Table 'mysql.global_priv' doesn't exist

这是个真正的“怪”问题,让我完全找不到切入点。网上也没有任何相关解答。

我准备创建“mysql.global.priv”这个表试一下,但突然想起了一些事情:

如果一件事情过于麻烦,它很有可能就不是正确的。

这是我以前遇到问题得出的结论。于是我往前回溯,才觉得“all-databases”完全没必要。

于是我恢复了Vultr上的备份,把事情重新做了一遍。这次只备份WordPress的数据库:

$ mysqldump -u root -p wpdb > wpdb.sql

卸载MySQL并安装MariaDB后,

$ mariadb -u root -p wpdb < wpdb.sql

一切顺利!成功导入MariaDB,并在其上运行了WordPress。数据完好无损。

接下来就是迁移梦日记Wiki到MariaDB了。

使用saveDumpedImages.py备份所有图片:

import os,shutil

uploads_raw = os.popen('php /var/www/yn.ect.fyi/wiki/maintenance/run.php dumpUploads').read()
print(uploads_raw)
uploads_list=uploads_raw.splitlines()
for f in uploads_list:
    path="/var/www/yn.ect.fyi/wiki/"+f
    fname=os.path.basename(path)
    shutil.copy(path,"/root/ynpicsb/"+fname)
    print(f"found {path} and base name is {fname}")

然后使用dumpBackup.php创建一个XML备份。

在/w路径下使用MediaWiki1.39.6和新建的MariaDB数据库创建一个新Wiki,名字和图标等与原Wiki相同。

使用importDump.php和importImages.php导入原有备份和图片。

使用Composer安装一些插件,并将LocalSettings.php后手动增加的内容复制到新Wiki,把编辑权限设置为仅登录用户拥有。

一切准备就绪后,修改/etc/nginx/sites-enabled/yn.ect.fyi,添加rewrite规则:

# Location for wiki's entry points
location ~ ^/w/(index|load|api|thumb|opensearch_desc|rest|img_auth)\.php$ {
	include /etc/nginx/fastcgi_params;
	fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
	fastcgi_pass 127.0.0.1:9000; # or whatever port your PHP-FPM listens on
}

# Images
location /w/images {
	# Separate location for images/ so .php execution won't apply
}
location /w/images/deleted {
	# Deny access to deleted images folder
	deny all;
}
# MediaWiki assets (usually images)
location ~ ^/w/resources/(assets|lib|src) {
	try_files $uri =404;
	add_header Cache-Control "public";
	expires 7d;
}
# Assets, scripts and styles from skins and extensions
location ~ ^/w/(skins|extensions)/.+\.(css|js|gif|jpg|jpeg|png|svg|wasm|ttf|woff|woff2)$ {
	try_files $uri =404;
	add_header Cache-Control "public";
	expires 7d;
}
# Favicon
location = /favicon.ico {
	add_header Cache-Control "public";
	expires 7d;
}

# License and credits files
location ~ ^/w/(COPYING|CREDITS)$ {
	default_type text/plain;
}

## Uncomment the following code if you wish to use the installer/updater
## installer/updater
#location /w/mw-config/ {
#	# Do this inside of a location so it can be negated
#	location ~ \.php$ {
#		include /etc/nginx/fastcgi_params;
#		fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
#		fastcgi_pass 127.0.0.1:9000; # or whatever port your PHP-FPM listens on
#	}
#}

# Handling for Mediawiki REST API, see [[mw:API:REST_API]]
location /w/rest.php/ {
	try_files $uri $uri/ /w/rest.php?$query_string;
}

## Uncomment the following code for handling image authentication
## Also add "deny all;" in the location for /w/images above
#location /w/img_auth.php/ {
#	try_files $uri $uri/ /w/img_auth.php?$query_string;
#}

# Handling for the article path (pretty URLs)
location /wiki/ {
	rewrite ^/wiki/(?<pagename>.*)$ /w/index.php;
}

location /wiki {
	return 301 /wiki/;
}

# Allow robots.txt in case you have one
location = /robots.txt {
}
# Explicit access to the root website, redirect to main page (adapt as needed)
#location = / {
#	return 301 /wiki/Main_Page;
#}
# Every other entry point will be disallowed.
# Add specific rules for other entry points/images as needed above this
location /w/ {
	return 418;
}

大功告成!梦日记Wiki迁移到更稳定的数据库啦!

欢迎来梦日记Wiki玩!

还有,简单的往往是正确的。遇见接二连三的报错时,不能只解决眼前的错误,应该想想为什么会报错,自己之前做了什么。


评论

《“记录一次数据库迁移”》 有 1 条评论

  1. 梦日记!我爱你;数据库!我恨你(雾

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注