Maxscale-正确对待读写分离(2)

HH MySQL1219,8944字数 5493阅读18分18秒阅读模式

前言

在现在读写分离已经是不奇怪了, 基本上有接触一点MySQL的都会谈到要读写分离。下面我们以3个方面来探讨一些并且介绍如何使用Maxscale来做适合业务的读写分离:

  1. 读写分离要怎么做呢?
  2. 在一个项目当中应该什么时候接入读写分离呢?
  3. 如何正确的使用读写分离呢?

读写分离要怎么做

其实读写分离最稳定的做法就是直接嵌入到程序中,通过业务程序来直接做判断哪些SQL需要访问哪个库。但是往往对于一个开放团队来说,在一个已有的项目中应入读写分离,其实工作量还是有的。特别是在一个公司如果没有一个很好的程序设计的人员,做读写分离将会是无趣找代码、cpoy代码并且让代码变的维护性变的更差。文章源自运维生存时间-https://www.ttlsa.com/mysql/maxscale-right-read-write-split/

插曲:如果有一个比较强的程序设计人员可以将重构读写分离的程序达到一两行代码搞定(这边使用程序编程的装饰器和工厂模式配合将会比较合适,当然肯定有更合适的方法)。文章源自运维生存时间-https://www.ttlsa.com/mysql/maxscale-right-read-write-split/

当然现在能做的读写分离的中间件很多,Maxscale就能够胜任这样的工作,Maxscale的Hint解析分发SQL能见读写分离做的更加灵活。最主要的是使用Maxscale的Hint最代码的改造将会减少很多。文章源自运维生存时间-https://www.ttlsa.com/mysql/maxscale-right-read-write-split/

注意:这边有的人会有疑问Maxscale天生不就是做读写分离么,为啥还要用到Hint,别急下面会说道'正确的使用读写分离'(可能你的业务默认使用Maxscale读写分离就能满足了)。文章源自运维生存时间-https://www.ttlsa.com/mysql/maxscale-right-read-write-split/

做读写分离其实在前期和开发沟通是主要一方面,要不断引导他们如何去做读写分离。毕竟一个公司里面不都是经验丰富的开发0-2年工作经验的偏多。文章源自运维生存时间-https://www.ttlsa.com/mysql/maxscale-right-read-write-split/

什么时候接入读写分离

在一个新项目开始的时候基本上是以功能为核心,以功能多并且还比较好来抢占市场,来积累一定的用户,是不稳定的。因为他们在一点点的快速摸索迭代中,过早的引入读写分离,会在一定程度上拉长项目的进度(当然这个阶段如果有DBA最基本的规范优化还是要有的)。文章源自运维生存时间-https://www.ttlsa.com/mysql/maxscale-right-read-write-split/

如果有项目需要重构 或 一般当项目上线一段时间了发现了发现了一般业务的SQL不能满足业务的发展需要一些花哨的功能来响应市场,但是这些功能编写出来的SQL会一定程度上会影响到数据库的运行。这时候就应该规划读写分离的使用。文章源自运维生存时间-https://www.ttlsa.com/mysql/maxscale-right-read-write-split/

当然如果对项目的的未来有个比较好的预测,那从项目的开始就使用读写分离降低之后接入读写分离的成本这是一个正确的做法。比如现在阿里、移动、电信又要搞一个产品那我觉得可以把读写分离提前就搞上,毕竟他们有用户基础,只要产品出来稍微推广一下压力就上来了。文章源自运维生存时间-https://www.ttlsa.com/mysql/maxscale-right-read-write-split/

正确的使用读写分离

要正确的使用读写分离,那就必须和实际情况和业务相结合了。文章源自运维生存时间-https://www.ttlsa.com/mysql/maxscale-right-read-write-split/

这边先说使用普通读写分离的一个现象:直接通过中间件使用读写分离,让读都走 slave。在没啥压力的时候主从不延时的时候这样能很好的服务。但是当压力以上来主从有延时了那么就有问题了。比如:创建一个商品创建的时候写的是Master,可是创建成功了一般还要从新查询一下数据库数据,并转跳到编辑页面。但是这时候主从有延时的时候就读取不到数据了。文章源自运维生存时间-https://www.ttlsa.com/mysql/maxscale-right-read-write-split/

所以一般在查询列表的时候可以读slave,但是在精确查找的时候应该是读master的。当然比较普遍的复杂查询也放在slave是比较明智的。文章源自运维生存时间-https://www.ttlsa.com/mysql/maxscale-right-read-write-split/

Maxscale使用Hint读写分离实验

二话不说在之前环境的基础上来做接下来的实验。直接上配置文件:文章源自运维生存时间-https://www.ttlsa.com/mysql/maxscale-right-read-write-split/

主要配置文章源自运维生存时间-https://www.ttlsa.com/mysql/maxscale-right-read-write-split/

[Read-Write Service]
...
filters=Hint
 
[Hint]
type=filter
module=hintfilter

完整配置文章源自运维生存时间-https://www.ttlsa.com/mysql/maxscale-right-read-write-split/

[root@normal_11 ~]# cat /etc/maxscale.cnf 
###################################################
# CREATE USER maxscale@'%' IDENTIFIED BY "123456";
# GRANT replication slave, replication client ON *.* TO maxscale@'%';
# GRANT SELECT ON mysql.* TO maxscale@'%';
# GRANT ALL ON maxscale_schema.* TO maxscale@'%';
# GRANT SHOW DATABASES ON *.* TO maxscale@'%';
# groupadd maxscale
# useradd -g maxscale maxscale
# cd /opt
# tar -zxf maxscale-2.0.1.rhel.7.tar.gz
# ln -s /opt/maxscale-2.0.1.rhel.7 /usr/local/maxscale
# chown -R maxscale:maxscale /usr/local/maxscale
# mkdir -p /u01/maxscale/{data,cache,logs,tmp}
# mkdir -p /u01/maxscale/logs/{binlog,trace}
# chown -R maxscale:maxscale /u01/maxscale
# /usr/local/maxscale/bin/maxkeys /u01/maxscale/data/
# /usr/local/maxscale/bin/maxpasswd /u01/maxscale/data/.secrets 123456
###################################################
 
[maxscale]
# 开启线程个数,默认为1.设置为auto会同cpu核数相同
threads=auto
# timestamp精度
ms_timestamp=1
# 将日志写入到syslog中
syslog=1
# 将日志写入到maxscale的日志文件中
maxlog=1
# 不将日志写入到共享缓存中,开启debug模式时可打开加快速度
log_to_shm=0
# 记录告警信息
log_warning=1
# 记录notice
log_notice=1
# 记录info
log_info=1
# 不打开debug模式
log_debug=0
# 日志递增
log_augmentation=1
 
# 相关目录设置
basedir=/usr/local/maxscale/
logdir=/u01/maxscale/logs/trace/
datadir=/u01/maxscale/data/
cachedir=/u01/maxscale/cache/
piddir=/u01/maxscale/tmp/
 
[server1]
type=server
address=192.168.137.21
port=3306
protocol=MySQLBackend
serv_weight=1
 
[server2]
type=server
address=192.168.137.22
port=3306
protocol=MySQLBackend
serv_weight=3
 
[server3]
type=server
address=192.168.137.23
port=3306
protocol=MySQLBackend
serv_weight=3
 
[MySQL Monitor]
type=monitor
module=mysqlmon
servers=server1,server2,server3
user=maxscale
passwd=1D30C1E689410756D7B82C233FCBF8D9
# 监控心态为 10s
monitor_interval=10000
# 当复制slave全部断掉时,maxscale仍然可用,将所有的访问指向master节点
detect_stale_master=true
# 监控主从复制延迟,可用后续指定router service的(配置此参数请求会永远落在 master)
# detect_replication_lag=true
 
[Read-Only Service]
type=service
router=readconnroute
servers=server1,server2,server3
user=maxscale
passwd=1D30C1E689410756D7B82C233FCBF8D9
router_options=slave
# 允许root用户登录执行
enable_root_user=1
# 查询权重
weightby=serv_weight
 
[Read-Write Service]
type=service
router=readwritesplit
servers=server1,server2,server3
user=maxscale
passwd=1D30C1E689410756D7B82C233FCBF8D9
max_slave_connections=100%
# sql语句中的存在变量只指向master中执行
use_sql_variables_in=master
# 允许root用户登录执行
enable_root_user=1
# 允许主从最大间隔(s)
max_slave_replication_lag=3600
filters=Hint
 
[MaxAdmin Service]
type=service
router=cli
 
[Read-Only Listener]
type=listener
service=Read-Only Service
protocol=MySQLClient
port=4008
 
[Read-Write Listener]
type=listener
service=Read-Write Service
protocol=MySQLClient
port=4006
 
[MaxAdmin Listener]
type=listener
service=MaxAdmin Service
protocol=maxscaled
socket=/u01/maxscale/tmp/maxadmin.sock
port=6603
 
[Hint]
type=filter
module=hintfilter

编写Python程序实现Hint读写分离

这边由于在MySQL Client 控制台效果演示不出来,这边就用程序来演示:文章源自运维生存时间-https://www.ttlsa.com/mysql/maxscale-right-read-write-split/

  1. 代码

在代码中我们手动指定SELECT读取Master 在SQL后面加上 -- maxscale route to master文章源自运维生存时间-https://www.ttlsa.com/mysql/maxscale-right-read-write-split/

[root@normal_11 tmp]# cat test.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
 
from sqlalchemy import create_engine
 
conf = {
    'host': '192.168.137.11',
    'port': 4006,
    'user': 'HH',
    'passwd': 'oracle',
    'db': 'test',
    'charset': 'utf8'
}
# 生成链接数据库engine
str = ('mysql+mysqldb://{username}:{password}@{host}:{port}/{database}'
            '?charset=utf8'.format(username = conf.get('user', ''),
                                   password = conf.get('passwd', ''),
                                   host = conf.get('host', ''),
                                   port = conf.get('port', 3306),
                                   database = conf.get('db', '')))
 
engine = create_engine(str)
conn = engine.connect()
 
sql = """
SELECT * from t1; -- maxscale route to master
"""
 
conn.execute("SET AUTOCOMMIT=1")
conn.execute(sql)
  1. 执行
[root@normal_11 tmp]# python test.py
  1. 查看日志
# 这边Maxscale路由到手动指定master上
2016-11-05 11:34:43.681   [7]  info   : (route_single_stmt): > Autocommit: [enabled], trx is [not open], cmd: COM_QUERY, type: QUERY_TYPE_READ, stmt: 
SELECT * from t1; -- maxscale route to master
, Hint: HINT_ROUTE_TO_MASTER
  1. 手动指定 Slave
-- 修改代码中的查询语句,让语句路由到Slave
SELECT * from t1; -- maxscale route to master
-- 修改给
SELECT * from t1; -- maxscale route to slave
  1. 运行代码 并 产看日志情况
# 这边Maxscale路由到手动指定slave上
2016-11-05 11:48:56.974   [6]  info   : (route_single_stmt): > Autocommit: [enabled], trx is [not open], cmd: COM_QUERY, type: QUERY_TYPE_READ, stmt: 
SELECT * from t1; -- maxscale route to slave
, Hint: HINT_ROUTE_TO_SLAVE

总结

Maxscale的Hint功能就能满足通过业务情况在实现正确的读写分离了,并且这对于程序原来说只需要有使用到SQL语句的地方就好了。文章源自运维生存时间-https://www.ttlsa.com/mysql/maxscale-right-read-write-split/

昵称: HH文章源自运维生存时间-https://www.ttlsa.com/mysql/maxscale-right-read-write-split/

QQ: 275258836文章源自运维生存时间-https://www.ttlsa.com/mysql/maxscale-right-read-write-split/

ttlsa群交流沟通(QQ群②: 6690706 QQ群③: 168085569 QQ群④: 415230207(新) 微信公众号: ttlsacom)文章源自运维生存时间-https://www.ttlsa.com/mysql/maxscale-right-read-write-split/

感觉本文内容不错,读后有收获?

逛逛衣服店,鼓励作者写出更好文章。

weinxin
我的微信
微信公众号
扫一扫关注运维生存时间公众号,获取最新技术文章~
HH
  • 本文由 发表于 03/12/2016 00:47:27
  • 转载请务必保留本文链接:https://www.ttlsa.com/mysql/maxscale-right-read-write-split/
评论  12  访客  12
    • qinglang
      qinglang 1

      请问楼主,我用上面的python脚本去测试时,会报错,说认证失败(4006端口)。但是我改为直接连接3306端口又可以连接上。随后我用navicat工具连接测试4006端口那个账户,也是没问题。我的Maxscale版本是2.0.3。以下是报错信息:sqlalchemy.exc.OperationalError: (_mysql_exceptions.OperationalError) (1045, “Access denied for user ‘testrw’@’172.18.7.130’ (using password: YES) to database ‘test'”)

        • qinglang
          qinglang 1

          @ qinglang 解决了,深刻理解了

            • fengqsqd
              fengqsqd 0

              @ qinglang 想问下是怎么解决的?我也遇到同样问题

          • jerry
            jerry 3

            我的这个配置写的是
            use_sql_variables_in=all
            意思为,程序带有变量的时候,也会在从库上执行。

            比如discuz论坛,每次查询的时候,程序都会执行set names utf8;

            如果从库上不执行的话,会出现乱码。

              • HH
                HH

                @ jerry 没大明白你的意思,具体你可以做一下实验。都设置了use_sql_variables_in=all那就都会执行相关的语句,应该不会存在你所说的问题。

                  • jerry
                    jerry 3

                    @ HH 谢谢楼主的回复,是这样的,我线上碰到过,我use_sql_variables_in=master的话,以前出过问题,还要就是web端的字符集也要修改,我说的是系统级别的,centos的话/etc/sysconfig/i18n
                    还有全局模式下环境变量也要改,改成中文字符集的模式,不然网站的中文会出现乱码,真的!数据库级别已经都是utf8了

                • jerry
                  jerry 3

                  您好,博主,很有幸拜读了您的文章,但我有一个疑问,就是我这里有几张表,事务一致性很高,必须强制走主库,我不明白应该如何再maxscale上配置,你说的这个Hint是maxscale安装好以后自带的吗?我还是不明白它是通过什么原理,能够让某些select语句强行走主库的

                    • HH
                      HH

                      @ jerry 首先Maxscale的功能是可拔插的,Hint功能的插件默认就在Maxscale中了,只要配置一下就可以使用了。
                      关于Hint的原理,这个没读源码也不懂,但是从软件设计的角度上看,maxscale相关程序会解析你的SQL语句,从而读取出你的hint让后再对hint进行解析,从而判断出你的SQL是需要走主库还是从库。

                        • jerry
                          jerry 3

                          @ HH 我这里读写分离以后,因为对主从的延时要求相当高,我用pt工具检测的是基本有1~2秒的延时就会有问题,我现在改用mysql 5.7的并行复制试试

                            • 匿名
                              匿名 9

                              @ jerry 没明白你的需求

                                • jerry
                                  jerry 3

                                  @ 匿名 您好,博主,我的需求和你写的很相似,也是某些表对事务的一致性非常高,比如下订单是一个写操作,在主库,订单生成以后,去查询这张订单的信息,在某个从库,另一台服务器,但是在生产环境,高并发的情况下,偶尔回有延时,导致订单其实已生成了,可能查询没有信息,我的需求就是这样的,如何做到某几张表强制走主库,就是select查询也走主库,由maxscale来控制的!

                              • jerry
                                jerry 3

                                @ HH 我按照官方文档的配置加了如下配置
                                filters=Hint

                                [Hint]
                                type=filter
                                module=hintfilter
                                他有这样的解释:The first INSERT query will be routed to the master. The following SELECT query would normally be routed to the slave but with the added routing hint it will be routed to the master. This way we can do an INSERT and a SELECT right after it and still get up-to-date data.
                                我相信应该能满足我的需求了,不过还是很感谢博主的文章给了我启发

                          评论已关闭!