Mysqlnd_ms的安装配置与使用

公司最近需要一个扩展性良好的Mysql主从分离方案,老大要求对业务层的代码改动要小。
业务层代码改动小,意味着要在连接层直接把读/写请求分发,而不是到业务层通过”SQL识别”的来分离。
脑子里有两套方案, Mysqlnd_ms 和 Mysql-Proxy。两个方案各有优/缺点

Mysqlnd_ms

优点:
1. PHP官方推荐的扩展,PECL安装方便

  1. 基于PHP扩展对SQL进行解析,进而分离请求,对业务层代码改动小,性能损失也小
  2. 扩展本身已经在社区打滚有些年头了,较稳定,口碑好
  3. 入门级文档清晰,易配置易上手
  4. 有连接池
  5. 功能强大,支持SQL Hint, 事务,最终/Session/强一致性等场景

缺点:
1. 基于扩展的封装,扩展的实现对上层透明。扩展本身缺乏debug工具, 所以真遇到问题可能比较难查

  1. Failover 策略较基础
  2. 缺乏中文文档
  3. 强一致性的场景会有些坑

Mysql-Proxy

优点:
1. 来自Mysql官方的解决方案

  1. 性能好,资源占用少
  2. 有连接池
  3. 配置简单/文档众多
  4. 应有广泛,口碑好

缺点:
1. 需要第三方脚本(Lua)支持

鉴于”对业务层代码改动要小”这个需求,以及笔者对Mysql-Proxy 使用较少,所以在简单比较之后,果断选择了Mysqlnd_ms。

安装

1. wget http://pecl.php.net/get/mysqlnd_ms-1.5.2.tgz 
2. tar xzvf mysqlnd_ms-1.5.2.tgz
3. cd mysqlnd_ms-1.5.2
4. /path/to/phpize
5. ./configure --enable-mysqlnd-ms --with-php-config=/usr/local/php/bin/php-config
6. make
7. make install
8. sudo /etc/init.d/php-fpm restart
9. /path/to/php -m | grep mysql #看到"mysqlnd_ms"扩展表示安装成功

配置

1. 创建配置文件 /usr/local/etc/php/php.d/mysqlnd_ms.ini

extension="/usr/local/php-5.5.9/lib/php/extensions/no-debug-non-zts-20121212/mysqlnd_ms.so" 
; 启动mysqlnd_ms
mysqlnd_ms.enable=1
; 加载的主从配置文件
mysqlnd_ms.config_file="/usr/local/etc/php/php.d/mysqlnd_ms.conf" 
; 是否禁用读写分离
mysqlnd_ms.disable_rw_split=0
; mysqlnd 日志输出格式(线上环境可选)
mysqlnd.debug="d:t:x:A,/tmp/mysqlnd.trace" 
;SERVER_QUERY_NO_GOOD_INDEX_USED=16
;SERVER_QUERY_NO_INDEX_USED=32
;SERVER_QUERY_WAS_SLOW=1024
; 日志掩码
mysqlnd.log_mask=1072

; 网络读缓存大小
; 64K
mysqlnd.net_read_buffer_size=65536
; 网络读等待时间
mysqlnd.net_read_timeout=600

2. 创建主从策略文件 /usr/local/etc/php/php.d/mysqlnd_ms.conf (位置由mysqlnd_ms.ini指定)

Demo 主从写成了同一个库

{
    "master-slave": { // section 对应一组服务
        "master": { // 主库配置(默认主库只包含一项)
            "master_0": {
                "host": "192.168.100.100",//host必选 其他可为空。 扩展优先使用本文件中的配置,如果为空,再去使用php连接mysql时使用的参数
                "port": "3306",
                "socket": "\/var\/data\/mysql\/mysql.sock",
                "db": "master-slave",
                "user": "root",
                "password": "",
                "connect_flags": 0
            }
        },
        "slave": { // 从库配置(可配置多项,slave为空会在php处报warning)
            "slave_0": {
                "host": "192.168.100.101",
                "port": "3306",
                "socket": "\/var\/data\/mysql\/mysql.sock",
                "db": "master-slave",
                "user": "root",
                "password": "",
                "connect_flags": 0                                                                   
            },
            "slave_1": {
                "host": "192.168.100.102",
                "port": "3306",
                "socket": "\/var\/data\/mysql\/mysql.sock",
                "db": "master-slave",
                "user": "root",
                "password": "",
                "connect_flags": 0                        
            },
        },   
        "lazy_connections": 1, //只在执行sql之前才连接数据库
        "server_charset" : "utf8" //服务端编码
    }   
}

/etc/init.d/php-fpm restart #重启fpm生效

3. 一些基本的参数说明

参数名称 描述
master 主库配置
host ip or hostname
port 端口
socket 连接套接字
db 库名字
user 用户名
password 用户密码
connect_flags 连接参数
slave 从库配置
global_transaction_id_injection GTID的配置 详见 http://cn2.php.net/manual/zh/mysqlnd-ms.plugin-ini-json.php
filter 选择从库的策略 random/roundrobin/user/user_multi/node_groups 策略详情详见http://cn2.php.net/manual/zh/mysqlnd-ms.plugin-ini-json.php
failover 故障转移 disabled (默认), master(读从失败就连主库),loop_before_master(读从失败轮寻其他从库再读主库)
lazy_connections 被动连接
server_charset 服务端字符集
trx_stickiness 事务控制策略

使用

  1. 通常情况下,使用mysqlnd_ms 不需要改动业务端的代码,只需要在连接数据库时,使用配置中的host,即可实现主从分离,如下:
    /* Master */
    
    foreach( $dbh -> query ( 'show tables' ) as  $row )
    
    {
    
        print_r ( $row );
    
    }

    /* Slave */ foreach( $dbh -> query ( ‘SELECT * from test LIMIT 1’ ) as $row ) { print_r ( $row ); }

    $dbh = null ; } catch ( PDOException $e ) { print “Error!: ” . $e -> getMessage () . ”
    ” ; die(); } catch (Exception $e) { var_dump($e); }

    1. 采取主从分离,就一定存在着同步延迟的问题。 在一个繁忙的服务中,主/从同步会存在延迟。mysqlnd_ms 将这种场景细分为:
    • 最终一致性:用户可能无法立即看到自己写入的数据
    • session一致性:用户自己可以立即看到自己写入的数据
    • 强一致性:所有的用户都可以立即看到其他用户的写入的数据

    mysqlnd_ms 针对不同的场景,提供不同的策略。如果对延迟比较敏感(如:用户提交表单后,页面立即跳转展示用户提交的数据),在编码的过程中还需要参考下述文档

    1. SQL Hints: http://cn2.php.net/manual/zh/mysqlnd-ms.quickstart.sqlhints.php
    2. 事务: http://cn2.php.net/manual/zh/mysqlnd-ms.quickstart.transactions.php
    3. 服务级别和一致性: http://cn2.php.net/manual/zh/mysqlnd-ms.quickstart.qos-consistency.php
    4. Global transaction IDs (GTID):http://cn2.php.net/manual/zh/mysqlnd-ms.quickstart.gtid.php
    5. Cache integration: http://cn2.php.net/manual/zh/mysqlnd-ms.quickstart.cache.php

    一些陷阱

    1. Mysqlnd_ms 的行为是这样的。 所有的Select 操作会访问从库, 除Select 其他的所有操作都访问主库,包括(SHOW, SET等)。
      所以习惯使用mysql变量来编程的同学要注意了,如:

      set @id=2;(主库)
      select * from table where id:=@id(从库); 
      

      上面的第二条语句会因为在从库找不到id这个变量而报错, 这种情况要SQL Hint 给Mysql一些提示

    2. Mysqlnd_ms 并不支持 mysqli 的multi_statements。 如使用mysqli_multi_query() 来批量执行SQL
      $sql = "SELECT * FROM {$table} WHERE ..";
      $sql .= "INSERT INTO {$table}";
      $mysqli -> multi_query ($sql);
      

      上面的第一条SQL会被识别为访问从库,Mysqlnd_ms 无法拆分一个批量操作,所以第二条Insert 也会进从库而造成数据不一致。

    3. 事务的默认行为也和上述第二点一样,无法拆分一个事务,所以”读/写”请求尽量不要写进同一个事务中。如果一定要写,要使用SQL Hint 给Mysqlnd_ms 一些提示。
Posted in : php


发表评论

电子邮件地址不会被公开。 必填项已用*标注