翻过了两座网站维护小山

文章目录
  1. 1. 一、磁盘占用100%
  2. 2. 二、Discuz X3.2 forum_threaddisablepos 表损坏

昨天登录 SSH 去看服务器状态的时候,磁盘占用就已经100%了(实际上算了一下,97%差不多),不过基本操作还是没表现出问题,心里就合计着等到某天真出了问题再修吧。结果一觉起来,就有人报告网站又崩溃了。没办法,要做到快速响应,赶快修吧。

这篇文章记录了今日维护碰到的两个问题:

  1. 磁盘占用100%,而且找不到可以删除的文件;
  2. Discuz X3.2 的 forum_threaddisablepos 表“损坏”。

一、磁盘占用100%

很久之前,就已经出现了匪夷所思的现象:

$ df
$ du / -sh

du 和 df 的结果相差很大

我们配的是 50G 的硬盘,实际文件占用不到 10G,磁盘占用却每天都在往上涨。

开始(9月初)的时候利用命令找了一下大文件删除:

$ find / -type f -print0 | xargs -0 du -h | sort -hr | head -20

看到是 php-fpmnginx 的日志最大,删。到去年十一月的时候,占用率已经到80%,需要每几天就删除日志(对,没弄 cron 脚本)。占用率到100%的时候页面载入就会出错,数据库读写异常,所以每次一看100%了赶快删日志。

和lan姐讨论了一下,他(嗯,没打错)认为是 inode 的问题,小文件占用了太多空间。我想了一下,很可能,毕竟我们的 info 节数据被分开来按照单个文件的方式存了,而这不是小数量。我还想着在闲时改代码,放数据库里。

科普:小文件也能占用大空间?

在 Windows 上,磁盘分配的空间单元是簇(cluster),文件占用大小(在未设置压缩属性时)是簇大小的整数倍,与文件的对应关系由分区表(partition table)记录。例如,NTFS 的默认格式化选项上,簇大小是 4K(4 KiB),也就是说即使你的文件内容只有 1.5 KiB,资源管理器显示大小为 1.5 KB,也会占用 4 KiB 的磁盘空间。在 Linux 上,对应的概念是块(block)和 inode。

平均一个文件会浪费 0.5 个存储单元的空间,文件一多就体现出来了。文件越小,浪费空间所占比重越大,就出现了许多小文件浪费了大空间的情况。

不过即使是勤劳地删除日志,最后也到了即使删除所有能删的东西磁盘占用率也上了93%的尴尬境地。就到了现在。

今天我找了一下维护的资料,查了一下 inode 的实际占用率:

$ df -i /home

实际使用的 inode 才6%

实际才用了6%,说明小文件浪费的空间没达到阈值。那么是什么问题呢?于是我谷歌了一下:“linux df 100% usage, du shows less”。

谷歌同志实在是太强大了,理解了我想找什么。结果的第一条就是我所需要的。

三楼的回答中提到:

This was caused by apache (httpd) keeping large log files in memory which had been deleted from disk.

我不熟悉 Linux 的磁盘分配策略。Windows 下,打开一个文件会自动锁定句柄,在不关闭之前文件是无法删除的。而根据lan姐(解决后问的)所说,Linux 的文件可以随意删除,但是空间释放要到关闭时才进行。(这也就是最大化利用内存映射的体现吧!)

于是按照所提示的运行命令(我们的服务器日志保存在 /var/log):

$ lsof | grep "/var" | grep deleted

然后就可以看出,是 php-fpmnginx 的日志占用着:

主要是 php-fpm 和 nginx 的“功劳”

于是重新启动服务来关闭文件并释放(因为已经删除,所以停止服务时会自动释放):

$ service nginx restart
$ service php-fpm restart

于是磁盘占用就恢复正常了:

磁盘占用恢复到正常值

至此这个问题告一段落。

二、Discuz X3.2 forum_threaddisablepos 表损坏

(注:pre_ 是 Discuz X 中的待替换表前缀,在安装的时候可以配置。例如配置为 aaa,则会创建名为 aaa_forum_threaddisablepos 的表。)

打开任何一个版块,都会报错:

Table ‘database.pre_forum_threaddisablepos’ does not exist.

由于之前也出现过类似帖子表的损坏,我就尝试了一下以前的修复命令。保持 MySQL 运行,使用 mysqlcheck

$ ./mysqlcheck --all-databases --auto-repair -u root -p

但是却报告这张表并不存在:

pre_forum_threaddisablepos 表不存在

使用 REPAIR TABLE 命令也失败了(REPAIR TABLE 是 MySQL 的方言):

使用 SQL 语句修复也失败

于是我到了表的存储目录下去找了一下这张表:

这张表只存在架构,不存在实体

只存在 .frm 保存的架构数据,不存在表的内容。这更蛋疼了。只好求助于百度(毕竟 Discuz 主要在国内用),搜索表名“forum_threaddisablepos”。还好,前几个回答就有靠谱的。这些问题发表在 Discuz 的官方站点上,但是不知道为什么我访问不了,说是域名解析失败,明明可以 ping 的。幸好可以借助百度快照查看文本内容。

首先找到的线索是,有人说(“有用户说‘有人说’”)是这是一张内存表。

另一篇帖子中,20楼提供的方案解决了我的问题。

执行语句(注意替换 pre_):

CREATE TABLE IF NOT EXISTS `pre_forum_threaddisablepos` (
`tid` mediumint(8) unsigned NOT NULL default '0',
PRIMARY KEY (`tid`)
) ENGINE=MEMORY DEFAULT CHARSET=gbk;

最后能用了。

分享到 评论