corn / mypdoms
基于PDO构建的纯PHP mysqlnd_ms克隆实现
Requires
- ext-pdo: *
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_1
或my_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的不同之处
MyPDOMS
是PDO
的子类,因此PDO的文档也适用于MyPDOMS
,但有以下核心区别:
- 如建立连接部分中所述的上述区别
lastUsedHost
属性包含已发送查询的最后主机的名称(例如master
或slave_1
)- 以下方法将始终发送到主连接
beginTransaction
commit
rollBack
inTransaction
lastInsertId
quote
- 尽管quote不会导致任何网络I/O,但它始终在主连接上调用
- 这些方法将被发送到由
lastUsedHost
命名的连接。 getAvailableDrivers
总是返回['mysql']
- 调用
setAttribute
将导致以下事件序列- 您传入的属性和值将存储在
MyPDOMS
对象的内部 - 属性将设置在主连接上
- 属性将设置在任何已建立的从连接上
- 当建立新的从连接时,所有之前设置的属性将立即设置在它上面
- 如果所有连接在调用
setAttribute
时都返回true
,则返回true
- 您传入的属性和值将存储在
- 调用
getAttribute
将返回内部缓存中的值,而不是来自PDO
连接对象 prepare
、query
和exec
将根据 查询路由 中注明的标准路由到连接- 您可以将属性
alwaysUseMaster
设置为 true 以始终使用主连接,尽管查询通常会路由到其他地方
查询路由
查询将根据以下检查序列路由到主服务器或从服务器
- SQL 语句中的前导注释将检查 SQL 提示
- 如果找到
HINT_MASTER
,则将查询发送到主服务器 - 如果找到
HINT_SLAVE
,则将查询发送到从服务器 - 如果找到
HINT_LAST_USED
,则将查询发送到最后使用的连接
- 如果找到
- 如果第一个 SQL 词是
INSERT
、UPDATE
、DELETE
、REPLACE
、LOAD
、ALTER
、CREATE
、DROP
、RENAME
或TRUNCATE
之一,则将查询发送到主服务器 - 如果第一个 SQL 词是
SELECT
并且查询以FOR UPDATE
结尾,则将查询发送到主服务器 - 如果上述任何一项都不匹配,则将查询发送到从服务器
注意:路由逻辑不会检查是否有打开的事务,因为所有可能导致更新或锁定的查询都已经自动路由到主服务器。
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