MySQL集群读写分离的自定义实现

基于MySQL Router可以实现高可用,读写分离,负载均衡之类的,MySQL Router可以说是非常轻量级的一个中间件了。
看了一下MySQL Router的原理,其实并不复杂,原理也并不难理解,其实就是一个类似于VIP的代理功能,其中一个MySQL Router有两个端口号,分别是对读和写的转发。
至于选择哪个端口号,需要在申请连接的时候自定义选择,换句话说就是在生成连接字符串的时候,要指明是读操作还是写操作,然后由MySQL Router转发到具体的服务器上。

引用这里的话说就是
一般来说,通过不同端口实现读/写分离,并非好方法,最大的原因是需要在应用程序代码中指定这些连接端口。
但是,MySQL Router只能通过这种方式实现读写分离,所以MySQL Router拿来当玩具玩玩就好。其原理参考下图,相关安装配置等非常简单。

 

其实暂不论“MySQL Router拿来当玩具玩玩就好”,类似需要自己指定端口(或者说指定读写)来实现读写分离这种方式,自己完全可以实现,又何必用一个中间件呢?
对于MySQL Router来说,它自己本身又是单点的,还要考虑Router自身的高可用(解决了一个问题的同时又引入一个问题)。
很早之前就在想,可不可以尝试不借助中间件,也就无需关注中间件自身的高可用,自己实现读写分离呢?

对于最简单的master-salve复制的集群方式的读写分离,
可以基于在原始的数据库连接上指定一个优先级,把master服务器的优先级指定到最高,其余两个指定成一个较低的优先级
对于应用程序发起的请求,需要指明是读还是写,如果是写操作,就指定到master上执行,如果是读操作,就随机地指向slave操作,完全可以在连接层就实现类似于MySQL Router的功能。
其实非常简单,花不了多久就可以实现类似这么一个功能,在连接层实现读写分离,高可用,负载均衡,demo一个代码实现。

如下简单从数据库连接层实现了读写分离以及负载均衡。
1,写请求指向连接字符串中最高优先级的master,如果指定的最高优先级实例不可用,这里假如是实现了故障转移,依次寻找次优先级的实例
2,slave复制master的数据,读请求随机指向不同的slave,一旦某个slave不可用,继续寻找其他的slave
3,维护一个连接池,连接一律从连接池中获取。

故障转移可以独立实现,不需要在连接层做,连接层也不是做故障转移的。这样一旦发生故障,只要实现了故障转移,应用程序端可以不用做任何修改。

# -*- coding: utf-8 -*-
import pymysql
import random
from DBUtils.PooledDB import PooledDB
import socket

class MySQLRouter:

    operation = None
    conn_list = []

    def __init__(self, *args, **kwargs):
        for k, v in kwargs.items():
            setattr(self, k, v)

    # 探测实例端口号
    @staticmethod
    def get_mysqlservice_status(host,port):
        mysql_stat = None
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        result = s.connect_ex((host, int(port)))
        # port os open
        if (result == 0):
            mysql_stat = 1
        return mysql_stat

    def get_connection(self):
        if not conn_list:
            raise("no config error")

        conn = None
        current_conn = None
        # 依据节点优先级排序
        self.conn_list.sort(key=lambda k: (k.get(‘priority‘, 0)))
        #写或者未定义请求,一律指向高优先级的服务器,可读写
        if(self.operation.lower() == "write") or not self.operation:
            for conn in conn_list:
                # 如果最高优先级的主节点不可达,这里假设成功实现了故障转移,继续找次优先级的实例。
                if self.get_mysqlservice_status(conn["host"], conn["port"]):
                    current_conn = conn
                    break
                else:
                    continue
        #读请求随机指向不同的slave
        elif(self.operation.lower() == "read"):
            #随机获取除了最该优先级节点之外的节点
            conn_read_list = conn_list[1:len(conn_list)]
            random.shuffle(conn_read_list)
            for conn in conn_read_list:
                #如果不可达,继续寻找其他除了主节点之外的节点
                if self.get_mysqlservice_status(conn["host"], conn["port"]):
                    current_conn = conn
                    break
                else:
                    continue
        try:
            #从连接池中获取当前连接
            if (current_conn):
                pool = PooledDB(pymysql,20, host=current_conn["host"], port=current_conn["port"], user=current_conn["user"], password=current_conn["password"],db=current_conn["database"])
                conn = pool.connection()
        except:
            raise

        if not conn:
            raise("create connection error")

        return conn;

if __name__ == ‘__main__‘:

    #定义三个实例
    conn_1 = {‘host‘: ‘127.0.0.1‘, ‘port‘: 3306, ‘user‘: ‘root‘, ‘password‘: ‘root‘,"database":"db01","priority":100}
    conn_2 = {‘host‘: ‘127.0.0.1‘, ‘port‘: 3307, ‘user‘: ‘root‘, ‘password‘: ‘root‘,"database":"db01","priority":200}
    conn_3 = {‘host‘: ‘127.0.0.1‘, ‘port‘: 3308, ‘user‘: ‘root‘, ‘password‘: ‘root‘,"database":"db01","priority":300}

    conn_list = []
    conn_list.append(conn_1)
    conn_list.append(conn_2)
    conn_list.append(conn_3)

    print("####execute update on master####")
    myrouter = MySQLRouter(conn_list=conn_list, operation="write")
    conn = myrouter.get_connection()
    cursor = conn.cursor()
    cursor.execute("update t01 set update_date = now() where id = 1")
    conn.commit()
    cursor.close()
    conn.close()

    print("####loop execute read on slave,query result####")
    #循环读,判断读指向哪个节点。
    for loop in range(10):
        myrouter = MySQLRouter(conn_list = conn_list,operation = "read")
        conn = myrouter.get_connection()
        cursor = conn.cursor()
        cursor.execute("SELECT id,cast(update_date as char), CONCAT(‘instance port is: ‘, CAST( @@PORT AS CHAR)) AS port FROM t01;")
        result = cursor.fetchone()
        print(result)
        cursor.close()
        conn.close()

这里用过服务器的一个优先级,将写请求指向最高优先级的master服务器,读请求随机指向非最高优先级的slave,
对于更新请求,都在master上执行,slave复制了master的数据,每次读到的数据都不一样,并且每次都请求的执行,基本上都随机地指向了两台slave服务器
通过查询返回一个端口号,来判断读请求是否平均分散到了不通的slave端。

与“MySQL Router拿来当玩具玩玩就好”相比,这里的实现一样low,因为对数据的请求需要请求明确指定是读还是写。

不过,对于自动读写分离,无非是一个SQL语句执行的是的读或写判断问题,并非难事,这个需要解析请求的SQL是读的还是写的问题。
某些数据库中间件可以实现自动的读写分离,但是要明白,对于那些支持自动读写分离的中间件,往往是要受到一定的约束的,比如不能用存储过程什么的,为什么呢?
还是上面提到的SQL解析的问题,因为一旦使用了存储过程,无法解析出来这个SQL到底是执行的是读还是写,最起码不是太直接。
对于SQL读写的判断,也就是维护一个读或者写的枚举正则表达式,非读即写,只是要格外关注这个读写的判断的效率问题。

原文地址:https://www.cnblogs.com/wy123/p/11295852.html

时间: 2024-08-02 00:42:49

MySQL集群读写分离的自定义实现的相关文章

Mysql集群读写分离(Amoeba)

实验环境 Master.Amoeba--IP:192.168.1.5 Slave---IP:192.168.1.10 安装JDK JDK下载地址:http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html [[email protected] ~]# mkdir /usr/local/JDF [[email protected] ~]# tar -xvf jdk-8u101-linux-x

Mysql高级集群-读写分离Amoeba

一.环境介绍Master-IP:10.0.0.201Slave- IP:10.0.0.202Amobea-IP:10.0.0.203 二.安装JDK# mkdir /Amoeba# tar -xvf jdk-7u40-linux-x64.tar.gz -C /Amoeba/# vim /etc/profileJAVA_HOME=/Amoeba/jdk1.7.0_40export JAVA_HOME PATH=$JAVA_HOME/bin:$PATHexport PATH CLASSPATH=.:

浅谈高性能数据库集群——读写分离

本文主要介绍高性能数据库集群读写分离相关理论,基本架构,涉及的复杂度问题以及常见解决方案. 1 读写分离概述 基本架构图: 2 适用场景 读写分离不是银弹,并不是一有性能问题就上读写分离,而是应该先优化,例如优化慢查询,调整不合理的业务逻辑,引入缓存查询等只有确定系统没有优化空间后才考虑读写分离集群 3 引入的系统复杂度问题 问题一 主从复制延迟 问题二 分配机制 如何将读写操作区分开来,然后访问不同的数据库服务器? 解决方案1 客户端程序代码封装实现 基本架构图 业界开源实现 Sharding

十四、linux-MySQL的数据库集群读写分离及高可用性、备份等

一.数据库集群及高可用性 二. 三.mysql实现读写分离 mysql实现读写分离有多种方式: 1)代码语言(php\python\java等)层面实现读写分离,找开发进行实现. 2)通过软件工具实现读写分离,例如amoeba软件 amoeba软件既可以实现负载均衡,也可以实现读写分离,就是进行服务器端压力的分离. 原文地址:https://www.cnblogs.com/dangjingwei/p/11567368.html

MySQL主从复制、读写分离、高可用集群搭建

MySQL主从复制.读写分离.高可用集群搭建  一.服务介绍   1.1 Keepalived     Keepalived,见名知意,即保持存活,其目的是解决单点故障,当一台服务器宕机或者故障时自动切换到其他的服务器中.Keepalived是基于VRRP协议实现的.VRRP协议是用于实现路由器冗余的协议,VRRP协议将两台或多台路由器设备虚拟成虚拟设备,可以对外提供虚拟路由器IP(一个或多个),即漂移IP(VIP). 1.2 ProxySQL ProxySQL是一个高性能,高可用性的MySQL

mysql集群:主从服务器读写分离

mysql集群架构方式很多,根据不同的需求做不一样的架构,简单一点的就是mysql的replication,也就是Mysql的复制功能,模式有:master-slaves,master-slaves-slaves,master-master-slaves等可以有多个分层,那么现在我所要说的是master-slaves的模式(其他的模式原理基本都一样),然后再通过mysql官方提供的Mysql-proxy实现读写分离,达到的效果. 环境: 主机::192.168.1.109,slave1:192.

mycat实现简单的mysql集群负载均衡

什么是mycat呢? 简单理解为一个MySQL中间件,它支持分流.基于心跳的自动故障切换,支持读写分离,支持mysql主从,基于Nio管理线程的高并发- 详见官网:http://www.mycat.io/ 为什么需要mysql集群? 一个庞大的分布式系统的性能瓶颈中,最脆弱的就是连接,一个是客户端与后端的连接,另一个是后端与数据库的连接,说白了就是发送端请求太多,接收端能够的接收和处理的请求并不多,在客户端与后端中可以利用类似nginx的负载均衡解决,而在后端与数据库中可以利用类似mycat的负

MySql 集群配置

MYSQL CLUSTER方案介绍 本文的大致框架来自罗志威.黄川的报告, 在它的基础上进行简化和修改一些bug并且添加了主从复制的章节,最后做出该文档 MySQL Cluster 是MySQL适合于分布式计算环境的高实用.高冗余版本.它采用了NDB Cluster 存储引擎,允许在1个 Cluster 中运行多个MySQL服务器.现在mysql cluster 被独立出来, 作为一个专门的产品进行运营, mysql-server-5.6+ 就不在存在对mysql cluster的支持,需要独立

mysql集群一:主从复制,通过mysql-proxy做负载均衡

mysql集群架构方式很多,根据不同的需求做不一样的架构,简单一点的就是mysql的replication,也就是Mysql的复制功能,模式有:master-slaves,master-slaves-slaves,master-master-slaves等可以有多个分层,那么现在我所要说的是master-slaves的模式(其他的模式原理基本都一样),然后再通过mysql官方提供的Mysql-proxy实现读写分离,达到负载均衡的效果. 环境: 主机:master:192.168.1.109,s