Oracle数据库有其公司开发的配套rac来实现负载均衡,目前已知的最大节点数能到128个,但是其带来的维护成本无疑是很高的,并且rac的稳定性也并不是特别理想,尤其是节点很多的时候。

但是,相对mysql来说,rac的实用性要比mysql的配套集群软件mysql-cluster要高很多。因为从网上了解到情况来看,很少公司在使用mysql-cluster,大多数企业都会选择第三方代理软件,例如MySQL Proxy、Mycat、haproxy等,但是这会引起另外一个问题:单点故障(包括mysql-cluster:管理节点)。如果要解决这个问题,就需要给代理软件搭建集群,在访问量很大的情况下,代理软件的双机或三机集群会成为访问瓶颈,继续增加其节点数,无疑会带来各方面的成本。

那么,如何可以解决这个问题呢?

解决上述问题,最好的方式个人认为应该是在程序中实现。通过和其他mysql DBA的沟通,也证实了这个想法。但是由此带来的疑问也就产生了:会不会增加开发成本?对现有的应用系统做修改会不会改动很大?会不会增加后期版本升级的难度?等等。

对于一个架构设计良好的应用系统可以很肯定的回答:不会。

那么怎么算一个架构设计良好的应用系统呢?

简单来说,就是分层合理、功能模块之间耦合性底。以本人的经验来说,系统设计基本上可以划分为以下四层:

1.  实体层:主要定义一些实体类

2.  数据层:也可以叫SQL处理层。主要负责跟数据库交互取得数据

3.  业务处:主要是根据业务流程及功能区分模块(或者说定义不同的业务类)

4.  表现层:呈现最终结果给用户

实现上述功能(mysql的读写分离及负载均衡),在这四个层次中,仅仅涉及到数据层。

严格来说,对于设计良好的系统,只涉及到一个类的一个函数:在数据层中,一般都会单独划分出一个连接类,并且这个连接类中会有一个连接函数,需要改动的就是这个函数:在读取连接字符串之前加一个功能函数返回需要的主机、ip、端口号等信息(没有开发经历的同学可能理解这段话有点费劲)。

流程图如下:

代码如下:

import mmap
import json
import random
import mysql.connector
import time ##公有变量
#dbinfos={
# "db0":{'host':'192.168.42.60','user':'root','pwd':'Abcd1234','my_user':'root','my_pwd':'Abcd.1234',"port":3306,"database":"","role":"RW","weight":10,"status":1},
# "db1":{'host':'192.168.42.61','user':'root','pwd':'Abcd1234','my_user':'root','my_pwd':'Abcd.1234',"port":3306,,"database":"":"R","weight":20,"status":1}
# } dbinfos={}
mmap_file = None
mmap_time=None ##这个函数返回json格式的字符串,也是实现初始化数据库信息的地方
##使用json格式是为了方便数据转换,从字符串---》二进制--》字符串---》字典
##如果采用其它方式共享dbinfos的方法,可以不用此方式
##配置库的地址
def get_json_str1():
return json.dumps(dbinfos) ##读取配置库中的内容
def get_json_str():
try:
global dbinfos
cnx = mysql.connector.connect(user='root', password='Abcd.1234',
host='192.168.42.60',
database='rwlb')
cursor = cnx.cursor()
cmdString="select * from rwlb"
cnt=-1
cursor.execute(cmdString)
for (host,user,pwd,my_user,my_pwd,role,weight,status,port,db ) in cursor:
cnt=cnt+1
dict_db={'host':host,'user':user,'pwd':pwd,'my_user':my_user,'my_pwd':my_pwd,"port":port,"database":db,"role":role,"weight":weight,"status":status}
dbinfos["db"+str(cnt)]=dict_db
cursor.close()
cnx.close()
return json.dumps(dbinfos)
except:
cursor.close()
cnx.close()
return "" ##判断是否能正常连接到数据库
def check_conn_host():
try:
cnx = mysql.connector.connect(user='root', password='Abcd.1234',
host='192.168.42.60',
database='rwlb')
cursor = cnx.cursor()
cmdString="select user()"
cnt=-1
cursor.execute(cmdString)
for user in cursor:
cnt=len(user)
cursor.close()
cnx.close()
return cnt
except :
return -1; ##select 属于读操作,其他属于写操作-----这里可以划分的更详细,比如执行存储过程等
def analyze_sql_state(sql):
if "select" in sql:
return "R"
else:
return "W" ##读取时间信息
def read_mmap_time():
global mmap_time,mmap_file
mmap_time.seek(0)
##初始时间
inittime=int(mmap_time.read().translate(None, b'\x00').decode())
##当前时间
endtime=int(time.time())
##时间差
dis_time=endtime-inittime
print("dis_time:"+str(dis_time))
#重新读取数据
if dis_time>10:
##当配置库正常的情况下才重新读取数据
print(str(check_conn_host()))
if check_conn_host()>0:
print("read data again")
mmap_time.seek(0)
mmap_file.seek(0)
mmap_time.write(b'\x00')
mmap_file.write(b'\x00')
get_mmap_time()
get_mmap_info()
else:
print("can not connect to host")
#不重新读取数据
else:
print("do not read data again") ##从内存中读取信息,
def read_mmap_info(sql):
read_mmap_time()
print("The data is in memory")
global mmap_file,dict_db
mmap_file.seek(0)
##把二进制转换为字符串
info_str=mmap_file.read().translate(None, b'\x00').decode()
#3把字符串转成json格式,方便后面转换为字典使用
infos=json.loads(info_str)
host_count=len(infos)
##权重列表
listw=[]
##总的权重数量
wtotal=0
##数据库角色
dbrole=analyze_sql_state(sql)
##根据权重初始化一个列表。这个是比较简单的算法,所以权重和控制在100以内比较好----这里可以选择其他比较好的算法
for i in range(host_count):
db="db"+str(i)
if dbrole in infos[db]["role"]:
if int(infos[db]["status"])==1:
w=infos[db]["weight"]
wtotal=wtotal+w
for j in range(w):
listw.append(i)
if wtotal >0:
##产生一个随机数
rad=random.randint(0,wtotal-1)
##读取随机数所在的列表位置的数据
dbindex=listw[rad]
##确定选择的是哪个db
db="db"+str(dbindex)
##为dict_db赋值,即选取的db的信息
dict_db=infos[db]
return dict_db
else :
return {} ##如果内存中没有时间信息,则向内存红写入时间信息
def get_mmap_time():
global mmap_time
##第二个参数1024是设定的内存大小,单位:字节。如果内容较多,可以调大一点
mmap_time = mmap.mmap(-1, 1024, access = mmap.ACCESS_WRITE, tagname = 'share_time')
##读取有效比特数,不包括空比特
cnt=mmap_time.read_byte()
if cnt==0:
print("Load time to memory")
mmap_time = mmap.mmap(0, 1024, access = mmap.ACCESS_WRITE, tagname = 'share_time')
inittime=str(int(time.time()))
mmap_time.write(inittime.encode()) ##如果内存中没有对应信息,则向内存中写信息以供下次调用使用
def get_mmap_info():
global mmap_file
##第二个参数1024是设定的内存大小,单位:字节。如果内容较多,可以调大一点
mmap_file = mmap.mmap(-1, 1024, access = mmap.ACCESS_WRITE, tagname = 'share_mmap')
##读取有效比特数,不包括空比特
cnt=mmap_file.read_byte()
if cnt==0:
print("Load data to memory")
mmap_file = mmap.mmap(0, 1024, access = mmap.ACCESS_WRITE, tagname = 'share_mmap')
mmap_file.write(get_json_str().encode()) ##测试函数
def test1():
get_mmap_time()
get_mmap_info()
for i in range(10):
sql="select * from db"
#sql="update t set col1=a where b=2"
dbrole=analyze_sql_state(sql)
dict_db=read_mmap_info(sql)
print(dict_db["host"]) def test2():
sql="select * from db"
res=analyze_sql_state(sql)
print("select:"+res)
sql="update t set col1=a where b=2"
res=analyze_sql_state(sql)
print("update:"+res)
sql="insert into t values(1,2)"
res=analyze_sql_state(sql)
print("insert:"+res)
sql="delete from t where b=2"
res=analyze_sql_state(sql)
print("delete:"+res) ##类似主函数
if __name__=="__main__":
test2()

测试结果:

从结果可以看出,只有第一次向内存加载数据,并且按照权重实现了负载均衡。

因为测试函数test1()写的是固定语句,所以读写分离的结果没有显示出来。

另外:测试使用的数据库表结构及数据:

 desc rwlb;
+---------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| host | varchar(50) | YES | | NULL | |
| user | varchar(50) | YES | | NULL | |
| pwd | varchar(50) | YES | | NULL | |
| my_user | varchar(50) | YES | | NULL | |
| my_pwd | varchar(50) | YES | | NULL | |
| role | varchar(10) | YES | | NULL | |
| weight | int(11) | YES | | NULL | |
| status | int(11) | YES | | NULL | |
| port | int(11) | YES | | NULL | |
| db | varchar(50) | YES | | NULL | |
+---------+-------------+------+-----+---------+-------+
select * from rwlb;
+---------------+------+----------+---------+-----------+------+--------+--------+------+------+
| host | user | pwd | my_user | my_pwd | role | weight | status | port | db |
+---------------+------+----------+---------+-----------+------+--------+--------+------+------+
| 192.168.42.60 | root | Abcd1234 | root | Abcd.1234 | RW | 10 | 1 | NULL | NULL |
| 192.168.42.61 | root | Abcd1234 | root | Abcd.1234 | R | 20 | 1 | NULL | NULL |
+---------------+------+----------+---------+-----------+------+--------+--------+------+------+

python实现mysql的读写分离及负载均衡的更多相关文章

  1. Atlas mysql的读写分离和负载均衡<转>

    mysql的读写分离和负载均衡 http://my.oschina.net/superbigfu/blog/178134

  2. MaxScale:实现MySQL读写分离与负载均衡的中间件利器

    1. MaxScale 是干什么的? 配置好了 MySQL 的主从复制结构后,我们希望实现读写分离,把读操作分散到从服务器中,并且对多个从服务器能实现负载均衡. 读写分离和负载均衡 是MySQL集群的 ...

  3. Mysql-Proxy实现mysql读写分离、负载均衡 (转)

    在mysql中实现读写分离.负载均衡,用Mysql-Proxy是很容易的事,不过大型处理对于性能方面还有待提高,主要配置步骤如下: 1.1. mysql-proxy安装 MySQL Proxy就是这么 ...

  4. 如何轻松实现MySQL数据库的读写分离和负载均衡?

    配置好了 Mysql 的主从复制结构后,我们希望实现读写分离,把读操作分散到从服务器中,并且对多个从服务器能实现负载均衡.读写分离和负载均衡是 Mysql 集群的基础需求,MaxScale 就可以帮着 ...

  5. 【DB宝42】MySQL高可用架构MHA+ProxySQL实现读写分离和负载均衡

    目录 一.MHA+ProxySQL架构 二.快速搭建MHA环境 2.1 下载MHA镜像 2.2 编辑yml文件,创建MHA相关容器 2.3 安装docker-compose软件(若已安装,可忽略) 2 ...

  6. mycat(读写分离、负载均衡、主从切换)

    博主本人平和谦逊,热爱学习,读者阅读过程中发现错误的地方,请帮忙指出,感激不尽 1.环境准备 1.1新增两台虚拟机 mycat01:192.168.247.81 mycat02:192.168.247 ...

  7. 如何利用MHA+ProxySQL实现读写分离和负载均衡

    摘要:本文分享一下"MHA+中间件ProxySQL"如何来实现读写分离+负载均衡的相关知识. 本文分享自华为云社区<MySQL高可用架构MHA+ProxySQL实现读写分离和 ...

  8. phalcon:数据库分库,读写分离,负载均衡 系统方法执行顺序

    phalcon:数据库分库,读写分离,负载均衡 系统方法执行顺序 用命名空间区分不同的数据库实例,对应代码结构上是不同的目录区分,在同一目录下基类负责初始化连接.连接来自初始化时注入的多个db服务 隐 ...

  9. c数据库读写分离和负载均衡策略

    最近在学习数据库的读写分离和主从复制,采用的是一主多从策略,采用轮询的方式,读取从数据库的内容.但是,假如某一台从数据库宕机了,而客户端不知道,每次轮选到此从数据库,不都要报错?到网上查阅了资料,找到 ...

随机推荐

  1. 【翻译】Jay Kreps - 为何流处理中局部状态是必要的

    译者注: 原文作者是 Jay Kreps,也是那篇著名的<The Log: What every software engineer should know about real-time da ...

  2. 封装OkHttp,通过Callback改造Callback实现

    1:实现Callback回调接口import android.os.Handler;import android.os.Looper;import android.os.Message;import ...

  3. java split函数应该注意的问题

    split函数的参数是一个String,但是这个String会被解释成一个正则表达式. 比如 "test.txt".split(".").length得到的值是 ...

  4. JAVA三大特性之一——封装

    自学java已经有一段时间了,但是感觉对于很多知识点还是有必要总结和整理一下,下面我就来说一下我对JAVA三大特性之一——封装特性的认识和理解. 封装,从字面意思可以看出来,就是包装,也就是把我们写好 ...

  5. HTTP严格安全传输(HTTP Strict Transport Security, HSTS)chromuim实现源码分析(二)

    HTTP严格安全传输(HTTP Strict Transport Security, HSTS)chromuim实现源码分析(一) 下面来查看其他对保存HSTS信息的enabled_sts_hosts ...

  6. 每天一个Linux命令(20)--find命令之exec

    find 是我们很常用的一个Linux命令,但是我们一般查找出来的额并不仅仅是看看而已,还会有进一步的操作,这个时候exec的作用就显现出来了. exec解释: -exec  参数后面跟的是 comm ...

  7. 项目架构开发:数据访问层之UnitOfWork

    接上文 项目架构开发:数据访问层之IQuery 本章我们继续IUnitOfWork的开发,从之前的IRepository接口中就可以看出,我们并没有处理单元事务, 数据CUD每次都是立即执行的,这样有 ...

  8. AIO75产品特征与优势

    第一章 系统一体化 AIO7的核心流程由供应链.生产制造.财务成本及自动化办公构成,是迄今为止国内最完善的ERP.OA .HR .MES一体化产品.通过CRM(客户关系)及DRP(分销)扩充出“营销通 ...

  9. Kafka Eagle 源码解读

    1.概述 在<Kafka 消息监控 - Kafka Eagle>一文中,简单的介绍了 Kafka Eagle这款监控工具的作用,截图预览,以及使用详情.今天笔者通过其源码来解读实现细节.目 ...

  10. YARN学习笔记(一)——YARN的简介

    YARN的简介 什么是YARN MRv1的架构和缺陷 经典MapReduce的局限性 解决可伸缩性问题 YARN的架构 一个可运行任何分布式应用程序的集群 YARN中的应用程序提交 YARN的其他特性 ...