这篇主要会log一下最近碰到的问题,关于初始搭建大家可以看WordPress + Nginx + PHP-FPM在AWS LightSail上的搭建

网站报错

这个网站是我搭建的自己玩耍的博客,我时不时会登陆一下网站看看运行情况。某一次登录的时候发现了报错:

Error establishing a database connection

这个报错很明显是数据库挂了,所以马上登陆服务器查看MariaDB的日志:

Version: '5.5.68-MariaDB' socket: '/var/lib/mysql/mysql.sock' port: 3306 MariaDB Server

230101 12:21:10 mysqld_safe Number of processes running now: 0

230101 12:21:10 mysqld_safe mysqld restarted

230101 12:21:10 [Note] /usr/libexec/mysqld (mysqld 5.5.68-MariaDB) starting as process 18821 ...

230101 12:21:10 [ERROR] mysqld: Out of memory (Needed 128917504 bytes)

230101 12:21:10 [ERROR] mysqld: Out of memory (Needed 96681984 bytes)

230101 12:21:10 [ERROR] mysqld: Out of memory (Needed 72499200 bytes)

230101 12:21:12 InnoDB: The InnoDB memory heap is disabled

230101 12:21:12 InnoDB: Mutexes and rw_locks use GCC atomic builtins

230101 12:21:12 InnoDB: Compressed tables use zlib 1.2.7

230101 12:21:12 InnoDB: Using Linux native AIO

230101 12:21:12 InnoDB: Initializing buffer pool, size = 128.0M
InnoDB: mmap(137756672 bytes) failed; errno 12

230101 12:21:12 InnoDB: Completed initialization of buffer pool

230101 12:21:12 InnoDB: Fatal error: cannot allocate memory for the buffer pool

230101 12:21:12 [ERROR] Plugin 'InnoDB' init function returned error.

230101 12:21:12 [ERROR] Plugin 'InnoDB' registration as a STORAGE ENGINE failed.

230101 12:21:12 [Note] Plugin 'FEEDBACK' is disabled.

230101 12:21:12 [ERROR] Unknown/unsupported storage engine: InnoDB

230101 12:21:12 [ERROR] Aborting

数据库的报错提示无法获取足够的内存,接下来就需要找到Root cause

Root Cause猜测

因为是内存不足的报错,我的机器是单核1GB内存的机器,配置确实比较低。但是服务初始化之后,内存占用是270MB左右。剩下了接近700MB的内存,难道是短时间能有很多人访问我的网站吗?

所以打开CloudFlare的Analyst页面查看那段时间的登陆情况(我有一个Nginx服务,也能从Nginx那边查看那段时间的request发起情况):

登陆数

不出意外,访问数量不多。峰时也只有30个独立User,344个请求。这些请求数,按道理是不会导致内存溢出的。所以接下来怀疑的原因是内存回收出现问题。WordPress主题使用2个服务:

  1. MariaDB的数据库
  2. PHP-FPM的PHP后端

那么对应的问题是:

  1. PHP-FPM
    • 线程池配置失败:344个request对应了344个线程的话。根据经验344个PHP线程肯定会超出内存限制
    • 内存泄漏:我为了WordPress网站管理可以变得方便,会安装多个插件。可能插件存在内存泄漏问题
  2. MariaDB
    • InnoDB Buffer Size:就像报错的提示一样,首先怀疑了InnoDB的buffer配置不正确。

MariaDB配置

由于报错还是MariaDB,所以首先怀疑的对象还是MariaDB的InnoDB配置问题。查询MariaDB官网的内存配置页面Configuring MariaDB for Optimal Performance。我这边列几个重要的参数:

  1. innodb_buffer_pool_size:InnoDB的缓冲池大小。默认是128MB,推荐设置为可用内存的70%-80%
  2. innodb_log_file_size: InnoDB的回滚log。增大这个值,可以减少磁盘flush的操作,提高性能,但是它会增加内存使用量
  3. query_cache_type和query_cache_size:Query Cache可以储存SELECT语句的结果。如果能有效利用到query cache,可以极大的提高性能。虽然我的case是可以利用到query cache的,奈何内存太小。关闭query cache

MariaDB也会给出一些默认的配置:

  • my-small.cnf
  • my-medium.cnf
  • my-large.cnf
  • my-huge.cnf

为了减少报错概率,我先试用了my-small.cnf为模版,设置了我的配置文件:

innodb_buffer_pool_size = 16M
innodb_additional_mem_pool_size = 2M
innodb_log_file_size = 5M
innodb_log_buffer_size = 8M
innodb_flush_log_at_trx_commit = 1
innodb_lock_wait_timeout = 50
skip-external-locking
key_buffer_size = 16M
max_connections = 70
max_user_connections= 50
max_allowed_packet = 1M
table_open_cache = 8
wait_timeout = 20
query_cache_type = OFF
query_cache_size = 0
sort_buffer_size = 512K
net_buffer_length = 8K
read_buffer_size = 256K
read_rnd_buffer_size = 512K

信心满满的重启MariaDB。然而几天之后MariaDB还是光荣牺牲了。这个时候MariaDB只能占用不到100MB内存,那问题大概率不是MariaDB的配置。

PHP-FPM配置

接下来只能怀疑PHP的内存泄漏和线程池问题了。对于这俩,有几个参数可以配置:

  1. pm.max_requests:每个子线程在服务了x个request后,会自动重启。这样可以有效的减少第三方代码的内存泄漏。默认设置为0,不存在自动重启
  2. pm.max_children:最多的子线程数。默认设置为dynamic
  3. pm.max_spare_servers:期望的空闲时线程保留数

我自己写了一个简单的Python程序去ping我的网站。随着本地python并发提高,在默认参数下,php的线程数快速上升,而且平均每个线程就能占到10MB以上的内存,且随着服务在线时长增加,不断增加。最后我的服务器就在大量的PHP的线程下,耗尽了内存。

由于每次网页请求,大概会占用14%的CPU,所以最后我的设置:

pm.max_requests = 500
pm.max_children = 10
pm.max_spare_servers = 10

这样在峰值的访问时,也是CPU会先被耗尽(这个时候,大概率会TLS握手失败,但是是另一个故事了)。

结果

运行2周之后,服务器内存占用如图所示:

当前服务器内存占用

当前的配置距离完美还有很多的距离了。现在的配置内存反而没有完全利用起来。接下来可能要提高一些MariaDB可分配的资源(比如开启query cache)来提高性能,减少CPU的占用。

One thought on “1 Core 1 GB Memory小服务器WordPress调优小记”

Leave a Reply

Your email address will not be published. Required fields are marked *