MoreRSS

site iconSanguok | 山月修改

※興趣 ①閩語 ②文言 ③漢字 ④東亞各種文獻 ⑤劇畫稗玩/ACGN ⑥平面設計與排印
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

Sanguok | 山月的 RSS 预览

自建Bookwyrm无法查询远端用户?——开启数据库扩展

2024-10-12 12:01:44

在这篇博文中,笔者(山月)想记录自己如何解决BookWyrm部署过程中遇到的数据库问题,特别是在远程用户搜索时遇到的 “function similarity(character varying, unknown) does not exist” 错误。

背景

背景是笔者(山月)从YunoHost搬迁到现有环境,使用Docker Compose来运行BookWyrm,但在搜索用户时总是出现数据库报错,提示找不到相似度函数。经过查找,了解到这是由于PostgreSQL中缺少pg_trgm扩展导致的。

解法

为了解决这个问题,笔者(山月)进入PostgreSQL数据库,并运行以下命令来安装所需的扩展:

CREATE EXTENSION IF NOT EXISTS pg_trgm;

执行上述命令后,数据库中添加了相应的相似度函数,错误终于得到了修复,用户搜索功能也正常运行了。

总结

看来,有时候问题并不是代码本身,而是数据库的配置。

寄语

希望这篇文章能帮助到遇到类似问题的人,如果你也在使用BookWyrm或其他基于PostgreSQL的应用,记得检查是否已安装所有必要的扩展,尤其是像pg_trgm这样的函数库,它能为用户搜索和文本相似度匹配提供很大的帮助。

BookWyrm无法增添书本、作者、阅读进度……?——解决数据库自增序列问题

2024-10-12 11:28:53

selective focus photography of brown wooden book shelf

引言

最近笔者(山月)将BookWyrm应用从YunoHost迁移到了使用Docker Compose的独立服务器环境,然而在新的环境中遇到了严重的问题:数据库中的表无法正常新增内容,例如无法添加图书、作者,或更新阅读状态等。在调试过程中,每次进行数据迁移操作时,都会遇到数据库主键重复的报错。这篇博文记录了笔者解决这一问题的全过程,希望能够帮助遇到相似困境的朋友们。

问题背景

在将BookWyrm从YunoHost迁移到Docker Compose环境后,笔者(山月)发现新增数据的操作频繁失败,无论是添加图书、作者,还是更新阅读状态,都会遇到数据库报错的问题。具体表现为,当执行Django的migrate命令时,报错显示主键重复(例如 django.db.utils.IntegrityError: duplicate key value violates unique constraint)。

笔者最初的解决方案是手动调整有问题的数据库表的主键自增计数器。但由于涉及的表非常多,一个个手动调整非常低效。这个过程也让笔者意识到,数据库迁移中某些自增序列可能没有同步,导致插入新记录时主键重复。

解决方案:自动校准PostgreSQL自增序列

在多次尝试手动解决主键问题无果后,笔者(山月)决定寻找一种更加自动化的方法,来确保所有数据库表的自增序列都能够同步。笔者最终找到了一个有效的方法:编写一个函数自动校准数据库中所有表的自增序列。

这个函数通过遍历数据库中所有具有自增序列的表,查询每个表的最大主键值,并将自增序列设置为该最大值加一。这样,所有表的自增序列都能够与表中现有数据保持一致,避免了重复主键错误。

下面是这个自动校准函数的实现:

-- 避免重复创建函数
DROP FUNCTION IF EXISTS reset_sequences_for_tables();
-- 重新创建函数
CREATE OR REPLACE FUNCTION reset_sequences_for_tables() RETURNS VOID AS $$
DECLARE
    table_name_text TEXT;
    seq_name TEXT;
    max_id INT;
    default_value TEXT;
    pk_column_name TEXT;
BEGIN
    FOR table_name_text IN
        SELECT t.table_name
        FROM information_schema.tables AS t
        WHERE t.table_schema = 'public'
        AND EXISTS (
            SELECT 1
            FROM information_schema.columns AS c
            WHERE c.table_name = t.table_name
            AND c.column_default ILIKE 'nextval%'
        )
    LOOP
        SELECT column_name INTO pk_column_name
        FROM information_schema.columns AS c
        WHERE c.table_name = table_name_text
        AND c.column_default ILIKE 'nextval%';

        SELECT column_default INTO default_value
        FROM information_schema.columns AS c
        WHERE c.table_name = table_name_text
        AND c.column_name = pk_column_name;

        seq_name := substring(default_value from E'\'(\\w+)\'::regclass');

        IF EXISTS (SELECT 1 FROM pg_class WHERE relname = seq_name) THEN
            EXECUTE 'SELECT MAX(' || pk_column_name || ') FROM ' || table_name_text INTO max_id;

            IF max_id IS NULL THEN
                EXECUTE 'SELECT setval(' || quote_literal(seq_name) || ', 1, false)';
            ELSE
                EXECUTE 'SELECT setval(' || COALESCE(quote_literal(seq_name), 'null') || ', ' || max_id || ')';
            END IF;
        ELSE
            RAISE NOTICE 'Sequence not found for table: %', table_name_text;
        END IF;
    END LOOP;
END;
$$ LANGUAGE plpgsql;

创建函数后,执行它来重置所有自增序列的值:

SELECT reset_sequences_for_tables();

这一步将自动校准所有有自增序列的表的主键序列,让它们与表中的最大ID对应,避免重复主键的情况。

执行结果

执行该函数后,所有表的自增序列都得到了自动校准,数据库主键重复的问题也顺利解决了。执行migrate命令不再报错,BookWyrm的功能恢复正常,现在可以正常添加图书、作者和更新阅读状态。

总结

在迁移BookWyrm应用时,数据库中的自增序列可能因为不同的环境和数据迁移方式而失去同步,导致主键重复的问题。这篇博客记录了如何通过编写一个函数,自动校准PostgreSQL数据库中所有表的自增序列,确保数据一致性并解决问题。

如果你也遇到类似的问题,希望这篇文章能够对你有所帮助。

QNAP NAS使用者注意!千万莫对MariaDB做这件事……

2024-05-04 22:47:30

好吧,我标题党了,非常地微信,非常地小红书。主要是刚刚处理完问题,心情难以平抑。

简单来说,如果您是QNAP NAS使用者,在用QNAP官方提供的MariaDB(如,MariaDB 10)时,千万不要将MariaDB的配置文件(.conf)中,default-storage-engine这一行的默认配置改为InnoDB

经常使用QTS的朋友都知道,QTS虽然是基于Linux设计的,但QNAP有对Linux进行魔改,乃至我们常用的一些软件,如Apache、MySQL等,也都做了自己的魔改。网上的很多文档里提供的/data路径、.conf路径等,在QTS下也是讨不着的。想在QTS找到这些路径,有以下两种思路:

  1. /share/CACHEDEV1_DATA/.qpkg/下面对应的程序目录里翻翻看——例如传说中的MySQL配置文件my.cnf,在QTS下是/.../.qpkg/MariaDB10/etc/mariadb.conf
  2. 去传统的/etc/var之类的目录找找——不过在QTS的场合,多半是一些管道软连接,例如/etc/my.cnf实际指向/mnt/HDA_ROOT/.config/my.cnf;而迷惑人的是,该文件可能是别的MySQL实例的配置文件,而不是我们平常容易用到的QTS应用市场释出的MariaDB的配置文件。

言归正传,MariaDB的配置文件(.conf),即/.../.qpkg/MariaDB10/etc/mariadb.conf,其中的default-storage-engine这一行,默认配置为MyISAM。据说,虽然MyISAM可以作为临时解决方案,但考虑到数据库的完整性和性能,长期来看最好能使InnoDB可用。从个人的业务需求出发,当时是想要改为InnoDB了。

顺带一提,为了确认所有可用的存储引擎及其状态,可以使用这则sql命令:

SHOW ENGINES;

例如,我的场合,就收到如此输出:

+--------------------+---------+-------------------------------------------------------------------------------------------------+--------------+------+------------+
| Engine             | Support | Comment                                                                                         | Transactions | XA   | Savepoints |
+--------------------+---------+-------------------------------------------------------------------------------------------------+--------------+------+------------+
(从略)
| MyISAM             | DEFAULT | Non-transactional engine with good performance and small data footprint                         | NO           | NO   | NO         |
(从略)
| InnoDB             | NO      | Supports transactions, row-level locking, foreign keys and encryption for tables                | NULL         | NULL | NULL       |
+--------------------+---------+-------------------------------------------------------------------------------------------------+--------------+------+------------+
8 rows in set (0.000 sec) 

再次言归正传。所以要我就查找 default-storage-engine 这一行,将其更改为:

default-storage-engine = InnoDB

并确保没有任何行包含 skip-innodb。如果有,需要将其注释掉(在行前加 #)或删除。

然后增加InnoDB相关配置(可选),为了优化 InnoDB 的性能。在 [mysqld] 部分添加以下配置:

innodb_buffer_pool_size = 1G  # 根据你服务器的内存大小调整
innodb_log_file_size = 256M
innodb_log_buffer_size = 32M
innodb_flush_log_at_trx_commit = 1
innodb_file_per_table = 1
innodb_thread_concurrency = 8

修改配置文件后,需要重启数据库服务以使更改生效。一般能想到以下命令:

sudo systemctl restart mariadb.service

或者

sudo systemctl restart mysql.service

然而哪条在我的QTS都用不了。QTS图形界面下的AppCenter则不提供重启选项——这应该是为了数据稳定性考虑。于是我访问了QTS的shell界面,在App Management那边手动restart了MariaDB。

注意,一旦你尝试start或者restart一款程序,就不要再发出或startrestartstop的指令了——直到你在图形界面确确实实看到系统消息栏显示该程序“已启动”(如果你的指令是restart,那么你会先在系统消息栏看到“已停止”,过很久才会看到“已启动”,这时候才安全了),然后再去做你想要做的下一步指令。不要好像没有看到什么反应,就想要重复发出指令——在QTS下,这样会把数据搞坏。

按道理,如果数据库服务器重启成功,则需要验证InnoDB是否启用。重新连接到数据库,然后执行:

SHOW ENGINES;

检查 InnoDB 的状态是否为DEFAULTYES

我顺利重启了MariaDB,然而噩梦这才开始——从shell环境尝试访问mysql,账号密码、什么的照常要求输入,但是输进去后就访问不进mysql命令行环境了。QTS的图形界面那边也打不开MariaDB 10——虽然软件状态显示为“已启动”。

我甚至重启了NAS,并再次尝试从shell、图形界面访问,仍然不行。一度以为是QTS restart一款程序会比较暴力,导致因进程被强行中断而数据坏掉。但又觉得官方提供的实践方式应该不至于不靠谱乃尔。出于试看看的心态找到MariaDB的配置文件(.conf),将其中的default-storage-engine这一行,改回默认配置,即MyISAM。然后按前述方法从shell restart MariaDB 10。

一切恢复正常了。

作为结尾,所以,如果您是QNAP NAS使用者,在用QNAP官方提供的MariaDB(如,MariaDB 10)的时候,千万不要将MariaDB的配置文件(.conf)中,default-storage-engine这一行的默认配置改为InnoDB

解决Wikibase手动导入数据后无法新建实体之问题

2024-03-20 21:30:15

Wikibase简介

Wikibase是一组MediaWiki的扩展功能,用于在中央存储库中处理版本化数据。其主要组件包括:

  • Wikibase存储库:这是一个用于存储和管理数据的扩展功能。
  • Wikibase客户端:它可以从Wikibase存储库检索和嵌入结构化数据。

Wikibase的核心功能是提供一个协作空间,供个人和团体以结构化的方式贡献、编辑和管理信息。它可以轻松地被计算机消费,翻译成多种语言,并作为链接开放数据网络的一部分与世界共享。

解题

MediaWiki具备以.xml格式导出内容的功能,结构化的Wikibase内容(属性、项目、词位)自然不例外。

但是,如果直接从[[Special:Import]]页面上传.xml,会被告知无法导入Wikibase内容。因为系统默认阻止这样的批量上传,避免Wikibase属性(P后面的数字)、项目(Q后面的数字)、词位(L后面的数字)的唯一标识符冲突。

通过在MediaWiki服务器根目录的LocalSettings.php中,加入一行这样的代码:

# 启用Wikibase项目的导入
$wgWBRepoSettings['allowEntityImport'] = true;

来取消限制。

但是这样导入之后,再想通过[[Special:NewItem]]新建项目,却会被告知:

不可以建立一个新页面。 它已经存在。

这是因为想要新建的页面的唯一标识符与现存页面已有的唯一标识符冲突了。

——等等,可是我们在新建项目的时候,又没办法强行指定唯一标识符。难道是系统在不知道唯一标识符已经被占用的情况下,还想要指定该唯一标识符?

原因

是的。

在MediaWiki所使用的数据库里,我们能看到这一数据表:

****_wb_id_counters

****是实际设置的表头。每个人设的表头并不一样。在LocalSettings.php中可以找到表头设定。例如笔者是这么设置的:

# MySQL具体设置
$wgDBprefix = "sanguok_";

那么我就会看到这样的数据表名:

sanguok_wb_id_counters

好的。那么在这一数据表中,有以下内容(以笔者情况为例):

id_type id_value
77696B69626173652D6974656D 6

在Wikibase的wb_id_counters表中,id_typeid_value列用来追踪不同类型实体的ID计数。这里的77696B69626173652D6974656D实际上是一个十六进制编码的字符串。当将这个十六进制字符串解码为ASCII文本时,它代表了某种类型的实体标识符。

让我们将77696B69626173652D6974656D这串十六进制代码解码成ASCII文本来看看它表示什么:

hex_string = "77696B69626173652D6974656D"
ascii_string = bytes.fromhex(hex_string).decode('utf-8')
ascii_string

解码后,77696B69626173652D6974656D代表的ASCII文本是wikibase-item。这意味着在wb_id_counters表中,这个条目是用来追踪Wikibase中“项目”(即item,例如Q1Q2等)的最新ID计数的。在我的情况中,id_value6,这表明追踪到的最新项目ID是Q6

然而,在我的实际情况里,经过批量导入后,Wikibase中最新的条目是item:Q10000,这显然与wb_id_counters表中的记录不匹配。

解决

为了解决这个问题,需要根据实际的最新项目ID(在我的情况下是Q10000),手动更新wb_id_counters表中相应wikibase-item类型的id_value——例如在我的情况下,改为10000

之后,再试看看能否透过[[Special:NewItem]]新建条目。

应该能够成功的。

辰年再訪神保町

2024-02-26 13:39:18

今日,逛神保町

2019年第60回東京名物神田古本まつり 神保町交差点会場の様子
二〇一九年第六十回東京名物神田古本祭,神保町交差点會場的樣子(みそがい,CC BY-SA 4.0)

神保町這邊,素以舊書店之多聞。上次認認真真逛神保町的舊書店,已經是大疫以前的事情了。前兩年重返日京,也去了兩回神保町左近,但都是赴飯局。東京舊書街的話,本鄉正門前一帶、白山京華路逛過幾回,早稻田高馬沿途則是過門未入——畢竟現在未在彼處讀書,沒法以前那樣通學途中順路翻兩頁


今日由九段下車站落車,六號出口出站。於是一路向北。

先到了玉川堂。這是一家書道相關的店鋪。門口的玻璃櫥窗內,擺了一尊《九成宮醴泉銘》碑的縮小件。店內售有日文書道、寺社朱印帳、筆、墨等。前日謁櫻神宮時,已經「重金」求得了朱印帳,否則可能還真考慮在這裏買一冊。——不過據說,最好分別以兩本不同的朱印帳,去收集神道神社和佛教寺院的御朱印,因部分神職人員不會寫混有兩種御朱印的朱印帳。這樣的話,似乎再買一冊,亦是一擇。

山本書店,內有大量中國文、史關聯書籍。至其帙冊之語言,有日語,也有中文。這家中文書店,倒是我以前印象較淺的,算是今日收穫了。淺淺翻閱了幾本書:

  • 近代中国の思想と文学》(近代中國之思想與文學)。一看編者,居然是東大中文研。這本書,一九六七年出版,是部論文集,劈頭從嚴復開始,洎於聞一多。嚴復那邊是說幾道先生對西洋思想、著作的譯介;聞一多這邊,篇名是「聞一多與唐詩」。
  • 北京好日》,林語堂作,佐藤亮一譯。河出書房一九五一年出版,屬於《市民文庫》。譯者佐藤亮一,畢業於慶應義塾大學法學部政治學科。一九三二年,加入時事新報社,並於三六年成爲《每日新聞》的記者。在福島支局工作後,作爲軍隊記者報導中國戰線。因涉嫌持有資料而被逮捕,從四六年到次年,在北京的戰俘收容所中。回到日本後,到每日新聞東京本社工作,並開始進行翻譯活動。該部譯作,最初是在五〇年由ジープ社出版,後來由河出書房刊行之。
  • 中國民俗方言謠諺叢刊》,八冊,江蘇廣陵古籍刻印社以一九八九年五月出版。今天逛的時候,店內標價凡五千日圓。內容其實是對古代方言謠諺類書的影印。方言謠諺,有向群眾嘴裏求,和向鄉邦文獻裏求,兩種求法。個中利弊,從一九二零年代歌謠徵集運動開始,就每有爭論。按說既然是謠諺了,是群眾的真感情、真語言,無問百姓,反倒去翻古書做訓詁,有點反直覺,有種求禮於野,只爲把經註的慣性感。然而真的開始想要蒐集謠諺了,和做這些事情的人接觸了,就難免悲哀地發現大家最終還是陷入到一種歷史的漩渦裏,恐慌於俗世生活、俗世語言的不厚重,而要從古籍找東西爲自己背書,證明其有來有自,傳承有序,焉乃正確。甚者攘除異己,扶出一個正來,然後守著正在那邊創新,我則不知道該說什麼好了。
  • 廣東語詞典》,臺灣總督府編。當日看到的是國書刊行會在一九九三年十一月復刻的版本。觀編者單位即知,是日據時期的著作。標題說的「廣東語」,其實指的是臺灣客家話。翻開詞典,也很容易看到客家話的一個特徵詞「个」(音「gai」,意即「的」,原書用假名注音)。當時有司統計族裔,客家人就被登記爲「廣東人」——雖然客家人不盡然都來自廣東。(順帶一提,臺灣客語已有在線詞典語料庫,熱心有志,居功厥偉。)
  • 《現代支那語の科學》(現代漢語的科學)。我乍一看以爲是研究當時漢語裏的科學術語的(其實若然,標題不能這麼寫),然後從中可能可以看出一些近代東亞細亞詞彙與知識的流動的真趣來;但實際上是研究現代漢語(這個「現代」其實是民國大陸時期了)的。有講述北京音打敗南京音成爲正統的故事,也有對「現代」漢語的語法學描述。
  • 宣教師漢文小説の研究》(傳教士漢文小說研究),宋莉華著,鈴木陽一監譯,青木萌譯。這本的中文原著,我滿七世求不得,今天反而讓我撞見日譯本了。居然有日譯本,大概是日本研究界也把注意力放過來了?我能聯想到的相關研究,除了近代的交涉史這種顯而易見的關鍵詞,大概就是文類的輸入、語詞與知識的流動、特定文體對外來思想、價值的收編,這樣的了。另外一提「監譯」,這也是日本特色,華語世界少見。最後買了這本,售價是碼洋的一半左右。

接著去了波多野書店。店內則與前一家大不同,賣的是日本相關書籍了——歷史方面等等。在這裏買了:

  • 《蹇蹇錄》,政治家陸奧宗光的自傳回憶錄。《》:「王臣蹇蹇,匪躬之故」。書名自焉。意即大臣爲了國君,不避艱險。《錄》中有提到朝鮮東學黨起義、甲午戰爭、馬關條約、三國干涉還遼。初於一八九六年刊行。
  • 《北槎聞略》,桂川甫周著。昔大黑屋光太夫口述俄國風土,甫周聽而記之,厥有此帙。一七九四年編成。凡正文十有一卷,附錄一卷,衣類器什圖等二軸,地圖十頁。

凡兩本。皆屬岩波文庫。都是高中時候就聽聞,想著有日能一睹的書了。因爲實在是對這個曾經同樣通行漢文的國家,如何面對近代化浪潮的,留下了哪些時代的肉筆,而感到興趣。

長島書店。店內文學的書佔不少,大概可分爲日本文學,以及華語文學(的日譯,及其在日本、以日語寫就的研究著作)。

這種街邊而立的書棚,也是神保町的風景了。書棚上一般擺的是定價便宜——一兩百日元左右——的書,當然也有寫重量級全集擺在櫥櫃上。過路行人可以隨意駐足,翻閱,有興趣的話則被「誘」至店內。到了大雨時節,店員慌忙從店內擁出,爲書棚套上塑料膜,或者拉上防水帘,也是一種在地的風物詩了

文華堂。滿櫥子都是戰爭相關的書籍。近代以來、二戰爲止的書又佔了絕大部分——例如滿蒙問題舊日軍等。在此之前的,例如剛步入近代時的削藩,也有見到相關書籍。

中川書店。這本書基本可以說是迎合日本大部分中年男人趣味的書店了——大人的教養,曾經的日本有多好,日本該往何處去之類。

南海堂書店。以日本史的書籍爲主,其中大量近代史內容。

神田古書中心導覽牌

之後進到了神田古書センター(神田古書中心)。是一棟大樓,一到五層都是舊書店。一層是書肆高山本店。店中書類,跨度很大,趣味性質很強。一半是日本傳統戲曲,剩下一半是廚藝、武術(日本武道、中國武術——少林拳法、氣功等)、書道字帖之類。

二層爲漫畫店——夢野書房。大部分都是上了年頭的漫畫單行本、雜誌,紙面都有些泛黃。然而有一本《鬼滅之刃》,堂而皇之立在中間,竟毫無違和感。

三層爲鳥海書房,主打自然主題,動植物、本草、釣魚相關。四層爲梓書房,教育關聯。

みわ書房門口

五層爲みわ書房,都是舊童書。有過道通往薰風花乃堂,爲古物、礦物關聯,牆上也掛了一些植物畫。

古書中心旁邊,是神保町ブックセンター(神保町圖書中心)。一層爲書店+咖啡店,可以在店內消費飲食,一邊喝咖啡一邊從壁櫥上取書看;也可以只買書;也可以二者兼有。

二層是秦川堂書店。店內裝潢頗爲考究。店內的古書,有百年前的教科書、老文獻等。此外有老地圖、經年但無字跡的美術明信片、舊觀光手冊、舊溫泉攻略、舊書籤等古物,即便不是專門的收藏家,拾其一二,以爲裝點,也頗雅緻。

神保町圖書中心牆上的地圖

神保町一帶不止舊書店、藝術品店,一路逛下來,還有看到家古著店。

街角拐彎後,到了大雲堂書店。售書以日本史爲主,也有書道的內容。

明倫館書店,屬於自然科學系(雖然門口裝有文學賞的應徵廣告),內爲國內外的自然科學、建築、風景園林讀物。想到以前朋友來日本看望我,其中有學習風景園林者,我也有帶他們來這裏逛。

一誠堂書店。賣日本文學的書,也有賣日本近代以來出版事情的書。對於出版宅、書物宅(有這類宅的吧?)來說,應會感到興奮。看到書店的棕色櫃檯,我就想起一段往事。那時候我第一次來神保町,到這邊興奮地挑書,結帳的時候發現帶的錢不夠,還差一兩百日元。店頭的老爺爺問我是不是學生,從哪裏來,在哪裏唸書,教我務必勤勉於學習,然後居然把這不足的錢給免了。這實在是讓我很意外,我都做好割愛的準備了。想來實在暖心。

澤口書店的書,則與以下界隈相關:鐵道、電影、思想。當然也有一些日本文學作品(例如宮澤賢治)的初版。

不遠處,小宮山書店令我十分震撼。這家不純賣書。一層就是舊電影海報、畫集、寫真集、單幅畫作、單幅寫真等,陳列於櫥櫃,掛於壁上待沽。但如是則已矣。登到二層,簡直是三島樂園。牆上貼著三島由紀夫的照片(其中有與川端康成同框的照片),賣有三島文學研究的著作、三島由紀夫的真跡。其中一幅,是縱書「三島由紀夫」五字。一幅寫著:

鍊武

又一幅寫著:

處卋若大夢

太白。讀之慨然。

三層則是一些前衛藝術。四層未去,震撼而離店。

最後從神保町車站上車,結束了一日的覯書之旅。


當然,神保町一帶的書店,遠不止這些。有些人統計爲四百家,有些統計則爲130家。今天隨便走走逛逛,已感到有些累。要全部逛個遍,相信需要時間。

今天所逛過的書店,也並沒有盡記在上,只是挑了些特點殘留在腦海中的寫了。其實神保町有兩家在愛書人士當中相當有名的中文書店——東方書店內山書店

一直幻想在這樣的舊書店中打工的樣子。整理這些老書,與不同時代、地域的思想們對話,與懂行的顧客暢聊,與乘興而來的路人介紹自己的所愛。翻書,對客,個中趣事還可與網上書友分享。多事一件美事。以前關注過一位在這樣的書店裏打工的博主,後來他回中國任教了。往後沒在網上再看到相關的分享了。

PHP-FPM站点池配置调优以解决WordPress过度占用系统资源之问题

2023-12-22 18:38:00

white vintage typewriter with paper

众所周知,WordPress,简直电老虎来的。

从笔者实际运营经验来看,查看服务器状态,就会经常看到php-fpm: pool (站点域名)这一进程,对鄙服务器CPU资源的占用,其最低线稳定于⅔,动辄则99%,甚至于满格。

为此可能需要调整pm.max_childrenpm.start_serverspm.min_spare_serverspm.max_spare_servers这些参数。

参数配置之位置

PHP-FPM站点池的配置文件通常位于/etc/php/(版本号)/fpm/pool.d/www.conf。当然,有时根据实际情况,修改的对象会是/etc/php/(版本号)/fpm/pool.d/(具体域名).conf

在一些环境下,上述文件可能不支持直接修改,或者修改后也不会永久性保存。这时则根据实际情况来找寻配置位置,例如网站根目录下的.user.ini(记得配置好正确的文件权限)。

参数之修改

在许多PHP-FPM默认配置文件里,pm模式会被设置为ondemand,而pm.max_children的值被设置为4。在ondemand模式下,PHP-FPM只会在请求到达时启动新的子进程,并在子进程空闲一段时间后关闭它们。这种模式对于低流量的网站是非常有效的,但对于高流量的网站可能会导致额外的CPU消耗,因为需要频繁地创建和销毁子进程。以下是一些可能的优化建议:

  1. 修改进程管理模式

    • 考虑将pm模式从ondemand更改为dynamic。在dynamic模式下,PHP-FPM将保持一定数量的空闲进程,以便能够更快地响应新的请求。
      pm = dynamic
      
  2. 增加最大子进程数

    • pm.max_children的值决定了同时能处理多少请求。如果服务器资源允许,你可能想要增加这个值以处理更多的并发请求。
      pm.max_children = 16  ; 或更高的值,视服务器资源而定
      
  3. 设置服务器进程数

    • dynamic模式下,你还可以设置pm.start_serverspm.min_spare_serverspm.max_spare_servers参数来控制空闲进程的数量。例如:
      pm.start_servers = 2
      pm.min_spare_servers = 2
      pm.max_spare_servers = 4
      
  4. 优化最大请求数

    • pm.max_requests参数决定了每个子进程在重新生成之前可以处理多少请求。这可以防止可能的内存泄漏。你可以根据贵站的具体情况调整这个值。
      pm.max_requests = 5000  ; 或其他适合你的网站的值,4000可能够用了
      
  5. 调整空闲进程超时

    • pm.process_idle_timeout参数决定了一个子进程在被关闭之前可以保持空闲多长时间。你可以根据需要调整这个值。
      pm.process_idle_timeout = 20s  ; 或其他适合你的网站的值
      

以上建议可以帮助你优化PHP-FPM的配置,以更好地处理高流量并减少CPU的消耗。每次修改配置后,确保重启PHP-FPM服务以使更改生效。同时,建议在一个测试环境中尝试这些更改,以确保它们不会影响贵站的正常运行。

PHP的其他调优方向

  1. PHP版本

    • 确保正在使用的是最新的PHP版本。新版本的PHP通常会提供更好的性能和错误修复。
  2. 慢日志

    • 启用PHP和MySQL的慢日志功能,以便识别和优化执行缓慢的代码和查询。

PHP之外的其他因素

WordPress网站报错和服务器资源过度使用,也可能是由多种因素造成。

本文仅聚焦于PHP配置,只不过若果按照本文方式修改,仍有问题,也可以考虑下如下方向:

  1. 插件问题

    • 有些插件可能会消耗大量的资源。尝试禁用所有插件,然后逐个重新启用,以确定是哪个插件造成的问题。
  2. 主题问题

    • 有些主题可能编写不佳或包含不必要的功能,从而消耗大量资源。尝试切换到一个简单的主题,如WordPress的默认主题,看看问题是否得到解决。
  3. 代码优化

    • 确保你的WordPress网站代码优化良好。这包括简化代码,删除不必要的代码,以及确保代码没有错误。
  4. 数据库优化

    • 数据库查询可能会消耗大量资源。使用数据库优化插件,或手动清理和优化数据库。
  5. 缓存

    • 安装和配置缓存插件,以减少服务器的负载。例如,你可以使用W3 Total Cache或WP Super Cache插件。
  6. CDN服务

    • 使用内容分发网络(CDN)来减轻服务器的负担,并加速网站加载速度。
  7. 服务器配置

    • 检查服务器配置,特别是PHP和MySQL的配置。可能需要调整某些设置以改善性能。例如,可能需要增加PHP内存限制或优化PHP-FPM配置。
  8. 服务器资源

    • 考虑升级到一个更强大的服务器,或者增加更多的CPU和内存资源。
  9. 定期监控

    • 使用服务器监控工具来跟踪资源使用情况,以便在问题发生时快速识别和解决。