专栏首页Java识堂分库分表,读写分离后,数据库中间件扮演了一个怎样的角色?

分库分表,读写分离后,数据库中间件扮演了一个怎样的角色?

分库分表,读写分离会带来哪些问题?

前面一篇文章图解分布式系统架构(看推荐阅读)大概讲了一下分库分表,以及读写分离出现的场景,分库分表为了解决高并发和海量数据的问题。

分库后会出现新的问题

  1. 跨库join问题 如有2个库,订单库,用户库,要查询买了某件商品的所有用户信息
  2. 事务问题 用户下订单的时候需要扣减商品库存,如果订单数据和商品数据在一个数据库中,我们可以使用事务来保证扣减商品库存和生成订单的操作要么都成功要么都失败,但分库后就无法使用数据库事务了,这时就要用到分布式事务了

分表后也会出现新的问题

  1. join操作 水平分表后,数据分散在多个表中,如果需要与其他表进行join查询,需要在业务代码或数据库中间件中进行多次join查询,然后将结果合并
  2. count()操作 业务代码或者数据库中间件对每个表进行count(*)操作,然后将结果相加。或者新建一张表,假如表名为“记录数表”,包含table_name和row_count两个字段,每次插入或删除子表数据成功后,都更新“记录数表”
  3. order by操作 水平分表后,数据分散到多个字表中,排序操作无法再数据库中完成,只能由业务代码或数据库中间件分别查询每个子表中的数据,然后汇总进行排序

而高并发这个阶段,肯定是需要做读写分离的,啥意思?因为实际上大部分的互联网公司,一些网站,或者是 app,其实都是读多写少。所以针对这个情况,就是写一个主库,但是主库挂多个从库,然后从多个从库来读,那不就可以支撑更高的读并发压力了吗?

那么如何实现 MySQL 的读写分离?

其实很简单,就是基于主从复制架构,简单来说,就搞一个主库,挂多个从库,然后我们就单单只是写主库,然后从库读取bin log进行重放,这样主库和从库数据就一样,只不过并发量比较高时,会有主从同步延时问题

放个图理解一下MySQL主从复制的原理,这块面试经常被问到

总的来说,MySQL复制有三个步骤

  1. 在主库上把数据更改记录到二进制日志中(Binary Log)中(这些记录被称为二进制日志事件)
  2. 备库将主库上的日志复制到自己的中继日志(Relay Log)中
  3. 备库读取中继日志中的事件,将其重放到备库数据之上

现在理论知识都有了,就剩怎么实现了?本来就是为了实现一个功能,现在好了,单写读写分离,跨库join,分布式事务,排序操作等就够你忙的了。

这时候你就应该想起数据库中间件了,它能帮你进行上述操作,把你从复杂的数据处理中解放出来,专注于开发业务代码。

数据库中间件能帮你做什么?

目前国内用的最多的中间件就是sharding-jdbc,mycat,别的用的很少,不再介绍

而数据库中间件针对数据源管理,目前主要有两种思路

  1. 客户端模式,在每个应用程序模块中配置管理自己需要的一个(或者多个)数据源,直接访问各个数据库,在模块内完成数据的整合,sharding-jdbc的实现方式
  2. 通过中间代理层来统一管理所有的数据源,后端数据库集群对前端应用程序透明,mycat的实现方式

放两张图就能理解区别了

一般的建议是小公司用sharding-jdbc,大公司用mycat,因为维护一套mycat集群也需要人力,物力。鉴于篇幅限制,本文就介绍一下mycat的基本使用

以一个最形象的例子,让你明白mycat到底帮你做了什么?

先介绍一下什么是分片?简单来说,就是通过某种特定的条件,将我们存放在同一个数据库中的数据,分散存放到多个数据库上面,以达到分散单台设备负载的效果

如上图所表示,数据被分到多个分片数据库后,应用如果需要读取数据,就要需要处理多个数据源的数据。如果没有数据库中间件,那么应用将直接面对分片集群,数据源切换、事务处理、数据聚合都需要应用直接处理,原本该是专注于业务的应用,将会花大量的工作来处理分片后的问题,最重要的是每个应用处理将是完全的重复造轮子。

所以有了数据库中间件,应用只需要集中与业务处理,大量的通用的数据聚合,事务,数据源切换都由中间件来处理。

那么数据库中间件是怎么做到的呢?

绿色的部分为mycat的逻辑节点,蓝色的部分为物理节点(即数据库的部署地址)

schema:逻辑库

通常对实际应用来说,并不需要知道中间件的存在,业务开发人员只需要知道

数据库的概念,所以数据库中间件可以被看做是一个或多个数据库集群构成的逻辑库

table:逻辑表

既然有逻辑库,那么就会有逻辑表,分布式数据库中,对应用来说,读写数据的表就是逻辑表。逻辑表,可以是数据切分后,分布在一个或多个分片库中,也可以不做数据切分,不分片,只有一个表构成

datanode:分片节点

数据切分后,一个大表被分到不同的分片数据库上面,每个表分片所在的数据库就是分片节点

datahost:节点主机(上图蓝色节点)

数据切分后,每个分片节点(dataNode)不一定都会独占一台机器,同一机器上面可以有多个分片数据库,这样一个或多个分片节点(dataNode)所在的机器就是节点主机(dataHost),为了规避单节点主机并发数限制,尽量将读写压力高的分片节点(dataNode)均衡的放在不同的节点主机(dataHost)。

rule:分片规则

前面讲了数据切分,一个大表被分成若干个分片表,就需要一定的规则,这样按照某种业务规则把数据分到某个分片的规则就是分片规则,数据切分选择合适的分片规则非常重要,将极大的避免后续数据处理的难度。

实战Mycat

为了快速熟悉各种配置,一般直接从git上下载代码,本地用idea打开启动,方便练习一波,小编演示本文就是用的这种方法

mycat的配置其实是蛮简单的,最主要的是熟悉各配置文件的规则。如用户名,密码,分片规则,都是在配置文件中定义的

关于配置文件,conf目录下主要以下三个需要熟悉,要是本地测试用idea打开在resources目录下

小编演示一个最简单的映射配置,找一个数据库服务器,建立3个库,db1,db2,db3,把id为0-500 0000的数据放在db1,id为500 0001到1000 0000的数据放在db2,以此类推

server.xml是Mycat服务器参数调整和用户授权的配置文件(省略了一些配置,后面2个配置文件一样)

<mycat:server xmlns:mycat="http://io.mycat/">

    <user name="root" defaultAccount="true">
        <property name="password">123456</property>
        <property name="schemas">TESTDB</property>
    </user>

</mycat:server>

schema.xml是逻辑库,逻辑表定义以及分片定义的配置文件

<mycat:schema xmlns:mycat="http://io.mycat/">

    <!--逻辑库名-->
    <schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100">
       <!--rule的值和rule.xml的实现对应-->
        <table name="tb_test" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" />
    </schema>

    <!--dataHost可以配置不同主机上的数据库,这里为了演示就配置了一个主机上的不同数据库-->
    <dataNode name="dn1" dataHost="localhost1" database="db1" />
    <dataNode name="dn2" dataHost="localhost1" database="db2" />
    <dataNode name="dn3" dataHost="localhost1" database="db3" />

    <dataHost name="localhost1" maxCon="1000" minCon="10" balance="0"
              writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
        <!--心跳语句-->
        <heartbeat>select user()</heartbeat>
        <!--这里我本地mycat配了一个远程mysql-->
        <writeHost host="hostM1" url="远程mysql的ip地址:3306" user="root"
                   password="2014">
        </writeHost>

    </dataHost>

</mycat:schema>

rule.xml是分片规则的配置文件

<mycat:rule xmlns:mycat="http://io.mycat/">

    <tableRule name="auto-sharding-long">
        <rule>
            <!--根据哪个列进行分片-->
            <columns>id</columns>
            <!--分片规则,连续分片-->
            <algorithm>rang-long</algorithm>
        </rule>
    </tableRule>

    <function name="rang-long"
       <!--分片规则的实现类-->
        class="io.mycat.route.function.AutoPartitionByLong">
        <!--分片规则配置文件-->
        <property name="mapFile">autopartition-long.txt</property>
    </function>

</mycat:rule>

autopartition-long.txt详细的分片策略

# range start-end ,data node index
# K=1000,M=10000.
0-500M=0
500M-1000M=1
1000M-1500M=2

这个配置的意思是,id在0到500w放在第一个分片,以此类推

小编这里用Navicat(数据库连接工具)连接到本地的mycat

主机:localhost 端口:8066 用户名:root(server.xml中配置好的用户名密码) 密码:123456

看到有一个TestDB库,在这个库里面执行建表语句

CREATE TABLE `tb_test` (
  `id` int(11) NOT NULL,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

然后到对应的物理数据库db1,db2,db3上看,3个库都有了这个表。

在逻辑数据库中插入如下三条数据

insert into tb_test (id, name) values (1, "1");
insert into tb_test (id, name) values (5000001, "5000001");
insert into tb_test (id, name) values (10000001, "10000001");

可以看到id为1的数据插入到物理数据库中的db1,id为5000001的数据插入到db2,id为10000001的数据插入到db3

在逻辑数据库中执行如下语句又能拿到这3条记录

select id, name from tb_test

执行如下语句,可以看到mycat从三个数据库中取了记录,LIMIT 100是因为schema.xml中配置了sqlMaxLimit=“100”

explain select id, name from tb_test

有了mycat以后,我们的数据库地址配置成mycat即可,它帮我们做了很多,其他各种分片规则,读写分离等的配置就不再演示,理解整个框架的大概运行流程就行

最后再分享一个知识点,mycat1.5 开始会支持本地 xml 启动,以及从 zookeeper 加载配置转为本地 xml 的两种方式,即原来分享的zookeeper可以用作配置中心

本文分享自微信公众号 - Java识堂(erlieStar),作者:李立敏

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-12-18

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 聊聊登录那些事

    原来分享过一篇文章,Java自定义注解及应用,当时为了能突出重点,直接在url中传了用户的所属角色,并写了一般的做法。加上最近看了一些人的简历,发现神奇的相似,...

    Java识堂
  • 深入理解单例模式

    有一些对象其实我们只需要一个,比方说:线程池,缓存,对话框,处理偏好设置和注册表的对象,日志对象,充当打印机,显卡等设备的驱动程序的对象。事实上,这类对象只能有...

    Java识堂
  • 大白话带你认识JVM

    如果在文中用词或者理解方面出现问题,欢迎指出。此文旨在提及和而不深究,但会尽量效率地把知识点都抛出来

    Java识堂
  • 官方mycat安装入门教程

    cd /usr/local/mycat/conf vim wrapper.conf

    林老师带你学编程
  • 大型网站主从库复制延迟解决方案

    像Facebook、开心001、人人网、优酷、豆瓣、淘宝等高流量、高并发的网站,单点数据库很难支撑得住,WEB2.0类型的网站中使用MySQL的居多,要么用My...

    后端技术探索
  • Redis(6)——zset有序集合

    有序集合保留了集合不能有重复成员的特性,有序集合的元素可以排序,但是它和列表使用索引下标作为排序不同,有序集合给每个元素设置一个分数score 作为排序的依据。

    羊羽shine
  • Mycat - 高可用与负载均衡实现,满满的干货!

        和朋友去吃小龙虾,隔壁桌一个小女孩问妈妈:"妈妈,小龙虾回不了家,它妈妈会不会着急?" 她妈妈愣住了,我扒虾的手停下了,这么善良的问题,怎么下得了口。这...

  • 机器人刚柔耦合动力学建模与应用汇总

    柔性机器人轻量节能, 对环境和目 标的变化具有适应性, 但也存在因 为结构刚度较低而导致的结构振动的问题.现有的绝大多数机器人结构设计是结构刚度最大化, 以减小...

    ZC_Robot机器人技术
  • Java面试集锦

                    特点:以线性方式存储,可以存放重复对象 , 有序【存入与取出顺序一致】

    杨校
  • [Spring] Spring AOP 实现原理剖析(一)

    此处不对这几个术语做冗长的介绍,为了便于记忆,上面的元素在AOP中充当何种角色?我们随着实战的深入慢慢来讲。

    架构探险之道

扫码关注云+社区

领取腾讯云代金券