corn/mypdoms

基于PDO构建的纯PHP mysqlnd_ms克隆实现

v1.1.1 2018-08-30 20:47 UTC

This package is auto-updated.

Last update: 2024-08-28 12:02:46 UTC


README

MyPDOMS旨在成为mysqlnd_ms在大多数常见任务中使用的替代品。

需要PHP 5.6或更高版本。

虽然这是基于PDO构建的,但它只支持MySQL。

目录

配置

在实例化MyPDOMS之前,您需要对其进行配置。配置是通过使用静态方法setConfig完成的,该方法期望一个类型为数组的单个参数。期望的关联数组结构为:

  • [config name] - 配置的名称。配置是一组服务器的集合。
    • master - 主服务器的数据库配置
      • host - 此数据库运行的主机(必需)
      • port - 此数据库运行上的端口(可选;默认为3306)
      • username - 数据库用户名(可选;如果缺失,则回退到构造函数参数)
      • password - 数据库密码(可选;如果缺失,则回退到构造函数参数)
    • slaves - 包含您的从服务器数据库配置
      • [slave name] - 从服务器的数据库配置(不能是master
        • host - 此数据库运行的主机(必需)
        • port - 此数据库运行上的端口(可选;默认为3306)
        • username - 数据库用户名(可选;如果缺失,则回退到构造函数参数)
        • password - 数据库密码(可选;如果缺失,则回退到构造函数参数)

例如,您可能想这样做:

<?php
use Corn\MyPDOMS\MyPDOMS;

MyPDOMS::setConfig([
	'my_site_1' => [
		'master' => [
			'host' => '127.0.0.1',
			'port' => 3306,
			'username' => 'my_site_user',
			'password' => 'apples'
		],
		'slaves' => [
			'slave_1' => [
				'host' => '10.0.1.1',
				'username' => 'slave_user',
				'password' => 'readonly'
			],
			'slave_2' => [
				'host' => '10.0.1.2',
				'username' => 'slave_user',
				'password' => 'readonly'
			]
		]
	],
	'my_site_2' => [
		'master' => [
			'host' => 'localhost',
			'username' => 'my_site_2_user',
			'password' => 'oranges'
		]
	]
]);

您不需要提供任何从服务器配置。如果您没有配置任何从服务器,则所有查询都将发送到主服务器。

建立连接

从服务器连接是延迟建立的,但主连接是在构造新的MyPDOMS实例时建立的。构造函数与标准PDO构造函数相同,但有这些注意事项:

  • DSN中的host应该是您配置中的一个名称(在上面的示例中,为my_site_1my_site_2
  • 如果提供了DSN中的port,则忽略
  • 如果您在构造函数和配置(setConfig)中提供了数据库凭据($username和$passwd),则构造函数优先
    • 建议您在setConfig中提供您的凭据,以防止任何可能的凭据泄露,例如在堆栈跟踪中
  • 您提供的任何连接选项($options)都将用于建立到主服务器和所有从服务器连接的连接

以下是一个示例:

<?php
use Corn\MyPDOMS\MyPDOMS;

$db = new MyPDOMS('mysql:host=my_site_1;dbname=my_database;charset=utf8mb4', null, null, [MyPDOMS::ATTR_TIMEOUT => 5]);

与PDO的不同之处

MyPDOMSPDO的子类,因此PDO的文档也适用于MyPDOMS,但有以下核心区别:

  • 建立连接部分中所述的上述区别
  • lastUsedHost属性包含已发送查询的最后主机的名称(例如masterslave_1
  • 以下方法将始终发送到主连接
  • 这些方法将被发送到由 lastUsedHost 命名的连接。
  • getAvailableDrivers 总是返回 ['mysql']
  • 调用 setAttribute 将导致以下事件序列
    • 您传入的属性和值将存储在 MyPDOMS 对象的内部
    • 属性将设置在主连接上
    • 属性将设置在任何已建立的从连接上
    • 当建立新的从连接时,所有之前设置的属性将立即设置在它上面
    • 如果所有连接在调用 setAttribute 时都返回 true,则返回 true
  • 调用 getAttribute 将返回内部缓存中的值,而不是来自 PDO 连接对象
  • preparequeryexec 将根据 查询路由 中注明的标准路由到连接
  • 您可以将属性 alwaysUseMaster 设置为 true 以始终使用主连接,尽管查询通常会路由到其他地方

查询路由

查询将根据以下检查序列路由到主服务器或从服务器

  1. SQL 语句中的前导注释将检查 SQL 提示
    1. 如果找到 HINT_MASTER,则将查询发送到主服务器
    2. 如果找到 HINT_SLAVE,则将查询发送到从服务器
    3. 如果找到 HINT_LAST_USED,则将查询发送到最后使用的连接
  2. 如果第一个 SQL 词是 INSERTUPDATEDELETEREPLACELOADALTERCREATEDROPRENAMETRUNCATE 之一,则将查询发送到主服务器
  3. 如果第一个 SQL 词是 SELECT 并且查询以 FOR UPDATE 结尾,则将查询发送到主服务器
  4. 如果上述任何一项都不匹配,则将查询发送到从服务器

注意:路由逻辑不会检查是否有打开的事务,因为所有可能导致更新或锁定的查询都已经自动路由到主服务器。

SQL提示

可以使用 SQL 提示来覆盖默认的查询路由逻辑。以下 SQL 提示可用,并应在注释中添加到查询之前

  • MyPDOMS::HINT_MASTER - 将此查询发送到主服务器
  • MyPDOMS::HINT_SLAVE - 将此查询发送到从服务器
  • MyPDOMS::HINT_LAST_USED - 将此查询发送到最后使用的服务器
    • 这可能是在最后使用的是主服务器时的情况
    • 如果最后使用的服务器是从服务器,则将查询发送到该从服务器

示例

<?php
use Corn\MyPDOMS\MyPDOMS;

$db = new MyPDOMS($dsn);
$db->query("/*" . MyPDOMS::HINT_MASTER . "*/SELECT 1"); // will be sent to the master even though it's a SELECT

如果您想替换 mysqlnd_ms 而不必回过头来更新所有代码,您可以使用此代码片段

<?php
use Corn\MyPDOMS\MyPDOMS;

if (!defined('MYSQLND_MS_MASTER_SWITCH')) {
	define('MYSQLND_MS_MASTER_SWITCH', MyPDOMS::HINT_MASTER);
	define('MYSQLND_MS_SLAVE_SWITCH', MyPDOMS::HINT_SLAVE);
	define('MYSQLND_MS_LAST_USED_SWITCH', MyPDOMS::HINT_LAST_USED);
}

从服务器选择

当查询需要被路由到从服务器时,从服务器是 按查询 选择的。也就是说,从服务器不是按请求选择,而是每次执行查询时都选择。目前,唯一支持的选取机制是无权重的随机选取,其中每个查询都会发送到一个随机从服务器,所有服务器被选中的概率是相等的。

示例

<?php
use Corn\MyPDOMS\MyPDOMS;

// Assume configured slaves are slave_{1-5}

$db = new MyPDOMS($dsn);
$db->query("SELECT 1");
echo $db->lastUsedHost . "\n"; // slave_4
$db->query("SELECT 1");
echo $db->lastUsedHost . "\n"; // slave_3
$db->query("SELECT 1");
echo $db->lastUsedHost . "\n"; // slave_4
$db->query("SELECT 1");
echo $db->lastUsedHost . "\n"; // slave_2
$db->query("SELECT 1");
echo $db->lastUsedHost . "\n"; // slave_2
$db->query("SELECT 1");
echo $db->lastUsedHost . "\n"; // slave_2

预计将在后续版本中添加不同的选择算法,但如果您希望定义自己的选择算法,可以扩展 MyPDOMS 并重写 getSlave 方法。

预编译语句

既支持模拟的又支持非模拟的预处理语句,因为它们在准备时分配了一个连接。也就是说,从 prepare() 返回 PDOStatement 后,该语句每次执行时都将始终使用相同的数据库。

注意:lastUsedHost 在语句 准备 时更新,但在 执行 时不更新。这意味着以下情况是可能的

<?php
use Corn\MyPDOMS\MyPDOMS;

$db = new MyPDOMS($dsn);
$stmt = $db->prepare("SELECT 1");
echo $db->lastUsedHost . "\n"; // slave_1

$db->query("SELECT 1");
echo $db->lastUsedHost . "\n"; // slave_2

$stmt->execute(); // this is executed on slave_1 since it was prepared on slave_1
echo $db->lastUsedHost . "\n"; // slave_2

$db->query("/*" . MyPDOMS::HINT_LAST_USED . "*/ SELECT 1");
echo $db->lastUsedHost . "\n"; // slave_2