当 WordPress 网站流量增长到一定程度后,数据库往往成为性能瓶颈。特别是在高并发场景下,大量的读请求(如页面浏览、搜索)和写请求(如评论、订单)竞争同一台数据库服务器的资源。数据库读写分离(主从复制)将读请求分发到多个从数据库(Slave),写请求只发给主数据库(Master),可以大幅提升数据库的吞吐能力。

读写分离的基本原理 是:设置一个主数据库负责处理所有写入操作(INSERT、UPDATE、DELETE),以及部分关键的读取操作;设置一个或多个从数据库,从主数据库同步数据,并处理所有非关键的读取操作(如页面展示、搜索、列表查询)。应用程序在执行数据库查询时,根据查询类型自动选择连接不同的数据库。

HyperDB 是 WordPress 官方推荐的高可用数据库扩展,它支持数据库分片(Sharding)和读写分离。HyperDB 的配置在 db-config.php 文件中:

// db-config.php
$wpdb->add_database(array(
    'host'     => 'master-db.example.com',
    'user'     => 'username',
    'password' => 'password',
    'name'     => 'wordpress',
    'write'    => 1,  // 可以写入
    'read'     => 1,  // 可以读取
));

// 添加一个只读的从数据库
$wpdb->add_database(array(
    'host'     => 'slave-db1.example.com',
    'user'     => 'username',
    'password' => 'password',
    'name'     => 'wordpress',
    'write'    => 0,  // 只读
    'read'     => 1,
    'dataset'  => 'global', // 数据集分组
));

LudicrousDB 是 HyperDB 的现代替代品,由 Human Made 维护,兼容性更好,性能也更优。它的配置方式与 HyperDB 类似,但提供了更细粒度的控制和更好的调试支持。

配置读写分离的关键参数

  • $wpdb->add_database() 中的 read 和 write 参数决定了数据库的角色

  • dataset 参数可以将查询分组,不同的数据集可以指定不同的数据库服务器

  • 通过 $wpdb->send_reads_to_master 可以在某些场景下强制使用主数据库读取

数据集分组(Dataset) 是读写分离的高级应用。你可以将不同类型的数据分配到不同的从数据库。例如,把文章数据放到一组,把用户数据放到另一组:

$wpdb->add_database(array(
    'host'     => 'slave-posts.example.com',
    'dataset'  => 'posts',
    'read'     => 1,
));
$wpdb->add_database(array(
    'host'     => 'slave-users.example.com',
    'dataset'  => 'users',
    'read'     => 1,
));

数据库分片 是读写分离的进阶形态,适用于数据量极大的网站。分片将数据水平拆分到多个数据库中,比如按用户 ID 取模、按日期分区、按站点 ID(多站点网络)分片。HyperDB 支持基于数据集的自动分片。

主从同步延迟 是读写分离中最常见的问题。当主数据库写入后,从数据库还没来得及同步,这时用户查询可能读到旧数据。解决方案包括:

  • 在关键操作后使用 $wpdb->send_reads_to_master = true,强制后续读请求去主库

  • 使用 wpdb 的 $wpdb->db_connect 方法手动选择数据库连接

  • 监控主从延迟,在延迟过大时自动将读请求切换回主库

  • 使用半同步复制(Semi-Synchronous Replication)减少延迟

监控与维护 读写分离集群需要额外的监控。你应该监控:

  • 主从复制延迟(SHOW SLAVE STATUS 中的 Seconds_Behind_Master

  • 各数据库服务器的负载(CPU、内存、连接数)

  • 慢查询日志,优化 SQL 语句

  • 数据一致性,定期校验主从数据是否一致

代码层面的优化 也很重要。在读写分离环境中,你应该避免在模板循环中使用 get_post_meta 等会触发额外查询的函数,而是使用 update_meta_cache 批量预加载 meta 数据,减少数据库查询次数。

// 预加载 meta 数据,避免循环中的 N+1 查询
$posts = get_posts($args);
$post_ids = wp_list_pluck($posts, 'ID');
update_meta_cache('post', $post_ids);

适用场景 读写分离适合读多写少、流量较高的内容型网站和电商网站。对于日 PV 在 10 万以下的网站,做好页面缓存和对象缓存可能比读写分离更有效。读写分离通常作为最后一道数据库优化手段,在缓存已经做到极致但数据库仍然成为瓶颈时采用。

部署读写分离需要一定的服务器运维能力,建议在熟悉 MySQL 主从复制的基础上逐步实施。对于托管在云平台(如 AWS RDS、阿里云 RDS)的网站,云服务商通常提供了开箱即用的读写分离功能,可以大幅降低运维成本。