humanmade/ludicrousdb

LudicrousDB是一个基于Automattic的HyperDB插件,支持复制、故障转移、负载均衡和分区。

安装: 132,310

依赖项: 2

推荐者: 0

安全: 0

星标: 8

关注者: 3

分支: 80

开放问题: 0

类型:wordpress-muplugin

5.0.6 2023-05-19 19:41 UTC

README

LudicrousDB是基于Automattic的HyperDB插件,是一个支持复制、故障转移、负载均衡和分区的WordPress高级数据库接口。

安装

文件

将主插件文件夹ludicrousdb及其内容复制到以下位置之一:

  • wp-content/plugins/ludicrousdb/
  • wp-content/mu-plugins/ludicrousdb/

这两个都可以;LudicrousDB会自动识别。文件夹名称必须是ludicrousdb。从GitHub下载ZIP文件并解压时请小心。

插件插件

WordPress支持一些“插件插件”风格的插件,用于对一些特定功能的扩展覆盖。

LudicrousDB包括3个基本的数据库插件插件

  • db.php <-> wp-content/db.php - 替换 $wpdb 对象的引导
  • db-error.php <-> wp-content/db-error.php - 致命数据库错误输出的端点
  • db-config.php <-> ABSPATH/db-config.php - 用于配置数据库环境

您可能需要将这些文件复制到相应的位置,并在熟悉它们的功能和工作原理后进行修改。

配置

LudicrousDB可以管理连接到大量数据库的连接。通过将表名映射到数据集来将查询分布到适当的服务器。

数据集是指位于同一数据库中的表的集合。可能存在具有不同表的不同服务器的类似命名的数据库。还可能在不同的服务器上有数据库的多个副本。术语“数据集”消除了任何歧义。将数据集视为可以镜像在多个服务器上的表的集合。

配置LudicrousDB涉及定义数据库和数据集。定义数据库涉及指定服务器连接详情、它包含的数据集以及其读写能力和优先级。定义数据集涉及指定其精确的表名或注册一个或多个将表名转换为数据集的回调函数。

示例配置1:默认服务器

这是使用仅必要的参数(主机、用户、密码、名称)将服务器添加到LudicrousDB的最基本方法。这将为默认的“全局”数据集添加wp-config.php中定义的DB作为读写服务器。(默认情况下,每个表都在“全局”中。)

$wpdb->add_database( array(
	'host'     => DB_HOST,     // If port is other than 3306, use host:port.
	'user'     => DB_USER,
	'password' => DB_PASSWORD,
	'name'     => DB_NAME,
) );

再次添加相同的服务器,但这次将其配置为从属服务器。最后三个参数设置为默认值,但显示出来以便清晰。

$wpdb->add_database( array(
	'host'     => DB_HOST,     // If port is other than 3306, use host:port.
	'user'     => DB_USER,
	'password' => DB_PASSWORD,
	'name'     => DB_NAME,
	'write'    => 0,
	'read'     => 1,
	'dataset'  => 'global',
	'timeout'  => 0.2,
) );

示例配置2:分区

此示例显示了一个设置,其中已将多站点博客表从全局数据集中分离出来。

$wpdb->add_database( array(
	'host'     => 'global.db.example.com',
	'user'     => 'globaluser',
	'password' => 'globalpassword',
	'name'     => 'globaldb',
) );

$wpdb->add_database( array(
	'host'     => 'blog.db.example.com',
	'user'     => 'bloguser',
	'password' => 'blogpassword',
	'name'     => 'blogdb',
	'dataset'  => 'blog',
) );

$wpdb->add_callback( 'my_db_callback' );

// Multisite blog tables are "{$base_prefix}{$blog_id}_*"
function my_db_callback( $query, $wpdb ) {
	if ( preg_match("/^{$wpdb->base_prefix}\d+_/i", $wpdb->table) ) {
		return 'blog';
	}
}

配置函数

add_database()

$wpdb->add_database( $database );

$database是一个包含这些参数的关联数组

host          (required) Hostname with optional :port. Default port is 3306.
user          (required) MySQL user name.
password      (required) MySQL user password.
name          (required) MySQL database name.
read          (optional) Whether server is readable. Default is 1 (readable).
                         Also used to assign preference. See "Network topology".
write         (optional) Whether server is writable. Default is 1 (writable).
                         Also used to assign preference in multi-master mode.
dataset       (optional) Name of dataset. Default is 'global'.
timeout       (optional) Seconds to wait for TCP responsiveness. Default is 0.2
lag_threshold (optional) The minimum lag on a slave in seconds before we consider it lagged.
                         Set null to disable. When not set, the value of $wpdb->default_lag_threshold is used.

add_table()

$wpdb->add_table( $dataset, $table );

$dataset$table是字符串。

add_callback()

$wpdb->add_callback( $callback, $callback_group = 'dataset' );

$callback是一个可调用的函数或方法。$callback_group是此$callback所属的回调组。

回调将按它们注册的顺序执行,直到其中一个返回非null值。

默认的 $callback_group 是 'dataset'。该组中的回调函数将被调用并传入两个参数,预期计算数据集或返回 null。

$dataset = $callback($table, &$wpdb);

任何评估结果为 false 的内容都将导致查询被中止。

对于更复杂的配置,回调函数可以用来重写 $wpdb 的属性或 LudicrousDB::connect_db() 中的变量。如果回调函数返回一个数组,LudicrousDB 将提取该数组。它应该是一个关联数组,并且应该包含一个与使用 $wpdb->add_database() 添加的数据库相对应的 $dataset 值。它还可以包含 $server,这将被提取以覆盖每个随机选择的数据库服务器的参数。这允许您动态更改参数,如主机、用户、密码、数据库名、延迟阈值和 TCP 检查超时。

主从数据库

数据库定义可以包括 'read' 和 'write' 参数。它们作为布尔开关运行,但通常指定为整数。它们允许或禁止使用数据库进行读取或写入。

主数据库可能配置为允许读取和写入

'write' => 1,
'read'  => 1,

而奴隶数据库仅允许读取

'write' => 0,
'read'  => 1,

可能存在不让主数据库读取的情况,例如,当有多个奴隶可用且主数据库非常忙于写入时。

  'write' => 1,
  'read'  => 0,

LudicrousDB 跟踪自实例化以来所写入的表,并将后续的读取查询发送到接收写入查询的同服务器。因此,这样设置的主数据库仍然会接收读取查询,但仅在写入之后。

网络拓扑/数据中心感知

当您的数据库位于不同的物理位置时,通常连接到附近的服务器而不是更远的服务器是有利的。读取和写入参数可以用来将服务器放置在更或更少的偏好连接的逻辑组中。较低的数字表示更高的偏好。

此配置指示 LudicrousDB 尝试从本地奴隶之一随机读取。如果该奴隶不可达或拒绝连接,将尝试其他奴隶,然后是主数据库,最后是远程奴隶,顺序随机。

Local slave 1:   'write' => 0, 'read' => 1,
Local slave 2:   'write' => 0, 'read' => 1,
Local master:    'write' => 1, 'read' => 2,
Remote slave 1:  'write' => 0, 'read' => 3,
Remote slave 2:  'write' => 0, 'read' => 3,

在另一个数据中心,主数据库将是远程的。我们将在决定将读取发送到何处时考虑这一点。写入始终发送到主数据库,不管距离如何。

Local slave 1:   'write' => 0, 'read' => 1,
Local slave 2:   'write' => 0, 'read' => 1,
Remote slave 1:  'write' => 0, 'read' => 2,
Remote slave 2:  'write' => 0, 'read' => 2,
Remote master:   'write' => 1, 'read' => 3,

有多种方法在不同的位置实现不同的配置。您可以部署不同的配置文件。您可以编写代码来发现 Web 服务器的位置,例如通过检查 $_SERVERphp_uname(),并根据这些参数计算读取/写入参数。

奴隶延迟感知

LudicrousDB 通过基于定义的延迟阈值做出决策来适应奴隶延迟。如果没有设置延迟阈值,它将忽略奴隶延迟。否则,它将尝试找到一个非延迟的奴隶,然后再连接到一个延迟的奴隶。

如果一个奴隶的复制延迟大于在 $wpdb->default_lag_threshold 或每个数据库设置中定义的延迟阈值,则认为该奴隶延迟。您还可以通过在 'dataset' 组回调中返回 $server['lag_threshold'] 变量来重写延迟阈值。

LudicrousDB 不检查奴隶的延迟。您必须定义两个回调来实现这一点

$wpdb->add_callback( $callback, 'get_lag_cache' );

$wpdb->add_callback( $callback, 'get_lag' );

第一个在连接到奴隶之前被调用,应该根据 $wpdb->lag_cache_key 返回秒数或 false(如果未知)。

第二个回调在建立与奴隶的连接后调用。它应该返回它的复制延迟或 false(如果未知),基于 $wpdb->dbhs[ $wpdb->dbhname ] 中的连接。

示例复制延迟检测配置。

要检测复制延迟,请尝试 mk-heartbeat: (http://www.maatkit.org/doc/mk-heartbeat.html)

此实现需要数据库用户具有读取心跳表权限。

缓存使用共享内存以提高可移植性。可以修改以与Memcached、APC等兼容。

$wpdb->lag_cache_ttl = 30;
$wpdb->shmem_key = ftok( __FILE__, "Y" );
$wpdb->shmem_size = 128 * 1024;

$wpdb->add_callback( 'get_lag_cache', 'get_lag_cache' );
$wpdb->add_callback( 'get_lag',       'get_lag' );

function get_lag_cache( $wpdb ) {
	$segment = shm_attach( $wpdb->shmem_key, $wpdb->shmem_size, 0600 );
	$lag_data = @shm_get_var( $segment, 0 );
	shm_detach( $segment );

	if ( !is_array( $lag_data ) || !is_array( $lag_data[ $wpdb->lag_cache_key ] ) )
		return false;

	if ( $wpdb->lag_cache_ttl < time() - $lag_data[ $wpdb->lag_cache_key ][ 'timestamp' ] )
		return false;

	return $lag_data[ $wpdb->lag_cache_key ][ 'lag' ];
}

function get_lag( $wpdb ) {
	$dbh = $wpdb->dbhs[ $wpdb->dbhname ];

	if ( !mysql_select_db( 'heartbeat', $dbh ) )
		return false;

	$result = mysql_query( "SELECT UNIX_TIMESTAMP() - UNIX_TIMESTAMP(ts) AS lag FROM heartbeat LIMIT 1", $dbh );

	if ( !$result || false === $row = mysql_fetch_assoc( $result ) )
		return false;

	// Cache the result in shared memory with timestamp
	$sem_id = sem_get( $wpdb->shmem_key, 1, 0600, 1 ) ;
	sem_acquire( $sem_id );
	$segment = shm_attach( $wpdb->shmem_key, $wpdb->shmem_size, 0600 );
	$lag_data = @shm_get_var( $segment, 0 );

	if ( !is_array( $lag_data ) )
		$lag_data = array();

	$lag_data[ $wpdb->lag_cache_key ] = array( 'timestamp' => time(), 'lag' => $row[ 'lag' ] );
	shm_put_var( $segment, 0, $lag_data );
	shm_detach( $segment );
	sem_release( $sem_id );

	return $row[ 'lag' ];
}