Mycat之前世今生

如果我有一个32核心的服务器,我就可以实现1个亿的数据分片,我有32核心的服务器么?没有,所以我至今无法实现1个亿的数据分片。——MyCAT ‘s Plan

话说“每一个成功的男人背后都有一个女人”,自然MyCAT也逃脱不了这个诅咒,MyCAT背后是阿里曾经开源的知名产品——Cobar,核心功能和优势是MySQL数据库分片,此产品曾经广为流传,据说最早的发起者对Mysql很精通,后来从阿里跳槽了,阿里随后开源的Cobra,并维持到2013年年初,然后,就没有然后了。

Cobra的确做的很不错,基于Java,实现了MySQL二进制协议,因此可以将自己伪装成一个MySQL Server,并且很多MySQL客户端都能访问,这个做法很好,比自己实现一个新的数据库协议要明智的多,因为生态环境在哪里摆着。

由于是Java开发的,下载下来解压,只要配置几个不是很复杂的配置文件,猛击鼠标,就能启动Cobra,因此这个开源产品赢得了很多Java粉丝的追捧,当然,笨人也跟着进入,并且在某个大型云项目中——“苦海无边”的煎熬过。

爱情就像是见鬼,你根本不知道鬼是怎样的,只有撞见鬼以后,你才真正明白,什么是爱情。爱情也会很快褪色,你不会告诉TA你不好的地方,你会拼命将你的优点无限放大,就差把自己当成世界上第二伟大的人了。相信每个用过Cobra的人都经历了这样的过程,围城里的人已经出不来了,但还有更多的人拼命想挤进来。

仅以此文,献给哪些努力在IT界寻求未来的精英们以及小白们,还有更多被无视的,就差要打算转行的同仁人,同在江湖混,不容易啊,面试时候就装装糊涂,放人家一马,说不定,以后又是一个Made  in China的乔布斯啊。

关于Cobra的十个秘密

第一个秘密:纳尼,Cobra会假死?

Cobra的确会假死,很多人遇到这个问题,如何简单的来验证这点呢?我们来做个小实验,假如你的分片表中配置有表company,则打开mysql终端,执行下面的SQL:

select sleep(500) from company;

此SQL会执行等待500秒,你再努力以最快的速度打开N个mysql终端,都执行相同的SQL,确保N>当前Cobra的执行线程数:show @@threadpool的所有Processor1-E的线程池的线程数量总和,然后你再执行任何简单的SQL,或者试图新建立连接,都会无法响应,此时show @@threadpool里面看到TASK_QUEUE_SIZE已经在积压中。

不可能吧,据说Cobra是NIO的非阻塞的,怎么可能阻塞!别激动,去看看代码,Cobra前端是NIO的,而后端跟Mysql的交互,是阻塞模式,其NIO代码只给出了框架,还未来得及实现。真相永远在代码里,所以,为了发现真相,还是转行去做码农吧,貌似码农也像之前的技术工人,越来越稀罕了。

第二个秘密:高可用的陷阱?

每一个秘密的背后,总是隐藏着更大的秘密。Cobra假死的的秘密背后,还隐藏着一个更为“强大”的秘密,那就是假死以后,Cobra的频繁主从切换问题。我们看看Cobra的一个很好的优点——“高可用性”的实现机制,下图解释了Cobra如何实现高可用性:

 

分片节点dn2_M1配置了两个dataSource,并且配置了心跳检测(heartbeat)语句,在这种配置下,每个dataNode会定期对当前正在使用的dataSource执行心跳检测,默认是第一个,频率是10秒钟一次,当心跳检测失败以后,会自动切换到第二个dataSource上进行读写,假如Cobra发生了假死,则在假死的1分钟内,Cobra会自动切换到第二个节点上,因为假死的缘故,第二个节点的心跳检测也超时。于是,1分钟内Cobra频繁来回切换,懂得MySQL主从复制机制的人都知道,在两个节点上都执行写操作意味着什么?——可能数据一致性被破坏,谁也不知道那个机器上的数据是最新的

还有什么情况下,会导致心跳检测失败呢?这是一个不得不说的秘密:当后端数据库达到最大连接后,会对新建连接全部拒绝,此时,Cobar的心跳检测所建立的新连接也会被拒绝,于是,心跳检测失败,于是,一切都悄悄的发生了。

幸好,大多数同学都没有配置高可用性,或者还不了解此特性,因此,这个秘密,一直在安全的沉睡。

第三个秘密:看上去很美的自动切换

Cobar很诱人的一个特性是高可用性,高可用性的原理是数据节点DataNode配置引用两个DataSource,并做心跳检测,当第一个DataSource心跳检测失败后,Cobar自动切换到第二个节点,当第二个节点失败以后,又自动切换回第一个节点,一切看起来很美,无人值守,几乎没有宕机时间。

在真实的生产环境中,我们通常会用至少两个Cobar实例组成负载均衡,前端用硬件或者HAProxy这样的负载均衡组件,防止单点故障,这样一来,即使某个Cobar实例死了,还有另外一台接手,某个Mysql节点死了,切换到备节点继续,至此,一切看起来依然很美,喝着咖啡,听着音乐,领导视察,你微笑着点头——No problem,Everything is OK!直到有一天,某个Cobar实例果然如你所愿的死了,不管是假死还是真死,你按照早已做好的应急方案,优雅的做了一个不是很艰难的决定——重启那个故障节点,然后继续喝着咖啡,听着音乐,轻松写好故障处理报告发给领导,然后又度过了美好的一天。

         你忽然被深夜一个电话给惊醒,你来不及发火,因为你的直觉告诉你,这个问题很严重,大量的订单数据发生错误很可能是昨天重启cobar导致的数据库发生奇怪的问题。你努力排查了几个小时,终于发现,主备两个库都在同时写数据,主备同步失败,你根本不知道那个库是最新数据,紧急情况下,你做了一个很英明的决定,停止昨天故障的那个cobar实例,然后你花了3个通宵,解决了数据问题。

         这个陷阱的代价太高,不知道有多少同学中枪过,反正我也是躺着中枪过了。若你还不清楚为何会产生这个陷阱,现在我来告诉你:

  • Cobar启动的时候,会用默认第一个Datasource进行数据读写操作
  • 当第一个Datasource心跳检测失败,会切换到第二个Datasource
  • 若有两个以上的Cobar实例做集群,当发生节点切换以后,你若重启其中任何一台Cobar,就完美调入陷阱

那么,怎么避免这个陷阱?目前只有一个办法,节点切换以后,尽快找个合适的时间,全部集群都同时重启,避免隐患。为何是重启而不是用节点切换的命令去切换?想象一下32个分片的数据库,要多少次切换?

MyCAT怎么解决这个问题的?很简单,节点切换以后,记录一个properties文件( conf目录下),重启的时候,读取里面的节点index,真正实现了无故障无隐患的高可用性。

第四个秘密:只实现了一半的NIO

         NIO技术用作JAVA服务器编程的技术标准,已经是不容置疑的业界常规做法,若一个Java程序员,没听说过NIO,都不好意思说自己是Java人。所以Cobar采用NIO技术并不意外,但意外的是,只用了一半。

         Cobar本质上是一个“数据库路由器”,客户端连接到Cobar,发生SQL语句,Cobar再将SQL语句通过后端与MySQL的通讯接口Socket发出去,然后将结果返回给客户端的Socket中。下面给出了SQL执行过程简要逻辑:

SQL->FrontConnection->Cobar->MySQLChanel->MySQL

         FrontConnection 实现了NIO通讯,但MySQLChanel则是同步的IO通讯,原因很简单,指令比较复杂,NIO实现有难度,容易有BUG。后来最新版本Cobar尝试了将后端也NIO化,大概实现了80%的样子,但没有完成,也存在缺陷。

         由于前端NIO,后端BIO,于是另一个有趣的设计产生了——两个线程池,前端NIO部分一个线程池,后端BIO部分一个线程池。各自相互不干扰,但这个设计的结果,导致了线程的浪费,也对性能调优带来很大的困难。

         由于后端是BIO,所以,也是Cobar吞吐量无法太高、另外也是其假死的根源。

         MyCAT在Cobar的基础上,完成了彻底的NIO通讯,并且合并了两个线程池,这是很大一个提升。从1.1版本开始,MyCAT则彻底用了JDK7的AIO,有一个重要提升。

第五个秘密:阻塞、又见阻塞

         Cobar本质上类似一个交换机,将后端Mysql 的返回结果数据经过加工后再写入前端连接并返回,于是前后端连接都存在一个“写队列”用作缓冲,后端返回的数据发到前端连接FrontConnection的写队列中排队等待被发送,而通常情况下,后端写入的的速度要大于前端消费的速度,在跨分片查询的情况下,这个现象更为明显,于是写线程就在这里又一次被阻塞。

         解决办法有两个,增大每个前端连接的“写队列”长度,减少阻塞出现的情况,但此办法只是将问题抛给了使用者,要是使用者能够知道这个写队列的默认值小了,然后根据情况进行手动尝试调整也行,但Cobar的代码中并没有把这个问题暴露出来,比如写一个告警日志,队列满了,建议增大队列数。于是绝大多数情况下,大家就默默的排队阻塞,无人知晓。

         MyCAT解决此问题的方式则更加人性化,首先将原先数组模式的固定长度的队列改为链表模式,无限制,并且并发性更好,此外,为了让用户知道是否队列过长了(一般是因为SQL结果集返回太多,比如1万条记录),当超过指定阀值(可配)后,会产生一个告警日志。

<system><property name="frontWriteQueueSize">1024</property></system>

第六个秘密:又爱又恨的SQL 批处理模式

         正如一枚硬币的正反面无法分离,一块磁石怎样切割都有南北极,爱情中也一样,爱与恨总是纠缠着,无法理顺,而Cobar的 SQL 批处理模式,也恰好是这样一个令人又爱又恨的个性。

         通常的SQL 批处理,是将一批SQL作为一个处理单元,一次性提交给数据库,数据库顺序处理完以后,再返回处理结果,这个特性对于数据批量插入来说,性能提升很大,因此也被普遍应用。JDBC的代码通常如下:

String sql = "insert into travelrecord (id,user_id,traveldate,fee,days) values(?,?,?,?,?)";
ps = con.prepareStatement(sql);
for (Map<String, String> map : list) {ps.setLong(1, Long.parseLong(map.get("id")));ps.setString(2, (String) map.get("user_id"));ps.setString(3, (String) map.get("traveldate"));ps.setString(4, (String) map.get("fee"));ps.setString(5, (String) map.get("days"));ps.addBatch();
}
ps.executeBatch();
con.commit();
ps.clearBatch();

    Cobar的批处理模式的实现,则有几个地方是与传统不同的:

  • 提交到cobar的批处理中的每一条SQL都是单独的数据库连接来执行的
  • 批处理中的SQL并发执行

并发多连接同时执行,则意味着Batch执行速度的提升,这是让人惊喜的一个特性,但单独的数据库连接并发执行,则又带来一个意外的副作用,即事务跨连接了,若一部分事务提交成功,而另一部分失败,则导致脏数据问题。看到这里,你是该“爱”呢还是该“恨”?

先不用急着下结论,我们继续看看Cobar的逻辑,SQL并发执行,其实也是依次获取独立连接并执行,因此还是有稍微的时间差,若某一条失败了,则cobar会在会话中标记”事务失败,需要回滚“,下一个没执行的SQL就抛出异常并跳过执行,客户端就捕获到异常,并执行rollback,回滚事务。绝大多数情况下,数据库正常运行,此刻没有宕机,因此事务还是完整保证了,但万一恰好在某个SQL commit指令的时候宕机,于是杯具了,部分事务没有完成,数据没写入。但这个概率有多大呢?一条insert insert 语句执行commit指令的时间假如是50毫秒,100条同时提交,最长跨越时间是5000毫秒,即5秒中,而这个C指令的时间占据程序整个插入逻辑的时间的最多20%,假如程序批量插入的执行时间占整个时间的20%(已经很大比例了),那就是20%×20%=4%的概率,假如机器的可靠性是99.9%,则遇到失败的概率是0.1%×4%=十万分之四。十万分之四,意味着99.996%的可靠性,亲,可以放心了么?

另外一个问题,即批量执行的SQL,通常都是insert的,插入成功就OK,失败的怎么办?通常会记录日志,重新找机会再插入,因此建议主键是能日志记录的,用于判断数据是否已经插入。

最后,假如真要多个SQL使用同一个后端MYSQL连接并保持事务怎么办?就采用通常的事务模式,单条执行SQL,这个过程中,Cobar会采用Session中上次用过的物理连接执行下一个SQL语句,因此,整个过程是与通常的事务模式完全一致。

线程池:一个连接要在可用之前,做如下同步指令:

12:34:37.044  DEBUG [Processor1-E3] (MySQLConnection.java:325) -org.opencloudb.mysql.nio.MySQLConnection$StatusSync@15aaf0b3need syn schemaCmd MySQL Command Packet{length=0,id=0}need syn charCmd MySQL Command Packet{length=0,id=0}need syn txIsolationCmd MySQL Command Packet{length=0,id=0}need syn autcommitCmd MySQL Command Packet{length=0,id=0}

后端物理连接是有限的,只能一个线程使用,因此,最佳的连接池,是 基于DB Server的,而不是基于DB Server的的某一个database上,另外,为了最有效的获取一个“合适的连接”,需要判断某个空闲连接的上述4个指令是否与想要得到的连接的请求参数“最大匹配”,目前没有研究过开源的其他Java连接池是否有此功效,Mycat中目前已经按照此策略,进行了最大可能的优化

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/62062.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Flutter的未来与趋势,23年还学吗?

随着移动应用市场的不断扩大&#xff0c;跨平台开发框架的需求也越来越大。Flutter框架可以帮助开发者在不同平台上快速开发高质量的移动应用程序&#xff0c;这种趋势将进一步推动Flutter的发展和普及。 作为一名前端开发工程师&#xff0c;学习Flutter框架是非常有必要的。因…

链表OJ练习(1)

一、移除链表元素 本题为力扣原题203 题目介绍&#xff1a; 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 。 列表中的节点数目范围在 0~10000内 1<Node.val<50 0<val<50 …

构建简单的Node.js HTTP服务器,发布公网远程访问的快速方法

文章目录 前言1.安装Node.js环境2.创建node.js服务3. 访问node.js 服务4.内网穿透4.1 安装配置cpolar内网穿透4.2 创建隧道映射本地端口 5.固定公网地址 前言 Node.js 是能够在服务器端运行 JavaScript 的开放源代码、跨平台运行环境。Node.js 由 OpenJS Foundation&#xff0…

【附安装包】Substance3D 2022安装教程

软件下载 软件&#xff1a;Substance3D版本&#xff1a;2022语言&#xff1a;简体中文大小&#xff1a;4.0G安装环境&#xff1a;Win11/Win10&#xff08;1809版本以上&#xff09;硬件要求&#xff1a;CPU2.0GHz 内存4G(或更高&#xff0c;不支持7代以下CPU&#xff09;下载通…

thinkphp6 入门(2)--视图、渲染html页面、赋值

修改模板引擎 config/view.php // 模板引擎类型使用Think type > php, 2. 新建一个控制器 本文app的名称为test&#xff0c;在其下新建一个控制器User app/test/controller/User.php 注意&#xff1a;需要引用think\facade\View来操作视图 <?phpnamespace app\te…

h5 ws 客户端 监听ws服务器广播的信息

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>AI智能写作</title><!-- Bootstrap CSS --><meta charset"utf-8"><meta name"viewport" content"widt…

K8s的Pod出现Init:ImagePullBackOff问题的解决(以calico为例)

对于这类问题的解决思路应该都差不多&#xff0c;本文以calico插件安装为例&#xff0c;发现有个Pod的镜像没有pull成功 第一步&#xff1a;查看这个pod的描述信息 kubectl describe pod calico-node-wmhrw -n kube-system 从上图发现是docker拉取"calico/cni:v3.15.1&q…

Ubuntu 20.04.5 怎么安装微信

这是我的ubutun版本号 在这个系统装桌面版微信很多功能不健全。搜索了很多方法&#xff0c;这个算是不错的一个法子。 1.添加仓库 首次使用时&#xff0c;你需要运行如下一条命令将移植仓库添加到系统中。 wget -O- https://deepin-wine.i-m.dev/setup.sh | sh 2.应用安装 …

【C语言】文件操作详解

文章目录 前言一、文件是什么二、文件具体介绍1.文件名2.文件类型3.文件缓冲区4.文件指针5.文件的打开和关闭 三、文件的顺序读写1.字符输入函数&#xff08;fgetc&#xff09;2.字符输出函数&#xff08;fputc&#xff09;3.文本行输入函数&#xff08;fgets&#xff09;4.文本…

基于MQTT协议的物联网关

随着工业领域的不断发展&#xff0c;数字化转型已经成为企业迈向未来的必由之路。在这个数字化浪潮中&#xff0c;HiWoo Box以其强大的功能和创新的设计&#xff0c;在工业物联网领域被越来越多的人所熟知。特别是其基于MQTT协议的物联网关能力&#xff0c;也为企业实现智能化数…

Centos7本地安装Docker-compose

考虑github时常出现问题。以下内容是基于本地安装 安装包地址 文章参考链接 1、下载安装包上传包到/usr/local/bin 2、执行命令 # 修改权限 chmod x /usr/local/bin/docker-compose ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose3、验证是否安装成功 docker-…

「操作系统」1. 基础

前言&#xff1a;操作系统基础八股文 文章目录 一 、操作系统基础1.1 什么是操作系统&#xff1f;1.2 什么是系统调用1.3 什么是中断 &#x1f680; 作者简介&#xff1a;作为某云服务提供商的后端开发人员&#xff0c;我将在这里与大家简要分享一些实用的开发小技巧。在我的职…

java 多线程

01.多线程类java.lang.Thread 这里继承Thread类的方法是比较常用的一种&#xff0c;如果说你只是想起一条线程。没有什么其它特殊的要求&#xff0c;那么可以使用Thread.&#xff08;笔者推荐使用Runable&#xff0c;后头会说明为什么&#xff09;。下面来看一个简单的实例&…

成集云 | 钉钉财务费用单同步至畅捷通 | 解决方案

源系统成集云目标系统 方案介绍 财务管理作为企业管理中重要的组成部分&#xff0c;在企业的发展和成长中扮演着重要角色&#xff0c;成集云以钉钉费用单OA审批与畅捷通TCloud系统为例&#xff0c;与钉钉连接器深度融合&#xff0c;通过数据处理和字段匹配实现了费用…

【核磁共振成像】相位差重建

目录 一、相位差map重建一般步骤和反正切函数主值范围二、反正切运算三、可预期相位误差和伴随场的校正四、图形变形校正 一、相位差map重建一般步骤和反正切函数主值范围 MRI是一个相敏成像模态&#xff0c;MR原始数据傅里叶变换后的复数图像中每个像素值有模和相位。标准模重…

PL端DDR4读写测试实验(未完成)

文章目录 DDR4介绍实验过程编写XDC使用IP核上板验证TODO 参考 DDR4介绍 开发板PL有一颗16bit的DDR4。 先说明硬件信号&#xff08;按该芯片&#xff09;&#xff1a; 信号名说明DQData input/output&#xff0c;双向数据线&#xff08;这个芯片是x16的&#xff0c;使用DQ[15…

Gradle 如何配置全局 mavenCentral()

我们都知道 Gradle 会使用 Maven 的中央仓库。 在 Gradle 的配置文件中&#xff0c;通常有一个 mavenCentral() 如果我们想把 mavenCentral() 的仓库地址全局替换掉别的仓库地址的话。 我们可以在 C:\Users\yhu\.gradle 目录下创建一个 init.gradle 文件。 文件中的代码为&a…

原生小程序 wxs 语法(详细)

WXS WXS&#xff08;WeiXin Script&#xff09;是内联在 WXML 中的脚本段。通过 WXS 可以在模版中内联少量处理脚本&#xff0c;丰富模板的数据预处理能力。另外&#xff0c; WXS 还可以用来编写简单的 WXS 事件响应函数。 从语法上看&#xff0c; WXS 类似于有少量限制的 Java…

JAVA类和对象

如何创建类 ⚠Java当中一切皆对象 ⚠如何描述对象&#xff1f;用类。 我们可以类当成一个模板&#xff0c;用来描述对象特征&#xff0c;行为等等 //自定义的类型 -> 你定义的一个 Java当中 没有的类型 class PetDog {public String name;//名字public String color;//颜…

数学建模:主成分分析法

&#x1f506; 文章首发于我的个人博客&#xff1a;欢迎大佬们来逛逛 主成分分析法 算法流程 构建原始数据矩阵 X X X &#xff0c;其中矩阵的形状为 x ∗ n x * n x∗n &#xff0c;有 m m m 个对象&#xff0c; n n n 个评价指标。然后进行矩阵的归一化处理。首先计算矩…