没有不值得去解决的问题,也没有不值得去学习的技术!

Upgrading Redis 7.0.11 to 8.10.0 on Alibaba Cloud OneinStack: Kernel Optimization and Rollback Protection

Upgrading Redis 7.0.11 to 8.10.0 on Alibaba Cloud OneinStack: Kernel Optimization and Rollback Protection

After upgrading components like Nginx, OpenSSL, and PHP, I continued checking Redis on this Alibaba Cloud ECS.

The server uses the OneinStack environment, and Redis was not installed via RPM but deployed in:

Plaintext
/usr/local/redis

The version running before the upgrade was:

Plaintext
Redis 7.0.11

My goal this time was not simply to replace a redis-server file, but to complete the pre-upgrade audit, data backup, kernel parameter optimization, side-track compilation, compatibility verification, production cutover, and rollback plan as thoroughly as possible, ultimately upgrading Redis to:

Plaintext
Redis 8.10.0
jemalloc 5.3.0

The entire production cutover was ultimately successful, and the WordPress Chinese site, English site, and admin site all run normally.


1. Redis Environment Before Upgrade

Before the upgrade, the main details of the Redis setup were as follows:

  • Redis version: 7.0.11
  • Running mode: standalone
  • Listening address: 127.0.0.1:6379
  • Configuration file: /usr/local/redis/etc/redis.conf
  • Data directory: /usr/local/redis/var
  • systemd service: redis-server.service
  • Service user: redis
  • Maximum memory: 1GB
  • Eviction policy: allkeys-lru
  • AOF: Not enabled
  • Scheduled RDB: Disabled
  • Loaded modules: None
  • PHP Redis extension: 6.3.0
  • WordPress cache plugin: W3 Total Cache

The server has a total memory of about 3.6GiB. Before the Redis upgrade, it was using about 880MiB of memory, and the number of cache keys was around 300,000.

Redis has a very high cache hit rate, and the vast majority of keys have expiration times set, indicating that it primarily handles WordPress object cache and page-related caches, rather than storing irreplaceable business data.

However, this does not mean Redis can simply be emptied during the upgrade.

Losing 300,000 cache keys all at once would put significant cache rebuilding pressure on WordPress, PHP-FPM, and the remote RDS in a short period. Therefore, I still decided to preserve the existing cache data as much as possible.


2. Two System Issues Discovered Before Upgrade

Before the formal upgrade, I first checked the server’s kernel parameters.

I found that:

Plaintext
vm.overcommit_memory = 0

The corresponding warning had also been persistently present in the Redis logs:

Plaintext
WARNING Memory overcommit must be enabled!

Redis needs to create a child process via fork() when performing an RDB background save. If memory overcommit is not enabled, the background save may fail due to the kernel’s allocation checks, even if the server has available memory.

Therefore, I adjusted it to:

Plaintext
vm.overcommit_memory = 1

And wrote the persistent configuration to:

Plaintext
/etc/sysctl.d/99-redis-overcommit.conf

The configuration content is as follows:

INI
# Redis background saving and fork reliability
vm.overcommit_memory = 1

Simultaneously, I executed:

Bash
sysctl -w vm.overcommit_memory=1

Transparent Huge Pages Still Enabled

The initial THP status was:

Plaintext
enabled: [always] madvise never
defrag: always defer defer+madvise [madvise] never

This indicates that Transparent Huge Pages were still enabled.

To prevent Redis from incurring additional latency when copying memory pages, performing RDB saves, or handling high-frequency access, I created a systemd service to automatically disable THP at system startup.

The final status became:

Plaintext
always madvise [never]
always defer defer+madvise madvise [never]

At this point, both important system parameters required by Redis had been adjusted:

Plaintext
vm.overcommit_memory = 1
Transparent Huge Pages = never

3. Generate a Latest RDB First

The Redis configuration before the upgrade included:

Plaintext
save ""
appendonly no

This means:

  • Scheduled RDB saves were disabled;
  • AOF was also not enabled.

The dump.rdb on disk at that time was still generated on June 5, 2026, and was severely outdated.

Therefore, before replacing any binary files, I manually executed:

Bash
/usr/local/redis/bin/redis-cli BGSAVE

The background save completed successfully:

Plaintext
Background saving started
RDB 后台保存成功
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:7

The new RDB file was approximately:

Plaintext
263M

During this background save, Redis’s Fork Copy-on-Write peak was only about 6MB, indicating that the save process put very little additional memory pressure on the server.

I subsequently backed up the following:

  • All Redis 7.0.11 binary files
  • redis.conf
  • Latest dump.rdb
  • systemd service file
  • Redis Server, Memory, Persistence, and Keyspace information
  • SHA-256 of all backup files

The first complete backup directory was:

Plaintext
/root/redis-upgrade-backup-20260805-185250

The total backup size was about 334MB.


4. Initial Plan to Compile Everything in Redis 8.10.0

The target version selected this time was:

Plaintext
Redis 8.10.0

After downloading the source package, the file information was as follows:

Plaintext
文件大小:21550621 字节
SHA-256:f1baa4b28befd417aa6577ebeedde9e9fc7814cfcc299b2a6d2fd99ef7420a6c

The source package extracted normally, and the top-level directory and version information were completely correct:

Plaintext
redis-8.10.0
#define REDIS_VERSION "8.10.0"

However, during the compilation process, I encountered an issue related to the source package structure of Redis 8.


5. Top-Level Make Simultaneously Compiles Multiple Additional Modules

I initially tried to build Redis using the top-level make.

Although the Redis core redis-server, redis-cli, and redis-benchmark were all successfully generated, the top-level build also continued to try to compile:

  • RedisBloom
  • RediSearch
  • RedisJSON
  • RedisTimeSeries

Subsequently, a Python dependency error occurred:

Plaintext
ModuleNotFoundError: No module named 'dataclasses'

RedisTimeSeries also showed the following during the linking phase:

Plaintext
undefined reference to `main'

Ultimately, the top-level make returned a failure:

Plaintext
WARNING: The following module(s) failed to build:
redisbloom redisearch redisjson redistimeseries

ERROR: make build finished with module failure(s)

However, the logs also clearly showed:

Plaintext
Build complete.
redis-server: /root/redis-8.10.0/src/redis-server

In other words, the failures were in the additional modules, not the Redis core.


6. Why I Did Not Continue Solving the Compilation Issues for the Four Modules

Redis on this server is primarily used by WordPress and W3 Total Cache.

Before the upgrade, I executed:

Plaintext
MODULE LIST

The result was empty.

WordPress currently uses Redis’s basic capabilities, such as:

  • GET
  • SET
  • DEL
  • EXPIRE
  • Hash
  • List
  • Set
  • Sorted Set

It does not rely on the following commands:

Plaintext
FT.SEARCH
JSON.SET
TS.ADD
BF.ADD

Therefore, RedisBloom, RediSearch, RedisJSON, and RedisTimeSeries are not essential components for the current website.

If I were to continue installing or upgrading Python, Rust, CMake, LLVM, and the various dependencies of these modules just to compile them, it would not improve WordPress’s cache performance. Instead, it would significantly increase the server’s maintenance complexity.

Ultimately, I decided to adopt:

Plaintext
Redis 8.10.0 核心
不安装 RedisBloom
不安装 RediSearch
不安装 RedisJSON
不安装 RedisTimeSeries

Here, “doing it right in one go” does not mean installing every feature that might temporarily be unused. It means completely upgrading the parts currently needed and ensuring stable maintainability going forward.


7. Compiling Only the Redis Core Targets

By checking src/Makefile, I could see that the Redis core provides specific targets:

Plaintext
redis-server
redis-cli
redis-benchmark
redis-check-rdb
redis-check-aof
redis-sentinel

Therefore, instead of running the top-level make, I directly built the required core programs in the src directory:

Bash
make -C src -j2 \
    MALLOC=jemalloc \
    DISABLE_WERRORS=yes \
    redis-server \
    redis-cli \
    redis-benchmark

This completed successfully:

Plaintext
CC release.o
LINK redis-server
LINK redis-cli
LINK redis-benchmark
明确的核心目标检查成功

The generated versions were:

Plaintext
Redis server v=8.10.0
redis-cli 8.10.0
malloc=jemalloc-5.3.0

Next, I created an independent side-track directory:

Plaintext
/root/redis-8.10.0-core-stage

The directory contains:

Plaintext
redis-server
redis-cli
redis-benchmark
redis-check-rdb -> redis-server
redis-check-aof -> redis-server
redis-sentinel -> redis-server

This allows for complete testing of the new version without modifying the production directory.


8. Checking the Legacy RDB with Redis 8.10.0

I used the newly compiled Redis 8.10.0 to check the RDB generated by Redis 7.0.11:

Bash
/root/redis-8.10.0-core-stage/bin/redis-check-rdb \
    /root/redis-upgrade-backup-20260805-185250/var/dump.rdb

The check results were:

Plaintext
Checksum OK
RDB looks OK
303294 keys read
303242 expires
4364 already expired

This indicates that:

  • The RDB file structure is normal;
  • The checksum is normal;
  • Redis 8.10.0 can read the data generated by Redis 7.0.11;
  • Some expired cache keys exist in the file, which is normal.

9. Testing Redis 8.10.0 on Port 16379 First

To further reduce risk, I did not directly replace the production service. Instead, I started a temporary Redis 8.10.0 instance using port 16379.

The temporary instance did not load the production RDB and did not consume a large amount of memory; it was solely used to verify:

  • Whether the original Redis configuration could be read by 8.10.0;
  • Whether the Redis Server could start normally;
  • Whether the Redis CLI could read and write;
  • Whether the PHP Redis extension could connect;
  • Whether there were compatibility issues with systemd and system dynamic libraries.

The startup results were:

Plaintext
PONG
redis_version:8.10.0
redis_mode:standalone
tcp_port:16379
mem_allocator:jemalloc-5.3.0

The Redis CLI read/write test was successful:

Plaintext
redis_cli_value=ok

The PHP Redis extension test was also successful:

Plaintext
php_redis_value=ok

After testing, the temporary instance stopped normally.

Throughout the verification period, the production Redis 7.0.11 kept running without interruption, and no binaries were replaced.


10. Why vectorset Appeared in MODULE LIST

After the temporary Redis 8.10.0 started, MODULE LIST was not completely empty; instead, the following appeared:

Plaintext
vectorset

This is not RedisBloom, RediSearch, RedisJSON, or RedisTimeSeries, which were previously dropped from compilation.

Vector Set is a capability built alongside the current Redis 8 core program, so the final solution is more accurately described as:

Plaintext
Redis 8.10.0 核心
包含 Vector Set
不包含 RedisBloom
不包含 RediSearch
不包含 RedisJSON
不包含 RedisTimeSeries

It does not affect the existing WordPress cache and requires no additional handling.


11. Generating an RDB Again Before the Formal Cutover

Before the production cutover, I executed BGSAVE once more.

This save took about 8 seconds:

Plaintext
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:8

Before the cutover, Redis contained:

Plaintext
302735 个键

I then created the second complete backup, which was the one used for the formal cutover:

Plaintext
/root/redis-cutover-backup-20260805-193515

Simultaneously, I generated an independent rollback script:

Plaintext
/root/redis-rollback-20260805-193515.sh

The rollback script can accomplish the following:

  • Stop the current Redis;
  • Restore the Redis 7.0.11 binaries;
  • Restore the old configuration;
  • Restore the pre-upgrade RDB;
  • Restore the systemd service;
  • Start Redis 7.0.11;
  • Check the version and PING.

Only after both the backup and rollback script were successfully created did the production cutover officially begin.


12. Formally Switching Redis 7.0.11 to 8.10.0

The production cutover steps were:

  1. Stop Redis 7.0.11;
  2. Copy the Redis 8.10.0 binaries from the side-track directory to /usr/local/redis/bin;
  3. Rebuild the redis-check-rdb, redis-check-aof, and redis-sentinel links;
  4. Start Redis 8.10.0 using the original redis.conf;
  5. Wait for the RDB load to complete;
  6. Verify the version, key count, memory, PHP, and WordPress.

After Redis 7.0.11 stopped normally, Redis 8.10.0 started successfully:

Plaintext
Redis 8.10.0 已开始响应
PONG
redis_version:8.10.0
redis_mode:standalone
tcp_port:6379

The logs showed that Redis 8.10.0 successfully read the RDB generated by Redis 7.0.11:

Plaintext
Loading RDB produced by version 7.0.11
RDB memory usage when created 881.41 Mb
Done loading RDB, keys loaded: 301533, keys expired: 1170
DB loaded from disk: 2.787 seconds
Ready to accept connections tcp

The actual downtime from stopping Redis 7 to Redis 8 accepting connections was very short.


13. Why the Key Count Decreased After the Upgrade

The key count before the cutover was:

Plaintext
302735

The first check after the cutover was:

Plaintext
301533

The difference between the two was about 1,200 keys.

This was not data loss caused by the upgrade.

The Redis logs clearly recorded:

Plaintext
keys expired: 1170

These keys had already expired during the RDB load, so they did not enter the new instance.

Additionally, cache keys in Redis continue to expire and regenerate. When I checked again later, the key count had already grown to:

Plaintext
301868

This shows that WordPress and W3 Total Cache are continuing to use Redis normally.


14. WordPress and PHP Verification Results

After Redis 8.10.0 started, I performed read/write tests via the Redis CLI and the PHP Redis extension, respectively.

The results were as follows:

Plaintext
redis_cli_value=ok
php_redis_value=ok

I then bypassed the CDN and checked the three sites directly via the local origin server:

Plaintext
www.shuijingwanwq.com/wp-json/        200
en.shuijingwanwq.com/wp-json/         200
admin.shuijingwanwq.com/wp-login.php  200

This indicates that:

  • The Chinese site is normal;
  • The English site is normal;
  • The admin site is normal;
  • The PHP Redis extension can connect to Redis 8.10.0 normally;
  • W3 Total Cache did not fail due to the Redis upgrade.

The automatic rollback was ultimately not triggered.


15. Memory and Cache Status After Upgrade

About 3 minutes after the upgrade was completed, the Redis memory status was:

Plaintext
used_memory_human:872.55M
used_memory_rss_human:837.40M
maxmemory_human:1.00G
maxmemory_policy:allkeys-lru
mem_fragmentation_ratio:0.96
mem_allocator:jemalloc-5.3.0

The statistics were:

Plaintext
total_commands_processed:21184
rejected_connections:0
evicted_keys:0
keyspace_hits:162630
keyspace_misses:769

The cache hit rate at that time was about 99.5%.

Although 872.55M had already reached about 85% of the 1GB limit, currently:

  • No keys have been evicted;
  • No connections have been rejected;
  • The hit rate is very high;
  • The server still has about 2.1GiB of available memory;
  • Redis RSS is also lower than used_memory.

Therefore, I did not immediately increase maxmemory for the time being.

Currently, I continue to retain:

Plaintext
maxmemory 1024mb
maxmemory-policy allkeys-lru

I will observe it for 24 to 72 hours.

Only if the following situations occur will I consider adjusting the limit to:

Plaintext
1280mb

The judgment conditions mainly include:

  • used_memory stays close to 1GB for a long time;
  • evicted_keys continues to grow;
  • keyspace_misses increases significantly;
  • The cache hit rate shows a continuous decline.

For this comprehensive WordPress server with only 3.6GiB of total memory, it is not appropriate to directly increase Redis to 1.5GB or higher; space must also be reserved for PHP-FPM, Nginx, system cache, and RDB background saves.


16. Whether Two Log Messages Need Handling

PID File Message

During the systemd startup process, the following appeared:

Plaintext
Can't open PID file /var/run/redis/redis.pid (yet?)

But immediately after, the service had already normally entered:

Plaintext
active (running)

The Redis main process was also correctly identified by systemd.

This is a brief timing message between systemd checking the PID file and Redis writing the PID file when the Type=forking service starts; it does not mean Redis failed to start.

For now, there is no need to modify the existing systemd service model just to eliminate this message.

Redis Password Not Set

The Redis 8.10.0 logs also contained:

Plaintext
WARNING: Redis does not require authentication.
Redis will accept connections from any local client.

Currently, Redis only listens on:

Plaintext
127.0.0.1:6379

And is configured with:

Plaintext
protected-mode yes

Redis is only used by PHP and WordPress on the same server.

If I add a password now, I would also need to synchronously update W3 Total Cache and all Redis client configurations. Therefore, this message is temporarily not an item to be handled after this upgrade.


17. Final Upgrade Results

The final status of this Redis upgrade was:

Plaintext
Redis 7.0.11 → Redis 8.10.0:成功
jemalloc 5.3.0:正常
RDB 数据迁移:成功
30 万级缓存键加载:成功
RDB 加载时间:2.787 秒
Redis CLI 读写:成功
PHP Redis 扩展兼容:成功
WordPress 中文站:正常
WordPress 英文站:正常
WordPress 管理站:正常
vm.overcommit_memory:已调整为 1
Transparent Huge Pages:已关闭
自动回滚:未触发

After the upgrade, the Redis service maintained:

Plaintext
active
enabled
PONG

18. Temporarily Retaining Upgrade Files

To facilitate observation and rollback, I temporarily retained the following files:

Plaintext
/root/redis-upgrade-backup-20260805-185250
/root/redis-cutover-backup-20260805-193515
/root/redis-rollback-20260805-193515.sh
/root/redis-8.10.0
/root/redis-8.10.0-core-stage
/root/redis-8.10.0.tar.gz

The most important among these are the formal cutover backup and the rollback script:

Plaintext
/root/redis-cutover-backup-20260805-193515
/root/redis-rollback-20260805-193515.sh

These files will be kept for at least 7 days.

After confirming that Redis 8.10.0 runs stably continuously, I will delete the duplicate source code, temporary test directory, and earlier backups.


Summary

During this upgrade process, the truly time-consuming part was not the data compatibility between Redis 7 and Redis 8, but the multiple extension modules included in the default build process of the Redis 8.10.0 source package.

Ultimately, rather than continuing to install a series of currently unused dependencies just to achieve a “fully successful compilation,” I chose to compile and deploy only the Redis core based on the actual needs of WordPress.

Through a side-track directory, RDB checks, temporary port testing, PHP Redis verification, a final RDB backup, and an automatic rollback script, the production cutover was controlled within a very small risk margin.

The final result also proves that this approach is more suitable for the current server:

  • The Redis core was upgraded;
  • WordPress cache data was preserved;
  • PHP and W3 Total Cache remained compatible;
  • Kernel parameters were optimized;
  • No unnecessary module dependencies were introduced;
  • The entire cutover process did not trigger a rollback.

For a server that only uses Redis as a WordPress object cache, stability, rollback capability, and ease of maintenance are more important than compiling all extension modules.

系列导航

关于作者 · 持续学习与技术实践

我是一名拥有 15 年以上开发经验的技术从业者,自 2013 年起持续运营个人技术博客,记录工作与个人项目中遇到的真实问题,以及解决问题过程中的探索、实践和思考。

除了技术写作,我也在持续开发和维护自己的独立项目,探索新技术和新工具在实际场景中的应用。我始终相信,没有不值得去解决的问题,也没有不值得去学习的技术。

如果这篇文章对你有所帮助,欢迎浏览博客中的其他文章,也欢迎交流技术经验与想法。

关于我  ·  GitHub  ·  邮件联系