python实现mysql的读写分离及负载均衡


Posted in Python onFebruary 04, 2018

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、端口号等信息(没有开发经历的同学可能理解这段话有点费劲)。

       流程图如下:

python实现mysql的读写分离及负载均衡

           代码如下:

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()

测试结果:

python实现mysql的读写分离及负载均衡

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

因为测试函数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的读写分离及负载均衡,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的!

Python 相关文章推荐
python执行get提交的方法
Apr 29 Python
Python基础知识_浅谈用户交互
May 31 Python
为什么str(float)在Python 3中比Python 2返回更多的数字
Oct 16 Python
在Python中使用defaultdict初始化字典以及应用方法
Oct 31 Python
Django REST framework视图的用法
Jan 16 Python
手写一个python迭代器过程详解
Aug 27 Python
python @propert装饰器使用方法原理解析
Dec 25 Python
Python 批量读取文件中指定字符的实现
Mar 06 Python
Python如何给函数库增加日志功能
Aug 04 Python
Python如何截图保存的三种方法(小结)
Sep 01 Python
这样写python注释让代码更加的优雅
Jun 02 Python
Python使用Web框架Flask开发项目
Jun 01 Python
python负载均衡的简单实现方法
Feb 04 #Python
python爬虫爬取某站上海租房图片
Feb 04 #Python
Python爬虫实现百度图片自动下载
Feb 04 #Python
Python中的defaultdict与__missing__()使用介绍
Feb 03 #Python
Python网络爬虫神器PyQuery的基本使用教程
Feb 03 #Python
numpy.random.seed()的使用实例解析
Feb 03 #Python
Python网络爬虫中的同步与异步示例详解
Feb 03 #Python
You might like
php 数组的合并、拆分、区别取值函数集
2010/02/15 PHP
PHP中Enum(枚举)用法实例详解
2015/12/07 PHP
php自定义中文字符串截取函数substr_for_gb2312及substr_for_utf8示例
2016/05/28 PHP
Yii2框架RESTful API 格式化响应,授权认证和速率限制三部分详解
2016/11/10 PHP
PHP用函数嵌入网站访问量计数器
2017/10/27 PHP
php集成开发环境详解
2019/09/24 PHP
JS运行耗时操作的延时显示方法
2010/11/19 Javascript
Jquery之Ajax运用 学习运用篇
2011/09/26 Javascript
学习JavaScript的最佳方法分享
2011/10/21 Javascript
jQuery.query.js 取参数的两点问题分析
2012/08/06 Javascript
利用Javascript判断操作系统的类型实现不同操作系统下的兼容性
2013/01/29 Javascript
Javascript中valueOf与toString区别浅析
2013/03/19 Javascript
javascript使用onclick事件改变选中行的颜色
2013/12/30 Javascript
深入理解bootstrap框架之入门准备
2016/10/09 Javascript
javascript设计模式之Adapter模式【适配器模式】实现方法示例
2017/01/13 Javascript
JS正则表达式验证账号、手机号、电话和邮箱是否合法
2017/03/08 Javascript
给Easyui-Datebox设置隐藏或者不可用的解决方法
2017/05/26 Javascript
详解在vue-cli项目中安装node-sass
2017/06/21 Javascript
Vue中正确使用jQuery的方法
2017/10/30 jQuery
对Angular中单向数据流的深入理解
2018/03/31 Javascript
node结合swig渲染摸板的方法
2018/04/11 Javascript
Node.js系列之安装配置与基本使用(1)
2019/08/30 Javascript
JavaScript正则表达式验证登录实例
2020/03/18 Javascript
node中短信api实现验证码登录的示例代码
2021/01/20 Javascript
状态机的概念和在Python下使用状态机的教程
2015/04/11 Python
详解python开发环境搭建
2016/12/16 Python
用Python设计一个经典小游戏
2017/05/15 Python
CentOS中升级Python版本的方法详解
2017/07/10 Python
详解Python3之数据指纹MD5校验与对比
2019/06/11 Python
Ubuntu18.04下python版本完美切换的解决方法
2019/06/14 Python
迪拜领先运动补剂零售品牌中文站:Sporter商城
2019/08/20 全球购物
艺术系大学生毕业个人自我评价
2013/09/19 职场文书
幼儿园招生广告
2014/03/19 职场文书
教师党员个人自我剖析材料
2014/09/29 职场文书
学院党委班子四风问题自查报告及整改措施
2014/10/25 职场文书
Redis监控工具RedisInsight安装与使用
2022/03/21 Redis