我博客运行在廉价老旧的vps上,最近实现了博客数据近乎全内存运行,浏览速度提升明显。
下面是我设置全内存运行的方法。
第一层,用tmpfs把缓存挂载到内存。
有访客浏览博客时,数据请求首先会被nginx拦截,内存如果有压缩缓存,会被直接推给浏览者,整过过程php和mysql都不参与,因为缓存数据存储在内存,整个过程非常快。
《如何把W3 Total Cache页面缓存到内存里?》这篇文章详细介绍了如何设置。
将 vfs_cache_pressure 设置为 50 ,它让内核尽量少删、慢删这些目录缓存,使你的博客和 tmpfs 页面缓存能够长期处于“寻路地图健全”的满血加速状态!查询系统vfs_cache_pressure数值指令∶
cat /proc/sys/vm/vfs_cache_pressure
将swappiness设置为10左右比较合理,在物理内存不足时,启用虚拟内存,查询系统swappiness数值指令∶
cat /proc/sys/vm/swappiness
第二层,Memcached对象缓存。
Memcached的全部数据都在内存。如果在tmpfs内存盘没有找到缓存数据,php会到Memcached对象缓存里寻找数据,如果有缓存数据,就抓取数据拼装页面,把数据快速返回给浏览者,这个过程mysql不参与。在把网页发送给用户的同时,PHP 还会顺手干一件事——把拼好的 HTML 重新写回第一层(tmpfs 内存盘)。
很多缓存插件支持Memcached,记得在php里安装扩展,否则无法使用。
如果同一个服务器安装两个博客,同时开启对象缓存,为了防止冲突,需要给两个博客的缓存键名加上唯一的前缀(Salt),把它们在逻辑上隔离开。
我们需要在两个wordpress博客文件wp-config.php中, /* That's all, stop editing! Happy publishing. */ 上方加入:
define('WP_CACHE_KEY_SALT', 'blog1_');
代码中 blog1_ 可以是其他纯英文和_随意组合,只要两个博客不同就行。
第三层,数据库内存池
如果Memcached也没有缓存数据,php会从数据库内存池innodb_buffer_pool_size中直接捞出原始数据页返回。
PHP 顺手做两件事:
塞给第二层:PHP 把刚拿到的数据顺手存一份到 Memcached 里,下次就不用再查 MySQL 了。
塞给第一层:PHP 把拼接好的完整 HTML 字符串写入 tmpfs 内存盘(也就是生成 .html 和 .html.gz),然后吐给浏览器。
1,如何开启自动预热?
mysql8.4默认开启自动预热,这意味着你不需要手动去配置它们。只要你的博客运行过一段时间,数据库内存池已经把这 100MB 数据吃满了,那么即使你重启服务器或重启 MySQL 8.4,它也会在启动的瞬间把之前内存里的数据页索引重新加载一遍。你的数据库基本上天天都是“热”的。
查询自动预热是否开启的办法,用宝塔面板里的phpMyAdmin进入数据库,执行一下sql语句∶
SHOW VARIABLES LIKE 'innodb_buffer_pool_%_at_%';
得到结果是 innodb_buffer_pool_dump_at_shutdown ON
innodb_buffer_pool_load_at_startup ON
说明设置成功。
2,如何把内存池里的冷数据变成热数据?
我的两个博客数据不到100m,内存池是1000m,这个时候我们可以把冷数据也变成热数据,方法是把默认innodb_old_blocks_time = 1000 中的1000换成0 ,这样内存里的数据都是热数据,加上每天凌晨数据库备份扫描,内存池里的数据会一直保持热门状态。设置在宝塔面板文件夹,/etc/my.cnf,把innodb_old_blocks_time = 0 放在以下位置,然后保存文件,重启数据库。
[mysqld]
# ... 这里可能原本就有其他参数,比如你的 1000M 内存池设置 ...
innodb_buffer_pool_size = 1000M
# 在这里添加或修改这行:
innodb_old_blocks_time = 0
验证是否设置成功方法是在终端进入数据库,或者通过phpMyAdmin进入数据库,用以下代码查询,结果是0 ,说明设置成功。
SHOW VARIABLES LIKE 'innodb_old_blocks_time';
通过innodb_old_blocks_time = 0 加上每天晚上备份数据库,这两个操作让数据库数据全部缓存到内存池,并且全部数据都处于热门状态。
3,关闭performance_schema性能监控
关闭它可以节省几百兆内存,设置方法是在数据库配置文件中的[mysqld]下方放置代码∶
performance_schema = OFF
4,关闭innodb_flush_method = O_DIRECT ,避免缓存重复
如果不关闭innodb_flush_method ,数据库数据会在内存池和buff/cache各缓存一份,出现双重缓存,浪费内存空间,关闭后,MySQL 会直接绕过 Linux 的 Page Cache 读写磁盘文件,不仅能彻底释放被数据库占用的 buff/cache 内存,还能避免系统频繁回收缓存带来的磁盘 I/O 抖动。
关闭方法把下面代码放在数据库配置文件中innodb_buffer_pool_size的下方∶
innodb_flush_method = O_DIRECT
如何验证设置是否生效,方法是通过phpMyAdmin进入数据库,执行下面sql语句,返回结果的 Value 是 O_DIRECT,说明设置成功!
SHOW VARIABLES LIKE 'innodb_flush_method';
第四层,php进程常驻内存
1,进程模式选择。
我的博客访问量很小,所以进程选择静态模式。在静态模式下,PHP 进程永远不死。但 WordPress 是由大量开源插件拼出来的,很多插件的水平参差不齐,非常容易出现内存泄漏(Memory Leak)——也就是进程每处理一次请求,就会偷偷自己扣下几 KB 内存不还给系统。如果任由静态进程这样跑下去,这 12 个进程迟早会从小胖子变成大胖子,最后把 4G 内存吃干抹净。
所以一定要在配置文件里加上 pm.max_requests = 1024,防止进程占据内存不断膨胀,每个进程处理1024个请求后,必须重启,回归初始状态。把代码放在宝塔面板php》》FPM里∶
; 确保 pm 模式为静态,并且限制 12 个进程
pm = static
pm.max_children = 12
; 这行防内存泄漏的代码,必须且只能加在这里
pm.max_requests = 1024
2,Session存储模式选择
在宝塔面板里,把Session缓存到Memcached,这样既可以减轻磁盘负担,也可以实现用户登录状态常驻内存。
3,关闭时间戳
时间戳验证会频繁访问磁盘,影响服务器性能,所以我选择关闭,又为了让更新文件及时生效,我们设置定时任务脚本,让php重载配置。脚本如下∶
#!/bin/bash
echo "--------------------------------------------------------"
echo "开始执行 PHP 8.5 内存缓存无缝重载任务"
date "+%Y-%m-%d %H:%M:%S"
echo "--------------------------------------------------------"
# 使用 reload(重载)代替 restart(重启),无缝清空 OPcache
/etc/init.d/php-fpm-85 reload
echo "--------------------------------------------------------"
echo "PHP 8.5 缓存重载完成!"
echo "--------------------------------------------------------"
第五层,物理磁盘
在没有评论和更新文章的情况下,博客数据实现了全内存运行,反之,会出现短暂的物理磁盘写入,对于更新频率不高的个人博客来说很少见。但是这一层是保底,即使断电,也不会造成数据丢失,上面三层都做不到。
总结一下,第一层(tmpfs) 负责挡住 90% 的普通读者。第二层(Memcached) 负责拦截剩下的 8% 动态交互,尽量保护数据库。第三层(Buffer Pool) 负责在内存里消化 1.9% 的漏网之鱼。第四层(物理磁盘) 负责最后 0.1% 的数据写入,确保即使服务器炸了,博客数据也绝对不会丢。
我的博客只是简单安装了缓存插件,没搞这么复杂。