关于mycat:分布式-实战将业务从-MyCAT-平滑迁移到-dble

作者:肖亚洲 爱可生 DBA 团队成员,负责我的项目中数据库故障与平台问题解决,对数据库高可用与分布式技术情有独钟。 本文起源:原创投稿 *爱可生开源社区出品,原创内容未经受权不得随便应用,转载请分割小编并注明起源。 背景介绍客户环境近期呈现了几次问题,通过探讨后决定进行架构变更,要将 mycat 迁徙到 dble 。要求是:最小变动。 问题整顿联合客户状况与要求列举了下须要思考的事件: 参数设置分片函数数据节点的数据业务SQL问题解决1、参数设置 mycat 端参数如下: <system><property name="defaultSqlParser">druidparser</property><property name="processors">4</property><property name="processorBufferPool">20480000</property><property name="processorBufferLocalPercent">100</property><property name="frontSocketSoRcvbuf">10485760</property><property name="frontSocketSoSndbuf">41943040</property><property name="frontSocketNoDelay">1</property><property name="backSocketSoRcvbuf">41943040</property><property name="backSocketSoSndbuf">10485760</property><property name="backSocketNoDelay">1</property><property name="maxPacketSize">2048576000</property><property name="memoryPageSize">100m</property></system>针对该参数 DBLE 侧倡议如下: defaultSqlParser 、memoryPageSize 、processorBufferLocalPercent 已于 dble 中被废除,可不用配置processorBufferPool 参数名变更为 bufferPoolPageSize其余的参数在 dble 中可在 server.xml 中与 mycat 保持一致配置信息,局部参数信息详情可见:https://github.com/actiontech...留神:mycat 和 dble 的内存治理配置有较大不同。比方:没有 threadlocal 概念。倡议浏览以下文档:https://actiontech.github.io/... 2、分片函数 查看 mycat 分片规定: <function name="mod-long" class="io.mycat.route.function.PartitionByMod"><!-- how many data nodes --><property name="count">8</property></function>间接采纳 DBLE 的 hash 算法测试发现局部数据查问报错: 查看表内数据状况: 样例数据:20210810143211157000000000036 字段:user_code为varchar(32) 查看 mycat 环境分片规定:采纳的分片算法为 mod-long 。而 long 的取值范畴是 -9223372036854774808~9223372036854774807 ,查问出错的 20210810143211157000000000036 超过了 long 的取值范畴。所以能够得出 mycat 针对这一非凡状况做了非凡解决。 ...

July 25, 2022 · 1 min · jiezi

关于mycat:mycat-中间件安装与使用

一,什么是mycat 一个彻底开源的,面向企业应用开发的大数据库集群 反对事务、ACID、能够代替MySQL的加强版数据库 一个能够视为MySQL集群的企业级数据库,用来代替低廉的Oracle集群 一个交融内存缓存技术、NoSQL技术、HDFS大数据的新型SQL Server 联合传统数据库和新型分布式数据仓库的新一代企业级数据库产品 一个新鲜的数据库中间件产品 以上是官网阐明。其实就是数据库的连接池。mysql proxy也是一种连接池,然而效率很低。 二,mycat 装置 1,下载地址mycat http://dl.mycat.io/ 2,装置mycat # tar zxvf Mycat-server-1.6-RELEASE-20161028204710-linux.tar.gz -C /usr/local/三,配置mycat 1,配置server.xml # vim /usr/local/mycat/conf/server.xml //增加以下内容<user name= "user" > //mycat 用户名 <property name= "password" >user< /property > //mycat 明码 <property name= "schemas" >mytest< /property > //mycat 虚构数据库名 <property name= "readOnly" > true < /property > // 只读 < /user > <user name= "tankzhang" > <property name= "password" >admin< /property > <property name= "schemas" >mytest< /property > < /user >在这里要留神,默认的虚构数据名是TESTDB,如果schema.xml外面没有配置testdb,那就要把testdb改成schema.xml外面有的虚构数据名。这里定义的用户名和明码,虚构数据库名,并不是在mysql中实在存在的。 ...

February 14, 2022 · 3 min · jiezi

关于mycat:如何优雅的遍历Mycat分库表

Mycat作为数据库的中间件,被许多我的项目用来做关系型数据库的分库分表;我目前所在的我的项目因为业务和数据量的增长,也应用Mycat来进行分库分表。最近因为一个需要须要遍历一个分库表的数据,对数据进行相应的业务操作。 例如咱们有一个user表,该表被分为16个库,那么能够如何高效而优雅的实现对user表中所有数据的遍历呢? 应用表的主键ID分页遍历这种办法是最直接了当,也是最容易想到的;例如应用如下的sql语句分批查问表中的数据: select * from user order by id limit a, 10000; 该sql语句以每批1万条纪录的形式遍历user表,每次查问应用上次id的最大值替换a的值,直到没有纪录返回。 这种实现诚然可行,但其最大的问题在于效率,每次查问都会从所有的分库中各获取1万条纪录,那么Mycat总共失去16万行的数据,java培训再进行排序,取前1万条记录返回。如此遍历下来,总共查问了user表总记录数16倍的数据量,而且还进行排序,这种耗费无疑是微小的。如果分库数量更多,资源的耗费和节约更重大。此计划在性能上是不可承受的。 独自遍历每个分库Mycat提供了通过注解的形式指定从某个分库执行sql语句,注解语法为 /!mycat:dataNode = ${dn} / 其中,dn为分片节点的名称;这样,咱们就能够对上述的查问语句进行革新,指定分片节点进行查问遍历,如: /!mycat:dataNode = dn1 / select * from user order by id limit a, 10000; 应用以上的sql语句,先对名称为dn1分片节点的数据进行遍历,而后批改注解中的节点名称,持续遍历其余节点;把所有的节点都遍历实现后,user表的数据也就遍历结束。这种遍历形式大大提高了效率,不论user表被分成多少个分片,所有的数据都只被读取一次。 此办法的不足之处是必须当时获取所有分片节点的名称,写在代码或配置文件中;当分片进行扩容时,分片节点数量减少,配置文件或代码就必须做对应的批改,否则就获取不到表中所有的数据。 主动获取所有分片节点名称那么,有没有一种办法能够实时主动的取得分库表的所有分片节点名称呢? Mycat除了数据操作端口(默认8066)外,还提供了治理端口(默认9066),通过连贯该端口,能够查看Mycat的运行数据并做治理操作。通过治理端口登录Mycat后,能够通过show @@datanode命令查看分片节点信息,如下所示(后果已略去局部列):NAME列即为节点名称,只有获取到该列所有的值即可。然而治理端口有较高的操作权限,个别状况下出于平安起因,该端口仅运维有权限登录操作,利用无权拜访。 还有其余更好方法吗? 这时想到了执行打算, 对,执行打算! Mycat反对explain命令查看sql语句的执行打算,对于分库表,会返回sql语句执行须要查问哪些节点;这样咱们就能够通过结构一个须要查问所有节点的sql语句来获取全副节点信息,例如通过如下所示的语句:DATA_NODE列即为节点名称;节点取到之后,剩下的就简略了,无非是对各个节点独自遍历。这种形式通过查问一次Mycat就取得了所有分片节点的名称,java培训而且数据库的连贯也能够复用,真正做到了既高效又优雅。 当然,以上的解决方案是限定于Mycat与MySQL的范畴之内,如果数据能够同步到NoSQL中,那又是从另外的角度去解决了。

January 13, 2022 · 1 min · jiezi

关于mycat:数据库中间件Mycat-权威指南Mycat-实战笔记双管齐下

前阵子温习了一下音讯中间件MQ系列的知识点,突然想起还有个数据库中间件——MyCat! Mycat 是一个开源的分布式数据库系统,是一个实现了MySQL协定的服务器,前端用户能够把它看作是一个数据库代理,用MySQL客户端工具和命令行拜访,而其后端能够用MySQL原生协定与多个MySQL服务器通信,也能够用JDBC协定与大多数支流数据库服务器通信,其外围性能是分表分库,行将一个大表程度宰割为N个小表,存储在后端MySQL服务器里或者其余数据库里。 为了深刻学习一下Mycat数据库中间件,我从敌人那里整了两个文档:Mycat 权威指南 + Mycat 实战笔记,实践与技术联合,并行不悖! 全文所提及介绍的《Mycat 权威指南 + Mycat 实战笔记》是两份很不错的Mycat学习指南,因篇幅起因,大部分内容以图片展示,但都有整顿好的 原件pdf(点击下载)首先,学习一下数据库中间件:Mycat 权威指南 Mycat 权威指南分 四大类 内容:入门篇+ 高级进阶篇 + 生产实践篇 + 开发篇 Mycat 权威指南 入门篇 有以下 12 篇章节的内容: 第一章 概述第二章 Mycat前世今生第三章 Mycat中的概念第四章 疾速入门第五章 日志剖析第六章 Mycat防火墙配置第七章 Mycat的配置第八章 Mycat的分片join第九章 全局序列号第十章 Mycat 分片规定第十—章 常见问题与解决方案第十二章 Mycat性能测试指南 Mycat 权威指南 高级进阶篇 蕴含 11 篇章节的内容: 第一章 读写拆散第二章 高可用与集群第三章 事务反对第四章 Mycat SQL拦挡机制第五章 Mycat注解第六章 MyCAT反对的Catlet实现第七章 jdbc多数据库反对第八章 治理命令与监控第九章 压缩协定反对第十章 Mycat-Web第十—章 MyCAT对存储过程的反对 Mycat 权威指南 生产实践篇 有以下 8 篇章节的内容: ...

June 3, 2021 · 1 min · jiezi

关于mycat:93

February 17, 2021 · 0 min · jiezi

关于mycat:SpringMVCSpringSessionMycat-实现web项目的session共享和数据库高可用

现有零碎架构Springmvc+cas+nginx+nginx_upstream_jvm_route 插件实现负载并且实现session共享: 原理: 1:一开始申请过去,没有带session信息,jvm_route就依据round robin的办法,发到一台tomcat下面(依据cas信息初始化用 户信息存入session) 2:tomcat增加上session 信息,并返回给客户。 3. 用户再次申请,jvm_route看到session中有后端服务器的名称,它就把申请转到对应的服务器上 毛病: 1:并没有真正的实现负载,每次都负载到雷同机器上;当用户拜访时通过雷同的代理地址过去时会造成单节点压力 2:nginx_upstream_jvm_route 插件最高只反对到nginx1.14版本左右,nginx1.14版本有破绽,架构无奈降级。 新架构组成SpringMvc+SpringSession+mycat+mysql主主模式实现session共享和数据库高可用 SpringSession:把servlet容器实现的httpSession替换为spring-session;将session信息存入redis;Mysql:mysql主主模式,进行数据的实时双向同步Mycat:mysql中间件,治理mysql,能够做到mysql的读写拆散以及数据库故障时主动切换;利用直连mycat;Redis:应用哨兵模式,实现主从主动切换 备注:如果复用老的我的项目代码,而且旧我的项目应用httpSession 进行存储session信息的话,根本零革新。Spring-Session配置Spring-session及redis的依赖包Spring-session配置Redis单节点模式此配置是关键点,引了redis单 机依赖包; 设置了session在redis中的生效 工夫; 定义了cookie中session的名称Redis哨兵模式此配置是关键点,引了redis哨兵依赖包; 设置了session在redis中的生效工夫; 定义了cookie中session的名称Mysql主从配置第一个mysql配置; 此处参数重要的是MySQL主键自增的差值第二个MySQL配置,次要是起始值不一样Mycat根本信息mycat免装置,下载后间接解压即可Schema 配置的是数据库中的表, 有很多种属性且用处不一,此处比较简单Mycat注意事项:switchtype=2时,heartBeat节点值为show slave status,查看主从状态, writeHost中的user 必须具备 replicationclient 和super权限中的一种,不然会报错 调配权限:grant replicationclient , super on . to mas;

December 28, 2020 · 1 min · jiezi

关于mycat:MycatMycat核心开发者带你轻松掌握Mycat路由转发

写在后面相熟Mycat的小伙伴都晓得,Mycat一个很重要的性能就是路由转发,那么,这篇文章就带着大家一起来看看Mycat是如何进行路由转发的,好了,不多说了,咱们间接进入主题。环境筹备软件版本操作系统:CentOS-6.8 JDK版本:jdk1.8 Mycat版本:Mycat-server-1.6 MySQL:5.7 留神:这里,我将Mycat和MySQL装置在同一台虚拟机(IP:192.168.209.140 主机名为:binghe140),大家也能够将Mycat和MySQL装置到不同的主机上,测试成果是一样的。 创立物理库mysql -uroot -proot -h192.168.209.140 -P3306drop database if exists db1;create database db1;drop database if exists db2;create database db2;drop database if exists db3;create database db3;配置Mycatschema.xml配置<?xml version="1.0"?><!DOCTYPE mycat:schema SYSTEM "schema.dtd"><mycat:schema xmlns:mycat="http://org.opencloudb/" > <schema name="binghe" checkSQLschema="false" sqlMaxLimit="100"> <table name="travelrecord" dataNode="dn1,dn2,dn3" rule="auto-sharding-long"></table> </schema> <!-- <dataNode name="dn1$0-743" dataHost="localhost1" database="db$0-743" /> --> <dataNode name="dn1" dataHost="localhost1" database="db1" /> <dataNode name="dn2" dataHost="localhost1" database="db2" /> <dataNode name="dn3" dataHost="localhost1" database="db3" /> <!--<dataNode name="dn4" dataHost="sequoiadb1" database="SAMPLE" /> <dataNode name="jdbc_dn1" dataHost="jdbchost" database="db1" /> <dataNode name="jdbc_dn2" dataHost="jdbchost" database="db2" /> <dataNode name="jdbc_dn3" dataHost="jdbchost" 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> <!-- can have multi write hosts --> <writeHost host="hostM1" url="127.0.0.1:3306" user="root" password="root"></writeHost> <writeHost host="hostM2" url="127.0.0.1:3306" user="root" password="root"></writeHost> <!--<writeHost host="hostS1" url="localhost:3316" user="root"--> <!--password="123456" />--> <!-- <writeHost host="hostM2" url="localhost:3316" user="root" password="123456"/> --> </dataHost></mycat:schema>server.xml配置<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mycat:server SYSTEM "server.dtd"><mycat:server xmlns:mycat="http://org.opencloudb/"> <system> <property name="defaultSqlParser">druidparser</property> </system> <user name="binghe"> <property name="password">binghe.123</property> <property name="schemas">binghe</property> </user> <user name="test"> <property name="password">test</property> <property name="schemas">binghe</property> <property name="readOnly">true</property> </user></mycat:server>rule.xml配置<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mycat:rule SYSTEM "rule.dtd"><mycat:rule xmlns:mycat="http://org.opencloudb/"> <tableRule name="rule1"> <rule> <columns>id</columns> <algorithm>func1</algorithm> </rule> </tableRule> <tableRule name="rule2"> <rule> <columns>user_id</columns> <algorithm>func1</algorithm> </rule> </tableRule> <tableRule name="sharding-by-intfile"> <rule> <columns>sharding_id</columns> <algorithm>hash-int</algorithm> </rule> </tableRule> <tableRule name="auto-sharding-long"> <rule> <columns>id</columns> <algorithm>rang-long</algorithm> </rule> </tableRule> <tableRule name="mod-long"> <rule> <columns>id</columns> <algorithm>mod-long</algorithm> </rule> </tableRule> <tableRule name="sharding-by-murmur"> <rule> <columns>id</columns> <algorithm>murmur</algorithm> </rule> </tableRule> <tableRule name="sharding-by-month"> <rule> <columns>create_date</columns> <algorithm>partbymonth</algorithm> </rule> </tableRule> <tableRule name="latest-month-calldate"> <rule> <columns>calldate</columns> <algorithm>latestMonth</algorithm> </rule> </tableRule> <tableRule name="auto-sharding-rang-mod"> <rule> <columns>id</columns> <algorithm>rang-mod</algorithm> </rule> </tableRule> <tableRule name="jch"> <rule> <columns>id</columns> <algorithm>jump-consistent-hash</algorithm> </rule> </tableRule> <function name="murmur" class="org.opencloudb.route.function.PartitionByMurmurHash"> <property name="seed">0</property> <property name="count">2</property> <property name="virtualBucketTimes">160</property> </function> <function name="hash-int" class="org.opencloudb.route.function.PartitionByFileMap"> <property name="mapFile">partition-hash-int.txt</property> </function> <function name="rang-long" class="org.opencloudb.route.function.AutoPartitionByLong"> <property name="mapFile">autopartition-long.txt</property> </function> <function name="mod-long" class="org.opencloudb.route.function.PartitionByMod"> <!-- how many data nodes --> <property name="count">3</property> </function> <function name="func1" class="org.opencloudb.route.function.PartitionByLong"> <property name="partitionCount">8</property> <property name="partitionLength">128</property> </function> <function name="latestMonth" class="org.opencloudb.route.function.LatestMonthPartion"> <property name="splitOneDay">24</property> </function> <function name="partbymonth" class="org.opencloudb.route.function.PartitionByMonth"> <property name="dateFormat">yyyy-MM-dd</property> <property name="sBeginDate">2020-01-01</property> </function> <function name="rang-mod" class="org.opencloudb.route.function.PartitionByRangeMod"> <property name="mapFile">partition-range-mod.txt</property> </function> <function name="jump-consistent-hash" class="org.opencloudb.route.function.PartitionByJumpConsistentHash"> <property name="totalBuckets">3</property> </function></mycat:rule>登录Mycat登录Mycat命令行输出以下命令登录Mycat ...

November 14, 2020 · 4 min · jiezi

关于mycat:Mycat数据库中间件

1.非分片字段查问Mycat中的路由后果是通过分片字段和分片办法来确定的。例如下图中的一个Mycat分库计划: 依据 tt_waybill 表的 id 字段来进行分片分片办法为 id 值取 3 的模,依据模值确定在DB1,DB2,DB3中的某个分片 如果查问条件中有 id 字段的状况还好,查问将会落到某个具体的分片。例如: mysql>select * from tt_waybill where id = 12330;此时Mycat会计算路由后果 12330 % 3 = 0 –> DB1并将该申请路由到DB1下来执行。如果查问条件中没有 分片字段 条件,例如: mysql>select * from tt_waybill where waybill_no =88661;此时Mycat无奈计算路由,便发送到所有节点上执行: DB1 –> select * from tt_waybill where waybill_no =88661;DB2 –> select * from tt_waybill where waybill_no =88661;DB3 –> select * from tt_waybill where waybill_no =88661;如果该分片字段抉择度高,也是业务罕用的查问维度,个别只有一个或极少数个DB节点命中(返回后果集)。示例中只有3个DB节点,而理论利用中的DB节点数远超过这个,如果有50个,那么前端的一个查问,落到MySQL数据库上则变成50个查问,会极大耗费Mycat和MySQL数据库资源。 如果设计应用Mycat时有非分片字段查问,请思考放弃! 2.分页排序先看一下Mycat是如何解决分页操作的,如果有如下Mycat分库计划:一张表有30份数据分布在3个分片DB上,具体数据分布如下 DB1:[0,1,2,3,4,10,11,12,13,14]DB2:[5,6,7,8,9,16,17,18,19]DB3:[20,21,22,23,24,25,26,27,28,29](这个示例的场景中没有查问条件,所以都是全分片查问,也就没有假设该表的分片字段和分片办法) ...

October 20, 2020 · 2 min · jiezi

关于mycat:mycat分库分表初体验

一.Mycat分表配置导入mycat源码:如下 mycat有三个重要的配置文件 1.server.xml <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mycat:server SYSTEM "server.dtd"><mycat:server xmlns:mycat="http://io.mycat/"> <system> <property name="charset">utf8</property> <property name="nonePasswordLogin">0</property> <!-- 0是须要明码登陆、1则不须要明码登陆 .默认0,设置1则须要指定默认账户--> <property name="ignoreUnknownCommand">0</property><property name="useHandshakeV10">1</property> <property name="removeGraveAccent">1</property> <property name="useSqlStat">0</property> <!-- 1为开启实时统计、0为敞开 --> <property name="useGlobleTableCheck">0</property> <!-- 1为开启全局表一致性检测、0为敞开 --> <property name="sqlExecuteTimeout">300</property> <!-- SQL 执行超时 单位:秒--> <!-- 0:文件形式 1:数据库形式 2:工夫戳形式 3:zk --> <property name="sequenceHandlerType">0</property> <!--<property name="sequnceHandlerPattern">(?:(s*nexts+values+fors*MYCATSEQ_(w+))(,|)|s)*)+</property> INSERT INTO `travelrecord` (`id`,user_id) VALUES ('next value for MYCATSEQ_GLOBAL',"xxx"); --> <!--必须带有MYCATSEQ_或者 mycatseq_进入序列匹配流程 留神MYCATSEQ_有空格的状况--> <property name="sequnceHandlerPattern">(?:(s*nexts+values+fors*MYCATSEQ_(w+))(,|)|s)*)+</property> <property name="subqueryRelationshipCheck">false</property> <!-- 子查问中存在关联查问的状况下,查看关联字段中是否有分片字段 .默认 false --> <property name="sequenceHanlderClass">io.mycat.route.sequence.handler.HttpIncrSequenceHandler</property> <!-- <property name="useCompression">1</property>--> <!--1为开启mysql压缩协定--> <!-- <property name="fakeMySQLVersion">5.6.20</property>--> <!--设置模仿的MySQL版本号--> <!-- <property name="processorBufferChunk">40960</property> --> <!-- <property name="processors">1</property> <property name="processorExecutor">32</property> --> <!--默认为type 0: DirectByteBufferPool | type 1 ByteBufferArena | type 2 NettyBufferPool --> <property name="processorBufferPoolType">0</property><!--分布式事务开关,0为不过滤分布式事务,1为过滤分布式事务(如果分布式事务内只波及全局表,则不过滤),2为不过滤分布式事务,然而记录分布式事务日志--> <property name="handleDistributedTransactions">0</property> <!--off heap for merge/order/group/limit 1开启 0敞开 --> <property name="useOffHeapForMerge">0</property> <!--单位为m --> <property name="memoryPageSize">64k</property> <!--单位为k --> <property name="spillsFileBufferSize">1k</property> <property name="useStreamOutput">0</property> <!--单位为m --> <property name="systemReserveMemorySize">384m</property> <!--是否采纳zookeeper协调切换 --> <property name="useZKSwitch">false</property> <property name="strictTxIsolation">false</property> <!--如果为0的话,波及多个DataNode的catlet工作不会跨线程执行--> <property name="parallExecute">0</property> </system> <user name="user"> <property name="password">user</property> <property name="schemas">TESTDB</property> <property name="readOnly">true</property> <property name="defaultSchema">TESTDB</property> </user>--> <!--增加root用户,拜访enjoyDB逻辑库--> <user name="root"> <property name="password">123456</property> <property name="schemas">enjoyDB</property> </user></mycat:server>2.schema.xml(逻辑库配置) ...

September 20, 2020 · 2 min · jiezi

Mycat安装与配置

一、准备工作3台CnetOS7服务器,我这里是安装在虚拟机上的三台服务器,ip地址分别为192.168.0.150,192.168.0.151,192.168.0.152。 150用来安装Mycat,151和152提前安装好Mysql(安装步骤可参考https://segmentfault.com/a/11...。 在两台MySQL服务器上分别创建school_151,school_152,并在这两个库中创建相同的表students。 DROP TABLE IF EXISTS `students`;CREATE TABLE `students` ( `id` int NOT NULL, `name` varchar(255) NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;二、安装Mycat 1、从Mycat官网下载安装包:http://www.mycat.org.cn/ 2、主页往下翻选择下载版本,这里选择的是1.6.7.1版本 3、选择Linux版本,开始下载 4、将下载好的文件上传到服务器上,我这里的服务器是虚拟机上CentOS7系统 5、解压文件 tar -zxvf Mycat-server-1.6.7.4-test-20191022170541-linux.tar.gz 6、解压完毕,将解压后的文件转移到/usr/local目录下(这一步可做可不做) mv mycat/ /usr/local/ 三、配置Mycat 1、进入mycat目录下的conf目录 2、修改server.xml文件 vim server.xml server.xml里面配置了system、firewall和user,我们现在需要修改一下user里面的schemas,这是mycat的逻辑库名,需跟schema.xml里面的schema标签的name对应,如果需要配置多个schema,可以用,隔开。我们将schemas设置成school,保存并退出。 3、配置schema.xml vim schema.xml 首先配置dataHost,我们这里有两台Mysql服务器,所以需配置两个dataHost。 将两台Mysql服务器的url,user,password分别配置到dataHost下的writeHost,先将writeHost下的readHost注释掉。为了便于记忆这里将两个dataHost的name分别改为dh151,dh152。接下来配置dataNode。 我们配置两个dataNode,dataHost属性分别对应前面配置的两个dataHost的name,database属性对应两台Mysql服务器中的school_151,school_152,并将name分别设置为dn151,dn152。 最后配置schema。 schema的name属性对应server.xml中配置的schema,shcema下面的table标签的name属性与Mysql中的表名一致,dataNode属性对应上面配置的两个dataNode标签的name,rule属性表示的是分片规则,这里使用的是auto-sharding-long。这里只保留一个table标签,将其他的table标签注释掉。保存并退出。 4、修改autopartition-long.txt。因为我们配置的分片规则auto-sharding-long是根据id范围进行分片的,autopartition-long.txt默认配置了三个分片范围,我们只有两台Mysql进行分片,所以这里将最下面的1000M-1500M=2注销掉。 vim autopartition-long.txt 5、启动Mycat。进入mycat目录下的bin目录,输入以下命令。 ./mycat console console表示将启动信息显示在控制台,如果不需要显示启动信息,可以用: ./mycat start四、测试 ...

June 10, 2020 · 1 min · jiezi

MyCat踩坑记录

mycat在使用过程中觉得非常好,很实用,但使用过程中也碰到了不少问题,踩了不少坑,现做一些记录,历史的一些坑已经忘记了,从现在开始吧 全局表数据不同步这个问题比较常见,而且比较初级。现象为:连mycat的查询的时候多次执行SQL,返回的结果数量不一致或者数据不一致。一般原因为:查询的表中有全局表,且在全局表中的数据不一致造成 Unknown command重现现象为:项目中使用了mybatisplus(推荐,封装了mybatis的工具),在使用mybatisplus的saveOrUpdateBatch方法时,数据量超过一定的数据量时报 Unknown command,详细异常信息如下: 发生异常,org.apache.ibatis.exceptions.PersistenceException: ### Error flushing statements. Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Unknown command### Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Unknown command at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30) at org.apache.ibatis.session.defaults.DefaultSqlSession.flushStatements(DefaultSqlSession.java:254) at com.baomidou.mybatisplus.extension.service.impl.ServiceImpl.saveOrUpdateBatch(ServiceImpl.java:179) at com.baomidou.mybatisplus.extension.service.IService.saveOrUpdateBatch(IService.java:76) at com.gwall.core.service.impl.DstaskServiceImpl.saveOrUpdateCids(DstaskServiceImpl.java:148) at com.gwall.core.service.impl.DstaskServiceImpl$$FastClassBySpringCGLIB$$bf68f571.invoke(<generated>) at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:749) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:100) at com.gwall.core.common.aspects.GwallRequestIdAspect.requestIdHandle(GwallRequestIdAspect.java:46) at sun.reflect.GeneratedMethodAccessor83.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644) at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:633) at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:295) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688) at com.gwall.core.service.impl.DstaskServiceImpl$$EnhancerBySpringCGLIB$$e5a8ece4.saveOrUpdateCids(<generated>) at com.gwall.core.service.impl.base.AbstractServiceImpl.batchSaveCids(AbstractServiceImpl.java:727) at com.gwall.core.service.impl.base.AbstractServiceImpl$$FastClassBySpringCGLIB$$3afa95e6.invoke(<generated>) at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:749) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:100) at com.gwall.core.common.aspects.GwallRequestIdAspect.requestIdHandle(GwallRequestIdAspect.java:46) at sun.reflect.GeneratedMethodAccessor83.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644) at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:633) at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:295) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688) at com.gwall.base.service.impl.SuinServiceImpl$$EnhancerBySpringCGLIB$$946e8cf5.batchSaveCids(<generated>) at com.gwall.core.controller.AbstractController.batchSaveCids(AbstractController.java:500) at com.gwall.core.controller.AbstractController$$FastClassBySpringCGLIB$$9007052b.invoke(<generated>) at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:749) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:100) at com.gwall.core.common.aspects.GwallRequestIdAspect.requestIdHandle(GwallRequestIdAspect.java:46) at sun.reflect.GeneratedMethodAccessor83.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:644) at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:633) at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688) at com.gwall.base.controller.SuinController$$EnhancerBySpringCGLIB$$3fb28a8b.batchSaveCids(<generated>) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:892) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797) at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1039) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005) at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:908) at javax.servlet.http.HttpServlet.service(HttpServlet.java:660) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882) at javax.servlet.http.HttpServlet.service(HttpServlet.java:741) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at com.dianping.cat.servlet.CatFilter.logTransaction(CatFilter.java:255) at com.dianping.cat.servlet.CatFilter.doFilter(CatFilter.java:93) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:109) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:109) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:109) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.session.web.http.SessionRepositoryFilter.doFilterInternal(SessionRepositoryFilter.java:151) at org.springframework.session.web.http.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:85) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:109) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408) at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:853) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1587) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run(Thread.java:745)将数据库连接改为mysql 3306端口时,则正常,定位为mycat问题,咨询大佬后给出的方案是将 数据库连接字符串?后的部分去掉。具体如原数据库连接字符串:jdbc:mysql://host:8066/db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&useAffectedRows=true&autoReconnect=true&failOverReadOnly=false修改后:jdbc:mysql://host:8066/db问题修复,暂时只做问题记录,未研究具体原因 ...

May 26, 2020 · 2 min · jiezi

分布式-dble-用-jumpstringhash-替换-Mycat-一致性-hash-原因解析

作者:爱可生开源社区背景MyCat 对于字符串类型为分片字段的数据,有三种分片模式,分别是:模值 hash(求模法),jumpstringhash(跳跃法),一致性 hash(环割法)DBLE 对于 hash 算法选取方面,除了继承 MyCat 的模值 hash,并没有延续使用 MyCat 的一致性 hash,而是选取了另一种流行的算法 —— jumpstringhash。介绍下面对于环割法(一致性 hash)及跳跃法(jumpstringhash)的原理、特性及优缺点进行简单的介绍。 环割法(一致性 hash)环割法的原理如下: 初始化的时候生成分片数量X × 环割数量 N 的固定方式编号的字符串,例如 SHARD-1-NODE-1,并计算所有 X×N 个字符串的所有 hash 值。将所有计算出来的 hash 值放到一个排序的 Map 中,并将其中的所有元素进行排序。输入字符串的时候计算输入字符串的 hash 值,查看 hash 值介于哪两个元素之间,取小于 hash 值的那个元素对应的分片为数据的分片。特点缺点随机性强初始化耗时长,内存消耗较高,需要进行大量数据排序,分片消耗高跳跃法(jumpstringhash)跳跃法的原理如下: 根据公式: 将数据落在每一个节点的概率进行平均分配。 对于输入的字符串进行计算 hash 值,通过判断每次产生的伪随机值是否小于当前判定的节点 1/x,最终取捕获节点编号最大的作为数据的落点。在实际使用中使用倒数的方法从最大节点值进行反向判断,一旦当产生的伪随机值大于 x 则判定此节点 x 作为数据的落点。特点内存消耗小,均衡性高,计算量相对较小数据比较下面将通过测试对环割法和跳跃法的性能及均衡性进行对比,说明 DBLE 为何使用跳跃法代替了环割法。 数据源:现场数据 350595 条测试经过: 通过各自的测试方法执行对于测试数据的分片任务。测试方法:记录分片结果的方差;记录从开始分片至分片结束的时间;记录分片结果与平均数的最大差值。由于在求模法 PartitionByString 的方法中要求分片的数量是 1024 的因数,所以测试过程只能使用 2 的指数形式进行测试,并在 PartitionByString 方法进行测试的时候不对于 MAC 地址进行截断,取全量长度进行测试。测试结果: 使用不同方法时,方差随分片数量的情况变化表| 分片数量方差| 环割法1000片 | 环割法10000片 | 跳跃法 || ----- | ----- | ----- | ----- | | 4 | 4484780 | 812418 | 315703 || 8 | 601593 | 545599 | 315587 || 16 | 453694 | 131816 | 67018 || 32 | 213856 | 74360 | 86125 || 64 | 69313 | 46618 | 37939 || 128 | 24329 | 26415 | 25429 | ...

September 10, 2019 · 2 min · jiezi

基于Mycat的分库分表

数据库分库分表有两个种方式垂直切分 一个数据库由很多表的构成,每个表对应着不同的业务,垂直切分是指按照业务将表进行分类,分布到不同的数据库上面,这样也就将数据或者说压力分担到不同的库上面。优点: - 拆分后业务清晰,拆分规则明确;- 系统之间整合或扩展容易;- 数据维护简单。**缺点:** - 部分业务表无法 join,只能通过接口方式解决,提高了系统复杂度; - 受每种业务不同的限制存在单库性能瓶颈,不易数据扩展跟性能提高; - 事务处理复杂。由于垂直切分是按照业务的分类将表分散到不同的库,所以有些业务表会过于庞大,存在单库读写与存储瓶颈,所以就需要水平拆分来做解决。 水平切分相对于垂直拆分,水平拆分不是将表做分类,而是按照某个字段的某种规则来分散到多个库之中,每个表中包含一部分数据。简单来说,我们可以将数据的水平切分理解为是按照数据行的切分,就是将表中的某些行切分到一个数据库,而另外的某些行又切分到其他的数据库中,如图: ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190804210504414.png)**优点:** - 拆分规则抽象好,join 操作基本可以数据库做; - 不存在单库大数据,高并发的性能瓶颈; - 应用端改造较少; - 提高了系统的稳定性跟负载能力。**缺点:** - 拆分规则难以抽象; - 分片事务一致性难以解决; - 数据多次扩展难度跟维护量极大; - 拆分规则难以抽象; - 分片事务一致性难以解决;等等还有很多。具体的就参照Mycat的问题说明。还有Mycat支持的分片策略:http://www.mycat.io/document/... 基于Mycat用枚举分片算法实现分库创建三个数据库修改配置文件,如果觉得手动输入比较慢,可以直接复制然后修改库名和连接信息就可以 schema.xml配置<mycat:schema xmlns:mycat="http://io.mycat/"> <!-- testdb是mycat的逻辑库名称,链接需要用的 --> <schema name="testdb" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1"> <!--rule对应rule.xml name的名称 --> <table name="t_user" dataNode="dn1,dn2,dn3" rule="role2" /> </schema> <!-- database 是MySQL数据库的库名 --> <dataNode name="dn1" dataHost="localhost1" database="t_user1" /> <dataNode name="dn2" dataHost="localhost1" database="t_user2" /> <dataNode name="dn3" dataHost="localhost1" database="t_user3" /> <dataHost name="localhost1" maxCon="1000" minCon="10" balance="3" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100"> <heartbeat>select user()</heartbeat> <!-- 可以配置多个主从 --> <writeHost host="hostM1" url="192.168.100.131:3306" user="root" password="root"> <!-- 可以配置多个从库 --> <readHost host="hostS2" url="192.168.100.132:3306" user="root" password="root" /> </writeHost> </dataHost></mycat:schema>rule.xml配置<mycat:rule xmlns:mycat="http://io.mycat/"> <tableRule name="role2"> <rule> <!-- 根据传入的name字段来判断存入那个库 --> <columns>name</columns> <!-- 指定的规则 --> <algorithm>hash-int</algorithm> </rule> </tableRule> <function name="hash-int" class="io.mycat.route.function.PartitionByFileMap"> <!-- 指定的规则枚举文件 --> <property name="mapFile">partition-hash-int.txt</property> <!-- key 如果是数值就写0,不是就写1 --> <property name="type">1</property> <!-- 如果都不符合,默认存入1节点库中 --> <property name="defaultNode">1</property> </function></mycat:rule>partition-hash-int.txt配置 key是name对应的值,value指的是存在哪个库中hefei=0nanjing=1bengbu=2server.xml配置<mycat:server xmlns:mycat="http://io.mycat/"> <!-- 读写都可用的用户 --> <user name="root" defaultAccount="true"> <property name="password">root</property> <property name="schemas">testdb</property> </user> <!-- 只读用户 --> <user name="user"> <property name="password">user</property> <property name="schemas">testdb</property> <property name="readOnly">true</property> </user></mycat:server>这样配置完成,然后启动Mycat。使用Navicat连接的时候还是会出现表找不到的原因通过日志可以看到,每次查询表的时候都会携带逻辑库的名字。所以导致没有查询到。这个问题我还没有解决。只能通过写sql来查询和新增了。新增四个地区名称 ...

September 8, 2019 · 1 min · jiezi

WhatTomcat竟然也算中间件

关于 MyCat 的铺垫文章已经写了两篇了: MySQL 只能做小项目?松哥要说几句公道话!北冥有 Data,其名为鲲,鲲之大,一个 MySQL 放不下!今天是最后一次铺垫,后面就可以迎接大 Boss 了! <!--more--> 本来今天就该讲 MyCat 了,但是我发现还有一个概念值得和大家聊一下,那就是 Java 中间件! 因为 MyCat 是一个分布式数据库中间件,要理解 MyCat ,那你就得先知道到底什么是中间件! 松哥去年在一次外训中专门讲过中间件,本来想直接和大家分享一下讲稿,但是没找到,所以又动手敲了下。 中间件简介说起中间件,很多人首先想到的就是消息中间件,那么除了消息中间件呢?其实我们日常开发中,接触到的中间件太多了,我们来看维基百科上的一段介绍: 中间件(英语:Middleware),又译中间件、中介层,是提供系统软件和应用软件之间连接的软件,以便于软件各部件之间的沟通。在现代信息技术应用框架如 Web 服务、面向服务的体系结构等项目中应用比较广泛。如数据库、Apache 的 Tomcat ,IBM 公司的 WebSphere ,BEA 公司的 WebLogic 应用服务器,东方通公司的 Tong 系列中间件,以及 Kingdee 公司的等都属于中间件。看到这个,你可能会大吃一惊,原来我们不知不觉不知不觉中已经用过这么多中间件了!甚至连 Tomcat 也是一个中间件! 中间件,顾名思义,就是连接在两个软件之间的东西,是软件之间的一个粘合剂,一个胶水一样的东西。它位于操作系统和我们的应用程序之间,可以让开发者方便地处理通信、输入和输出,使开发者能够专注于自己的业务逻辑开发。 这么一说,好像 Tomcat 确实还有点像中间件!位于我们的操作系统和应用程序之间! 中间件分类中间件有很多,早在 1998 年 IDC 公司就将中间件分成了 6 大类,国内 2005 年之前出版的中间件相关的书上,很多都是按照这 6 大类来分的,分别是: 终端仿真/屏幕转换数据访问中间件(UDA)远程过程调用中间件(RPC)消息中间件(MOM)交易中间件(TPM)对象中间件这里边除了消息中间件和交易中间件大家可能听说过之外,其他的中间件估计都很少听说,这是因为时代在变化,有的中间件慢慢被淘汰了(例如 终端仿真/屏幕转换 中间件),有的则慢慢合并到其他框架中去了(例如 远程过程调用中间件)。 数据库中间件那么什么是数据库中间件呢? 前面文章我们提到,如果数据量比较大的话,我们需要对数据进行分库分表,分完之后,原本存在一个数据库中的数据,现在就存在多个数据库中了,那么我们的项目结构可能就是下面这个样子了: 我们要在 Java 代码中配置复杂的多数据源,配置读写分离,数据查询的时候还要进行数据的预处理,例如从多个 DB 上加载到的数据要先进行排序、过滤等等操作,这样我们的 Java 代码就参杂了很多业务无关的方法,而且这些参杂进来的代码,大多数都还是重复的。 ...

June 29, 2019 · 1 min · jiezi

分布式数据库中间件-MyCat-搞起来

关于 MyCat 的铺垫文章已经写了三篇了: MySQL 只能做小项目?松哥要说几句公道话!北冥有 Data,其名为鲲,鲲之大,一个 MySQL 放不下!What?Tomcat 竟然也算中间件?今天终于可以迎接我们的大 Boss 出场了! <!--more--> MyCat 简介前面文章我们提到,如果数据量比较大的话,我们需要对数据进行分库分表,分完之后,原本存在一个数据库中的数据,现在就存在多个数据库中了,就像下面这样: 那么此时 MyCat 所扮演的角色就是分布式数据库中间件! MyCat 是一个开源的分布式数据库中间件,它实现了 MySQL 协议,在开发者眼里,他就是一个数据库代理,我们甚至可以使用 MySQL 的客户端工具以及命令行来访问 MyCat 。 MyCat 现在已经不仅仅只支持 MySQL 了,同时也支持 MSSQL、Oracle、DB2、以及 PostgreSQL等主流数据库。甚至像 MongoDB 这种 NoSQL 也支持。 快速入门搭建读写分离要搞 MyCat ,一般要先搭建好 MySQL 的读写分离,MySQL 的读写分离可以参考松哥之前的这篇文章: 提高性能,MySQL 读写分离环境搭建(二)MyCat 安装环境: CentOS7JDK1.8MyCat 使用 Java 开发,因此,运行 MyCat ,一定要具备 Java 环境,配置 Java 运行环境这个比较容易,网上资料也很多,我就不详细介绍了。 Java 环境安装好之后,首先下载 MyCat: wget http://dl.mycat.io/1.6.7.1/Mycat-server-1.6.7.1-release-20190213150257-linux.tar.gz下载完成后,对下载文件进行解压。 tar -zxvf Mycat-server-1.6.7.1-release-20190213150257-linux.tar.gz解压成功后,会出现一个 mycat 目录,进入到 mycat/conf 目录,对 mycat 进行配置: ...

June 28, 2019 · 1 min · jiezi

题库分库分表架构方案

个人博客地址 https://www.texixi.com/2019/0…方案项目背景在现在题库架构下,针对新购买的1300W多道数据进行整合,不影响现有功能。由于数据量偏多,需要进行数据的切分目标场景兼容旧的功能对1300多W数据进行分库分表需要对旧的数据进行整合老师端选题组卷 可以根据 学段、学科、知识点、难度、题型 来筛选学生端根据老师端所选题目获取对应的题目对3年内以后扩展的增量数据预留数量空间数据样例学段数据量小学1285336初中6655780高中6144072学段学科数据量初中数学1869524初中化学1356224初中英语288440切分方案一切分为3个库, 分别是小学、初中、高中 数据占比如上每个库切分10个表 根据 (学科+首级知识点)%10每个库一个总表缺点:例:用到不同知识点时,需要多表获取数据优点:数据分布较为平均切分方案二 (采用)切分为3个库, 分别是小学、初中、高中 数据占比如上每个库切分10个表(全部10个学科) 根据 学科区分, 例: 数学表、物理表每个库一个总表缺点:数据不大平均, 数据量多的例数学有186W多、英语28W多优点:当有用到组卷等需要筛选多知识点题目时,不用多表查询数据id 自增区间划分小学 1-2亿中学 2-3亿高中 3亿起关联关系图根据知识点获取题目流程自增id对原有的id区间段不做处理对切分后的id自增段进行规划兼容旧功能解决的问题新旧数据有重复的知识点、题目新旧数据的结构不一样对旧的题库功能代码的修改两套题库合并主键冲突问题兼容旧功能 方案一 (个人推荐)有操作的旧的数据洗入新的结构,旧的数据只为兼容原有的功能数据,不做显示。优点:不用变动数据结构,最新的购买的数据结构较为清晰。 易维护扩展,因为目前旧的数据已经整合了两套数据缺点:需要修改全部旧有的功能代码(针对新的数据结构)兼容旧功能 方案二把新购买的数据整合进老的数据结构,同时保留三批数据,需要处理所有表的主键冲突、三批各表数据去重优点:旧有代码只修改数据结构切分的部分,不用全部修改功能代码缺点:数据较乱,三套不同的数据同时存在数据库需要处理新的结构整合进旧的数据结构,同时需要处理主键冲突,代码上需要处理对应的数据问题点测试环境和正式环境图片存放在那里?100多G,上传cdn需要几十天时间,有4000多W张,目前cdn不支持打包上传解决方案:购买单独服务器,主备,存放图片测试db 正式db 1300多w 目前占用100G左右, 需要存放空间解决方案:测试环境新加硬盘,新加db实例端口3307,正式环境db存放在图片服务器代码设计模式采用适配器模式(原先的代码结构不变)类图调研内容中间件MYCAT(未使用)什么是MYCAT一个彻底开源的,面向企业应用开发的大数据库集群支持事务、ACID、可以替代MySQL的加强版数据库一个可以视为MySQL集群的企业级数据库,用来替代昂贵的Oracle集群一个融合内存缓存技术、NoSQL技术、HDFS大数据的新型SQL Server结合传统数据库和新型分布式数据仓库的新一代企业级数据库产品一个新颖的数据库中间件产品MYCAT特性==支持库内分表(1.6)====支持单库内部任意join,支持跨库2表join,甚至基于caltlet的多表join==支持全局序列号,解决分布式下的主键生成问题。==分片规则丰富==,插件化开发,易于扩展。基于Nio实现,有效管理线程,解决高并发问题。==支持通过全局表,ER关系的分片策略,实现了高效的多表join查询==支持分布式事务(弱xa)。支持SQL黑名单、sql注入攻击拦截==支持MySQL、Oracle、DB2、SQL Server、PostgreSQL等DB的常见SQL语法====遵守Mysql原生协议==,跨语言,跨平台,跨数据库的通用中间件代理。==基于心跳的自动故障切换,支持读写分离,支持MySQL主从,==以及galera cluster集群。可以大幅降低开发难度,提升开发速度具体看 mycat 官网Mycat 注意事项全局表一致性检测 1.6版本开始支持(一致性的定时检测)分片 join(尽量避免使用 Left join 或 Right join,而用 Inner join)Mycat 原理应用要面对很多个数据库的时候,这个时候就需要对数据库层做一个抽象,来管理这些数据库,而最上面的应用只需要面对一个数据库层的抽象或者说数据库中间件就好了,这就是Mycat的核心作用。分片分析、路由分析、读写分离分析、缓存分析等,然后将此SQL发往后端的真实数据库,并将返回的结果做适当的处理,最终再返回给用户。Mycat 应用场景读写分离,配置简单分表分库,对于超过1000万的表进行分片,最大支持1000亿的单表分片报表系统,借助于Mycat的分表能力,处理大规模报表的统计文章整理应用场景 那些适合,那些不适合 https://www.cnblogs.com/barry…使用说明 https://juejin.im/post/59c325…总表使用mysql MERGE 引擎(不考虑)合并的表使用的必须是MyISAM引擎表的结构必须一致,包括索引、字段类型、引擎和字符集对于增删改查,直接操作总表即可。数据切分原则能不切分尽量不要切分。如果要切分一定要选择合适的切分规则,提前规划好。数据切分尽量通过数据冗余或表分组(Table Group)来降低跨库 Join 的可能。由于数据库中间件对数据 Join 实现的优劣难以把握,而且实现高性能难度极大,业务读取尽量少使用多表 Join。尽可能的比较均匀分布数据到各个节点上该业务字段是最频繁的或者最重要的查询条件。

January 29, 2019 · 1 min · jiezi

基于代理的数据库分库分表框架 Mycat实践

文章共 1796字,阅读大约需要 4分钟 !概 述在如今海量数据充斥的互联网环境下,分库分表的意义我想在此处就不用赘述了。而分库分表目前流行的方案最起码有两种:方案一:基于应用层的分片,即应用层代码直接完成分片逻辑方案二:基于代理层的分片,即在应用代码和底层数据库中间添加一层代理层,而分片的路由规则则由代理层来进行处理而本文即将要实验的 MyCAT框架就属于第二种方案的代表作品。注: 本文首发于 My Personal Blog:CodeSheep·程序羊,欢迎光临 小站环境规划在本文中,我拿出了三台 Linux主机投入试验,各节点的角色分配如下表所示:节点部署组件角色 192.168.199.75MySQL 、 MyCATmaster192.168.199.74MySQLslave192.168.199.76MySQLstandby master如果说上面这张表不足以说明实验模型,那接下来再给一张图好了,如下所示:我想这样看来的话,各个节点布了哪些组件,节点间的角色关系应该一目了然了吧实验环境规划好了以后,接下来进行具体的部署与实验过程,首先当然是 MyCAT代理的部署MyCAT 部署关于该部分,网上教程实在太多了,但最好还是参考官方文档来吧,下面也简述一下部署过程下载 MyCAT并解压安装这里安装的是 MyCAT 1.5wget https://raw.githubusercontent.com/MyCATApache/Mycat-download/master/1.5-RELEASE/Mycat-server-1.5.1-RELEASE-20161130213509-linux.tar.gztar -zxvf Mycat-server-1.5.1-RELEASE-20161130213509-linux.tar.gzmv mycat /usr/local/启动 MyCAT./mycat startMyCAT连接测试mysql -utest -ptest -h127.0.0.1 -P8066 -DTESTDBMyCAT 配置官网上对于这一部分的描述是非常详细的,MyCAT 配置主要涉及三个 XML配置文件:server.xml:MyCAT框架的系统参数/用户参数配置文件schema.xml: MyCAT框架的逻辑库表与分片的配置文件rule.xml :MyCAT框架的逻辑库表分片规则的配置文件用如下图形可以形象地表示出这三个 XML配置文件的配置内容和相互关系:下面来进入具体的实验环节 ,这也是围绕 MyCAT提供的几大主要功能展开的,主要涉及三个方面分库分表读写分离主备切换实验之前,我们先给出公共的 server.xml文件的配置,这部分后续实验过程中并不修改,其也就是定义了系统参数和用户参数:<?xml version=“1.0” encoding=“UTF-8”?><!DOCTYPE mycat:server SYSTEM “server.dtd”><mycat:server xmlns:mycat=“http://org.opencloudb/"> <system> <property name=“defaultSqlParser”>druidparser</property> <!– <property /> 这块诸多的property配置在此就不配置了,参照官网按需配置 –> </system> <user name=“test”> <property name=“password”>test</property> <property name=“schemas”>TESTDB</property> </user> <user name=“user”> <property name=“password”>user</property> <property name=“schemas”>TESTDB</property> <property name=“readOnly”>true</property> </user></mycat:server>分库分表实验预期实验效果:通过 MyCAT代理往一张逻辑表中插入的多条数据,在后端自动地分配在不同的物理数据库表上我们按照本文 第二节【环境规划】中给出的实验模型图来给出如下的 MyCAT逻辑库配置文件 schema.xml 和 分库分表规则配置文件 rule.xml准备配置文件schema.xml<?xml version=“1.0”?><!DOCTYPE mycat:schema SYSTEM “schema.dtd”><mycat:schema xmlns:mycat=“http://org.opencloudb/" > <schema name=“TESTDB” checkSQLschema=“false” sqlMaxLimit=“100”> <table name=“travelrecord” dataNode=“dn1,dn2” rule=“sharding-by-month” /> </schema> <dataNode name=“dn1” dataHost=“testhost” database=“db1” /> <dataNode name=“dn2” dataHost=“testhost” database=“db2” /> <dataHost name=“testhost” maxCon=“1000” minCon=“10” balance=“0” writeType=“0” dbType=“mysql” dbDriver=“native” switchType="-1” slaveThreshold=“100”> <heartbeat>select user()</heartbeat> <writeHost host=“hostM1” url=“localhost:3306” user=“root” password=“xxxxxx”> <readHost host=“hostS1” url=“192.168.199.74:3306” user=“root” password=“xxxxxx” /> </writeHost> <writeHost host=“hostM2” url=“192.168.199.76:3306” user=“root” password=“xxxxxx”> </writeHost> </dataHost></mycat:schema>其中定义了实验用到的 hostM1、hostS1 和 hostM2rule.xml <tableRule name=“sharding-by-month”> <rule> <columns>create_date</columns> <algorithm>partbymonth</algorithm> </rule> </tableRule> <function name=“partbymonth” class=“org.opencloudb.route.function.PartitionByMonth”> <property name=“dateFormat”>yyyy-MM-dd</property> <property name=“sBeginDate”>2018-11-01</property> </function>这里配置了 sharding-by-month的分库分表规则,即按照表中的 create_date字段进行分割,从 2018-11-01日期开始,月份不同的数据落到不同的物理数据库表中在三个物理节点数据库上分别创建两个库 db1和 db2create database db1;create database db2;连接 MyCATmysql -utest -ptest -h127.0.0.1 -P8066 -DTESTDB通过 MyCAT来创建数据库 travelrecord create table travelrecord (id bigint not null primary key,city varchar(100),create_date DATE);通过 MyCAT来往travelrecord表中插入两条数据insert into travelrecord(id,city,create_date) values(1,‘NanJing’,‘2018-11-3’);insert into travelrecord(id,city,create_date) values(2,‘BeiJing’,‘2018-12-3’);由于插入的这两条记录的 create_date分别是 2018-11-3和 2018-12-3,而我们配的分库分表的规则即是根据 2018-11-01这个日期为起始来进行递增的,按照前面我们配的分片规则,理论上这两条记录按照 create_date日期字段的不同,应该分别插入到 hostM1的 db1和 db2两个不同的数据库中。验证一下数据分片的效果由于 hostM1和 hostS1组成了 主-从库 关系,因此刚插入的两条数据也应该相应自动同步到 hostS1的 db1和 db2两个数据库中,不妨也来验证一下:读写分离实验预期实验效果:开启了 MyCAT的读写分离机制后,读写数据操作各行其道,互不干扰此节实验用到的配置文件 schema.xml 和 rule.xml基本和上面的【分库分表】实验没什么不同,只是我们需要关注一下 schema.xml配置文件中 <dataHost />标签里的 balance字段,它是与读写分离息息相关的配置:因此我们就需要弄清楚 <dataHost /> 标签中 balance参数的含义:balance=“0”:不开启读写分离机制,即读请求仅分发到 writeHost上balance=“1”:读请求随机分发到当前 writeHost对应的 readHost和 standby writeHost上balance=“2”:读请求随机分发到当前 dataHost内所有的 writeHost / readHost上balance=“3”:读请求随机分发到当前 writeHost对应的 readHost上我们验证一下 balance=“1"的情况,即开启读写分离机制,且读请求随机分发到当前 writeHost对应的 readHost和 standby writeHost上,而对于本文来讲,也即:hostS1 和 hostM2 上我们来做两次数据表的 SELECT读操作:mysql> select * from travelrecord limit 6;+—-+———-+————-+| id | city | create_date |+—-+———-+————-+| 3 | TianJing | 2018-11-04 || 5 | ShenYang | 2018-11-05 || 4 | Wuhan | 2018-12-04 || 6 | Harbin | 2018-12-05 |+—-+———-+————-+4 rows in set (0.08 sec)mysql> select * from travelrecord limit 6;+—-+———+————-+| id | city | create_date |+—-+———+————-+| 2 | BeiJing | 2018-12-03 || 8 | WuXi | 2018-12-06 || 1 | NanJing | 2018-11-03 || 7 | SuZhou | 2018-11-06 |+—-+———+————-+4 rows in set (0.01 sec)然后我们取出 mycat.log日志查看一下具体详情,我们发现第一次 select读操作分发到了 hostM2上:而第二次 select读操作分发到了 hostS1上:主备切换实验预期实验效果:开启 MyCAT的主备机制后,当主库宕机时,自动切换到备用机进行操作关于主备切换,则需要弄清楚 <dataHost /> 标签中 switchType参数的含义:switchType="-1”:不自动切换主备数据库switchType=“1”:自动切换主备数据库switchType=“2”:基于MySQL主从复制的状态来决定是否切换,需修改heartbeat语句:show slave statusswitchType=“3”:基于Galera(集群多节点复制)的切换机制,需修改heartbeat语句:show status like ‘wsrep%‘此处验证一下 Mycat的主备自动切换效果。为此首先我们将 switchType="-1” 设置为 switchType=“1”,并重启 MyCat服务:<dataHost name=“testhost” maxCon=“1000” minCon=“10” balance=“0” writeType=“0” dbType=“mysql” dbDriver=“native” switchType=“1” slaveThreshold=“100”>在本实验环境中,在 hostM1 和 hostM2均正常时,默认写数据时是写到 hostM1的接下来手动停止 hostM1 上的 MySQL数据库来模拟 hostM1 宕机:systemctl stop mysqld.service接下来再通过 MyCat插入如下两条数据:insert into travelrecord(id,city,create_date) values(3,‘TianJing’,‘2018-11-4’);insert into travelrecord(id,city,create_date) values(4,‘Wuhan’,‘2018-12-4’);效果如下:此时,我们恢复 hostM1,但接下来的数据写入依然进入 hostM2insert into travelrecord(id,city,create_date) values(5,‘ShenYang’,‘2018-11-5’);insert into travelrecord(id,city,create_date) values(6,‘Harbin’,‘2018-12-5’);接下来手动让 hostM2宕机,看 hostM1 是否能升级为主写节点再插入两条数据:insert into travelrecord(id,city,create_date) values(7,‘SuZhou’,‘2018-11-6’);insert into travelrecord(id,city,create_date) values(8,‘WuXi’,‘2018-12-6’);很明显,答案是肯定的后 记由于能力有限,若有错误或者不当之处,还请大家批评指正,一起学习交流!My Personal Blog:CodeSheep 程序羊我的半年技术博客之路 ...

December 20, 2018 · 2 min · jiezi

“分库分表 ?选型和流程要慎重,否则会失控

更多文章关注微信公众号《小姐姐味道》 https://mp.weixin.qq.com/s?__…数据库中间件之分库分表恭喜你,贵公司终于成长到一定规模,需要考虑高可用,甚至分库分表了。但你是否知道分库分表需要哪些要素?拆分过程是复杂的,提前计划,不要等真正开工,各种意外的工作接踵而至,以至失控。本文意图打开数据库中间件的广度,而不考虑实现深度,至于库表垂直和水平分的概念和缘由,不做过多解释。所以此文面向的是有一定研发经验,正在寻找选型和拆分流程的专业人士。切入层次以下,范围界定在JAVA和MySQL中。我们首先来看一下分库分表切入的层次。① 编码层在同一个项目中创建多个数据源,采用if else的方式,直接根据条件在代码中路由。Spring中有动态切换数据源的抽象类,具体参见AbstractRoutingDataSource。如果项目不是很庞大,使用这种方式能够快速的进行分库。但缺点也是显而易见的,需要编写大量的代码,照顾到每个分支。当涉及跨库查询、聚合,需要循环计算结果并合并的场景,工作量巨大。如果项目裂变,此类代码大多不能共用,大多通过拷贝共享。长此以往,码将不码。② 框架层这种情况适合公司ORM框架统一的情况,但在很多情况下不太现实。主要是修改或增强现有ORM框架的功能,在SQL中增加一些自定义原语或者hint来实现。通过实现一些拦截器(比如Mybatis的Interceptor接口),增加一些自定义解析来控制数据的流向,效果虽然较好,但会改变一些现有的编程经验。很多情况要修改框架源码,不推荐。③ 驱动层基于在编码层和框架层切入的各种缺点,真正的数据库中间件起码要从驱动层开始。什么意思呢?其实就是重新编写了一个JDBC的驱动,在内存中维护一个路由列表,然后将请求转发到真正的数据库连接中。像TDDL、ShardingJDBC等,都是在此层切入。包括Mysql Connector/J的Failover协议(具体指“load balancing”、“replication”、“farbic”等),也是直接在驱动上进行修改。请求流向一般是这样的:④ 代理层代理层的数据库中间件,将自己伪装成一个数据库,接受业务端的链接。然后负载业务端的请求,解析或者转发到真正的数据库中。像MySQL Router、MyCat等,都是在此层切入。请求流向一般是这样的:⑤ 实现层SQL特殊版本支持,如Mysql cluster本身就支持各种特性,mariadb galera cluster支持对等双主,Greenplum支持分片等。需要换存储,一般是解决方案,就不在讨论之列了。技术最终都会趋于一致,选择任何一种、都是可行的。但最终选型,受开发人员熟悉度、社区活跃度、公司切合度、官方维护度、扩展性,以及公司现有的数据库产品等多方位因素影响。选择或开发一款合适的,小伙伴们会幸福很多。驱动层和代理层对比通过以上层次描述,很明显,我们选择或开发中间件,就集中在驱动层和代理层。在这两层,能够对数据库连接和路由进行更强的控制和更细致的管理。但它们的区别也是明显的。驱动层特点仅支持JAVA,支持丰富的DB驱动层中间件仅支持Java一种开发语言,但支持所有后端关系型数据库。如果你的开发语言固定,后端数据源类型丰富,推荐使用此方案。占用较多的数据库连接驱动层中间件要维护很多数据库连接。比如一个分了10个 库 的表,每个java中的Connection要维护10个数据库连接。如果项目过多,则会出现连接爆炸(我们算一下,如果每个项目6个实例,连接池中minIdle等于5,3个项目的连接总数是 1065*3 = 900 个)。像Postgres这种每个连接对应一个进程的数据库,压力会很大。数据聚合在业务实例执行数据聚合,比如count sum等,是通过多次查询,然后在业务实例的内存中进行聚合。路由表存在于业务方实例内存中,通过轮询或者被动通知的途径更新路由表即可。集中式管理所有集群的配置管理都集中在一个地方,运维负担小,DBA即可完成相关操作。典型实现代理层特点异构支持,DB支持有限代理层中间件正好相反。仅支持一种后端关系型数据库,但支持多种开发语言。如果你的系统是异构的,并且都有同样的SLA要求,则推荐使用此方案。运维负担大代理层需要维护数据库连接数量有限(MySQL Router那种粘性连接除外)。但作为一个独立的服务,既要考虑单独部署,又要考虑高可用,会增加很多额外节点,更别提用了影子节点的公司了。另外,代理层是请求唯一的入口,稳定性要求极高,一旦有高耗内存的聚合查询把节点搞崩溃了,都是灾难性的事故。典型实现共同点篇幅有限,不做过多讨论。访问各中间件宣传页面,能够看到长长的Feature列表,也就是白名单;也能看到长长的限制列表,也就是黑名单。限定了你怎么玩,在增强了分布式能力后,分库分表本身就是一个阉割的数据库。使用限制确保数据均衡 拆分数据库的数据尽量均匀,比如按省份分user库不均匀,按userid取模会比较均匀不用深分页 不带切分键的深分页,会取出所有库所取页数之前的所有数据在内存排序计算。容易造成内存溢出。减少子查询 子查询会造成SQL解析紊乱,解析错误的情况,尽量减少SQL的子查询。事务最小原则 尽量缩小单机事务涉及的库范围,即尽可能减少夸库操作,将同类操作的库/表分在一起数据均衡原则 拆分数据库的数据尽量均匀,比如按省份分user库不均匀,按userid取模会比较均匀特殊函数 distinct、having、union、in、or等,一般不被支持。或者被支持,使用之后会增加风险,需要改造。产品建议聚焦在MyCat和ShardingJDBC上。另外,还有大量其他的中间件,不熟悉建议不要妄动。数据库中间件不好维护,你会发现大量半死不活的项目。以下列表,排名不分先后,有几个是只有HA功能,没有拆分功能的:Atlas、Kingshard、DBProxy、mysql router、MaxScale、58 Oceanus、ArkProxy、Ctrip DAL、Tsharding、Youtube vitess、网易DDB、Heisenberg、proxysql、Mango、DDAL、Datahekr、MTAtlas、MTDDL、Zebra、Cobar、Cobar汗、几乎每个大厂都有自己的数据库中间件(还发现了几个喜欢拿开源组件加公司前缀作为产品的),只不过不给咱用罢了。流程解决方案无论是采用哪个层面切入进行分库分表,都面临以下工作过程。信息收集统计影响的业务和项目项目范围越大,分库难度越高。有时候,一句复杂的SQL能够涉及四五个业务方,这种SQL都是需要重点关注的。确定分库分表的规模,是只分其中的几张表,还是全部涉及。分的越多,工作量越大,几乎是线性的。还有一些项目是牵一发动全身的。举个例子,下面这个过程,影响的链路就不仅是分库这么简单了。确定参与人员除了分库分表组件的技术支持人员,最应该参与的是对系统、对现有代码最熟悉的几个人。只有他们能够确定哪些SQL该废弃掉、SQL的影响面等。确定分库分表策略确定分库分表的维度和切分键。切分键(就是路由数据的column)一旦确定,是不允许修改的,所以在前期架构设计上,应该首先将其确立下来,才能进行后续的工作;数据维度多意味着有不同的切分键,达到不同条件查询的效果。这涉及到数据的冗余(多写、数据同步),会更加复杂。前期准备数据规整库表结构不满足需求,需要提前规整。比如,切分键的字段名称不同或者类型各异。在实施分库分表策略时,这些个性会造成策略过大不好维护。扫描所有SQL将项目中所有的SQL扫描出来,逐个判断是否能够按照切分键正常运行。在判断过程中肯定会有大量不合规的SQL,则都需要给出改造方案,这是主要的工作量之一。验证工具支持直接在原有项目上进行改动和验证是可行的,但会遇到诸多问题,主要是效率太低。我倾向于首先设计一些验证工具,输入要验证的SQL或者列表,然后打印路由信息和结果进行判断。技术准备建议以下提到的各个点,都找一个例子体验一下,然后根据自己的团队预估难度。以下:中间件所有不支持的SQL类型整理容易造成崩溃的注意事项不支持的SQL给出处理方式考虑一个通用的主键生成器考虑没有切分键的SQL如何处理考虑定时任务等扫全库的如何进行遍历考虑跨库跨表查询如何改造准备一些工具集实施阶段数据迁移分库分表会重新影响数据的分布,无论是全量还是增量,都会涉及到数据迁移,所以Databus是必要的。一种理想的状态是所有的增删改都是消息,可以通过订阅MQ进行双写。但一般情况下,仍然需要去模拟这个状态,比如使用Canal组件。怎么保证数据安全的切换,我们分其他章节进行讨论。充足的测试分库分表必须经过充足的测试,每一句SQL都要经过严格的验证。如果有单元测试或者自动化测试工具,完全的覆盖是必要的。一旦有数据进行了错误的路由,尤其是增删改,将会创造大量的麻烦。在测试阶段,将验证过程输出到单独的日志文件,充足测试后review日志文件是否有错误的数据流向。SQL复验强烈建议统一进行一次SQL复验。主要是根据功能描述,确定SQL的正确性,也就是通常说的review。演练在非线上环境多次对方案进行演练,确保万无一失。制定新的SQL规范分库分表以后,项目中的SQL就加了枷锁,不能够随意书写了。很多平常支持的操作,在拆分环境下就可能运行不了了。所以在上线前,涉及的SQL都应该有一个确认过程,即使已经经过了充足的测试。题外话没有支持的活别接,干不成。分库分表是战略性的技术方案,很多情况无法回退或者回退方案复杂。如果要拆分的库表涉及多个业务方,公司技术人员复杂,CTO要亲自挂帅进行协调,并有专业仔细的架构师进行监督。没有授权的协调人员会陷入尴尬的境地,导致流程失控项目难产。真正经历过的人,会知道它的痛!

December 5, 2018 · 1 min · jiezi