精华内容
下载资源
问答
  • 需要对Mysql数据库进行分库分表,故而最近一直在整理分库分表的相关知识,现手上的工作也告一段落了,抽空将自己最近的学习结果转化为博文,分享给大家,本博文打算做成一个系列的,首先是分库分表的理论知识的了解...

    由于业务需要,需要对Mysql数据库进行分库分表,故而最近一直在整理分库分表的相关知识,现手上的工作也告一段落了,抽空将自己最近的学习结果转化为博文,分享给大家,本博文打算做成一个系列的,首先是分库分表的理论知识的了解,其次是基于Java编程语言的分库分表的框架的开发,最后是分库分表的编制。让大家不仅仅从理论上了解mysql的分库分表,通过代码来更深层次的了解,理论是如何落地到实践的。最后非常感谢《可伸缩服务架构 框架与中间件》这本书的作者么,本博文的代码实现是参考此书,然后结合当前系统平台框架开发而成。

    坚持我写作的一贯风格,我们先需要带着问题来了解mysql的分库分表

    • 什么是分库分表,为什么我们需要分库分表
    • 如何进行分库分表,有什么优缺点
    • 对于分库分表有哪些架构设计,对于后期的扩容扩展怎么样
    • 目前行业内流行的解决方案有哪些?各自有什么特点
    • 自己设计一个数据库分库分表的框架,如何设计,需要考虑哪些因素

    为什么需要分库分表

    随着我们的系统运行,存储在关系型数据库的数据量会越来越大,系统的访问的压力也会随之增大,如果一个库中的表数据超过了一定的数量,比如说mysql中的表数据达到千万级别,就需要考虑进行分库分表;

    其次随着表数据的不断增大,会发现,查询也随着变得缓慢,如果添加索引的话,会发现影响到了新增和删除的性能,如果我们将数据库分散到不同的表上,单表的索引大小就得到了控制,对索引以及表结构的变更会变得很方便和高效;

    当数据库实例的吞吐量达到性能的瓶颈时,我们需要扩展数据库实例,让每个数据库实例承担其中一部分数据库的请求,分解总体的大请求量的压力;

    在数据库进行扩容的时候对应用层的配置改变最少, 就需要在每个数据库实例中预留足够的数据库数量

    以上的情况我们都可以使用分库分表,那么什么是分库分表呢?

    简而言之就是数据拆分:将一个表结构分为多个表,或者将一个表数据分片后放入多个表,这些表可以放在同一个数据库里,也可以放到不同的数据库中,甚至可以放到不同的数据库实例中

    数据拆分的方式

    数据拆分有两种方式:

    • 垂直拆分: 根据业务的维度,将原本一个库中的表拆分多个表,每个库中表与原有的结构不同
    • 水平拆分: 根据分片算法,将一个库拆分成多个库,每个库依旧保留原有的结构

    在实际的开发过程中,通常是先进行维度拆分形成微服务结构,然后再进行水平拆分

    分库分表

    比如我们有一张表,随着业务的不断进行,mysql中表中数据量达到了10亿,若是将数据存放在一张表中,则性能一定不会太好,根据我们使用的经验,mysql数据库一张表的数据记录极限一般在5000万左右,所以我们需要对进行分片存储(水平拆分),按照5000万一个单位来拆分的话,需要切片数量20个,也就是20个数据库表

    如果将20个相同业务表存放在同一个数据库中,那么单个数据库实例的网卡I/O、内存、CPU和磁盘性能是有限的,随着数据库访问频率的增加,会导致单个数据库实例和数据库达到性能瓶颈,因此我们需要将20个表分到多个数据库和多个数据库实例中,具体的评估如下:
    【TODO 对数据库实例和数据库表的数量的评估】

    image

    如何进行分库分表

    分库分表是对数据库拆分的一种解决方案,根据实施切片逻辑的层次不同,我们将分库分表方案大致分为三大类:客户端分片、代理分片和支持事务的分布式数据库

    • 客户端分片

    所谓的客户端分片即在使用数据库的应用层直接操作分片逻辑,分片规则需要在同一个应用的多个节点间进行同步,每个应用层嵌入一个操作切片的逻辑实现。

    image

    在客户端分片,目前主要有以下三种方式:

    1. 在应用层直接实现

    这是一种非常通用的解决方案,直接在应用层读取分片规则,解析分片规则,根据分片规则实现切分的路由逻辑,从应用层直接决定每次操作应该使用哪个数据库实例中的对应的数据库

    这种解决方案虽然有一定的代码侵入,但是实现起来比较简单,但是切片的逻辑是自己开发的, 如果生产上遇到了问题,能快速定位解决;

    当然这种方式也存在缺点:代码的耦合度比较高,其次这种实现方式会让数据库保持的链接比较多,这要看应用服务的节点数量,需要提前进行容量上的评估

    1. 通过定制JDBC协议实现

    这种解决方案主要是为了解决1中解决方案中的代码耦合,通过定制JDBC协议来实现(主要是针对业务逻辑层提供与JDBC一致的接口),让分库分表在JDBC的内部实现

    目前当当网开源的框架:Sharding JDBC 就是使用这种解决方案来实现的

    1. 通过定制ORM框架实现

    目前ORM框架非常流行,流行的JPA、Mybatis和Hibernate都是优秀的ORM框架,通过定制ORM框架来实现分库分表方案,常见的有基于Mybatis的分库分表方案的解决;

        <select id="selectUser" parameterType="java.util.Map" resultType="User">
            select user_id as userId,user_name as userName
            from user_#{index}
            where user_id = #{userId}
        </select>
    
    • 代理分片

    代理分片就是在应用层和数据库层之间添加一个代理层,把分片的路由规则配置在代理层,代理层对外提供与JDBC兼容的接口给应用层,在业务实现之后,在代理层配置路由规则即可;

    image

    这种方案的优点:让应用层的开发人员专注于业务逻辑的实现,把分库分表的配置留给代理层处理
    同样的业务存在缺点:增加了代理层,这样的话对每个数据库操作都增加了一层网络传输,这对性能是有影响的,同时需要维护增加的代理层,也有了硬件成本,线上生产环境出现了问题,不能迅速定位,需要有一定的技术专家来维护

    我们常见的 Mycat就是基于此种解决方案来实现的

    • 支持事务的分布式数据库

    支持分布式事务的框架,目前有OceanBase、TiDB框架,这些框架将可伸缩特定和分布式事务的实现包装到了分布式数据库内部实现,对使用者透明,使用者不需要直接控制这些特性,但是对事务的支持不如关系型数据,适合大数据日志系统、统计系统、查询系统、社交网站等

    分库分表的架构设计

    上面我们介绍过数据拆分的两种方式:垂直拆分和水平拆分;

    拆分方式 优点 缺点
    垂直拆分 1. 拆分后业务清晰,拆分规则明确
    2. 系统之间进行整合或扩展容易
    3. 按照成本、应用等级、应用的类型等将表放到不同的机器上,便于管理
    4.便于实现动静分离、冷热分离的数据库表的设计模式
    5. 数据维护简单
    1. 部分业务表无法进行关联、只能通过接口的方式来解决,提高了系统的复杂度
    2. 受每种业务不同的限制,存在单库性能瓶颈,对数据扩展和性能提升不友好
    3. 事务处理复杂
    水平拆分 1. 单裤单表的数据保持一定的量级,有助于性能的提高
    2. 切分的表的结构相同,应用层改造较少,只需要增加路由规则即可
    3. 提高了系统的稳定性和负载能力
    1. 切分后数据是分散的,很难利用数据库的关联查询,跨库查询性能较差
    2. 拆分规则难以抽象
    3. 分片数据的一致性难以解决
    4. 数据扩容的难度和维护量极大

    综上所述,我们发现垂直拆分和水平拆分具有共同点:

    1. 存在分布式事务问题
    2. 存在跨节点join的问题
    3. 存在跨节点合并排序、分页的问题
    4. 存在多数据源管理的问题

    垂直拆分更偏向于业务拆分的过程,在技术上我们更倾向于水平切分的方案;

    常见的分片策略:

    • 按照哈希切片

    对数据库的某个字段进行来求哈希,再除以分片总数后取模,取模后相同的数据为一个分片,这样将数据分成多个分片的方法叫做哈希分片

    我们大多数在数据没有时效性的情况下使用哈希分片,就是数据不管是什么时候产生的,系统都需要处理或者查询;

    优点 缺点
    数据切片比较均匀,数据压力分散的效果好 数据分散后,对于查询需求需要进行聚合处理
    • 按照时间切片

    按照时间的范围将数据分布到不同的分片上,比如我们可以将交易数据按照与进行切片,或者按照季度进行切片,由交易数据的多少来决定按照什么样的时间周期来进行切片

    这种切片方式适合明显时间特点的数据,常见的就是订单历史查询

    分布式事务

    本博文不进行分布式事务的分析和实践,后期我会更新一系列的分布式事务的博文,一起探讨分布式事务的原理、解决方案和代码实践等,本博文简单介绍了分布式事务的解决方案;

    上面说到的,不管是垂直拆分还是水平拆分,都有一个共同的问题:分布式事务

    我们将单表的数据切片后存储在多个数据库甚至是多个数据库实例中,所以依靠数据库本身的事务机制不能满足需要,这时就需要用到分布式事务来解决了

    三种解决方案

    • 两阶段提交协议

    两阶段提交协议中的两阶段是:准备阶段和提交阶段,两个阶段都是由事务管理器(协调者)发起,事务管理器能最大限度的保证跨数据库操作的事务的原子性。

    具体的交互逻辑如下:

    image

    优点 缺点
    是分布式系统环境下最严格的事务实现防范,
    保证了数据一致性和操作原子性
    1. 难以进行水平伸缩,因为在提交事务过程中,事务管理器需要和每个参与者进行准备和提交的操作协调
    2.每个参与者之间的协调需要时间,参与者一多的话,则锁定资源和消费资源之间的时间差就边长
    3. 两阶段提交协议是阻塞协议,在极端情况下不能快速响应的话,会造成阻塞问题
    • 最大努力保证模式

    这是一种非常通用的保证分布式一致性的模式,适合对一致性要求不是十分严格的但是对性能要求比较高的场景

    最大努力保证模式:在更新多个资源时,将多个资源的提交尽量延后到最后一刻进行处理,这样的话,如果业务流程出现问题,则所有的资源更新都可以回滚,事务仍然保持一致。

    最大努力保证模式在发生系统问题,比如网络问题等会出现问题,造成数据一致性的问题 ,这是就需要进行实时补偿,将已提交的事务进行回滚

    一般情况下,使用消息中间件来完成消费者之间的事务协调,客户端从消息中间件的队列中消费消息,更新数据库,此时会涉及到两个操作,一是从消息中间件消费消息,二是更新数据库,具体的操作步骤如下:

    1. 开启消息事务
    2. 接收消息
    3. 开启数据库事务
    4. 更新数据库
    5. 提交数据库事务
    6. 提交消息事务

    上述步骤最关键的地方在5和6,如果5成功了,但是6出现了问题,导致消息中间件认为消息没有被成功消费,既有的机制会重新再消费消息,就会出现消息重复消费,这是需要幂等处理来避免消息的重新消费

    其次我们还需要注意消息消费的顺序性问题,以及消费过程中是否调用远程接口等耗时操作

    优点 缺点
    性能较高 1. 数据一致性不能完美保证,只能是最大保证
    2. 可能出现消息重复消费(幂等处理)
    3. 数据库事务可能存在远程操作嵌套,互相影响
    • 事务补偿机制

    以上提到的两种解决方案:两阶段提交协议对系统的性能影响较大,最大努力保证模式会是多个分布式操作互相嵌套,有可能互相影响,那么我们采用事务补偿机制:

    事务补偿即在事务链中的任何一个正向事务操作,都必须存在一个完全符合回滚规则的可逆事务。如果是一个完整的事务链,则必须事务链中的每一个业务服务或操作都有对应的可逆服务。对于Service服务本身无状态,也不容易实现前面讨论过的通过DTC或XA机制实现的跨应用和资源的事务管理,建立跨资源的事务上下文.

    我们通过跨银行转账来说明:

    首先调用取款服务,完全调用成功并返回,数据已经持久化。然后调用异地的存款服务,如果也调用成功,则本身无任何问题。如果调用失败,则需要调用本地注册的逆向服务(本地存款服务),如果本地存款服务调用失败,则必须考虑重试,如果约定重试次数仍然不成功,则必须log到完整的不一致信息。也可以是将本地存款服务作为消息发送到消息中间件,由消息中间件接管后续操作。

    最后添加的重试机制是最大程度的确保补偿服务执行,保持数据的一致性,如果重试之后还是失败,则将操作保存在消息中间件中,等待后续处理,这样就更多了一重保障

    image

    分库分表引起的问题

    由于将完整的数据分成若干份,在以下的场景中会产生多种问题

    • 扩容与迁移

    在分库分表中,如果涉及的分片已经达到了承载数据的最大值,就需要对集群进行扩容,通常包括以下的步骤

    1. 按照新旧分片规则,对新旧数据库进行双写
    2. 将双写前按照旧分片规则写入的历史数据,根据新分片规则迁移写入新的数据库
    3. 将按照旧的分片规则查询改为按照新的分片规则查询
    4. 将双写数据库逻辑从代码中下线,只按照新的分片规则写入数据
    5. 删除按照旧分片规则写入的历史数据

    2步骤中迁移数据时,数据量非常大,通常会导致不一致,因此需要先迁移旧的数据,洗完后再迁移到新规则的新数据库下,再做全量对比,对比评估在迁移过程中是否有数据的更新,如果有的话就再清洗、迁移,最后以对比没有差距为准

    • 分库分表维度导致的查询问题

    进行了分库分表以后,如果查询的标准是分片的主键,则可以通过分片规则再次路由并查询,但是对于其他主键的查询、范围查询、关联查询、查询结果排序等,并不是按照分库分表维度查询的;

    这样的话,解决方案有以下三种:

    1. 在多个分片表中查询后合并数据集,这种方式的效率最低
    2. 冗余记录多份数据,方便查询, 缺点是需要额外维护一份数据,浪费资源
    3. 通过搜索引擎解决,但如果实时性要求很高,就需要实现实时搜索,可以利用大数据相关特性来解决
    • 跨库事务难以实现

    同时操作多个库,则会出现数据不一致的情况,此时可以引用分布式事务来解决

    • 同组数据跨库问题

    要尽量把同一组数据放到同一数据库服务器上,不但在某些场景下可以利用本地事务的强一致性,还可以是这组数据自治

    主流的解决方案

    目前针对mysql的分库分表,行业内主流的解决方案有:ShardingJDBC、Mycat

    Mycat代理分片框架

    Mycat是一款面向企业级应用的开源数据库中间件产品,他目前支持数据库集群,分布式事务与ACID,被普遍视为基于Mysql技术的集群分布式数据库解决方案

    Mycat支持多种分片规则:

    • 枚举法
    • 固定分片的hash算法
    • 范围约定
    • 求模法
    • 日期列分区法
    • 通配取模
    • ASCII码求模通配
    • 编程指定
    • 截取数据哈希解析
    • 一致性Hash

    具体的Mycat使用方法,以后应该会有一博文来整理,敬请期待啊~~~

    展开全文
  • 我们需要接受失望,因为它是有限的;我们不会失去希望,因为它是无穷的。一、概述随着时间和业务的发展...二、需要解决问题2.1 原有事务由于分库分表之后,新表在另外一个数据库中,如何保证主库和分库的事务性是必...

    我们需要接受失望,因为它是有限的;我们不会失去希望,因为它是无穷的。

    一、概述

    随着时间和业务的发展,数据库中表的数据量会越来越大,相应地,数据操作,增删改查的开销也会越来越大。因此,把其中一些大表进行拆分到多个数据库中的多张表中。

    本篇文章是基于非事务消息的异步确保的方式来完成分库分表中的事务问题。

    二、需要解决问题

    2.1 原有事务

    由于分库分表之后,新表在另外一个数据库中,如何保证主库和分库的事务性是必须要解决的问题。

    解决办法:通过在主库中创建一个流水表,把操作数据库的逻辑映射为一条流水记录。当整个大事务执行完毕后(流水被插入到流水表),然后通过其他方式来执行这段流水,保证最终一致性。

    429c3cf13e5358e94eb3fdfd14be42de.png

    2.2 流水

    所谓流水,可以理解为一条事务消息

    上面通过在数据库中创建一张流水表,使用一条流水记录代表一个业务处理逻辑,因此,一个流水一定是能最终正确执行的.因此,当把一段业务代码提取流水中必须要考虑到:

    流水延迟处理性。流水不是实时处理的,而是用过流水执行器来异步执行的。因此,如果在原有逻辑中,需要特别注意后续流程对该流水是不是有实时依赖性(例如后续业务逻辑中会使用流水结果来做一些计算等)。

    流水处理无序性。保证即使后生成的流水先执行,也不能出现问题。

    流水最终成功性。对每条插入的流水,该条流水一定要保证能执行成功

    因此,提取流水的时候:

    流水处理越简单越好

    流失处理依赖越少越好

    提取的流水在该业务逻辑中无实时性依赖

    ca21dc7665bbcbfe787d2bc25c08f67c.png

    2.4 流水处理完成

    因为流水表是放在原数据库中,而流水处理完成后是操作分库,如果分库操作完成去更新老表流水消息,那么又是夸库事务,如何保证流水状态的更新和分库也是在一个事务的?

    解决办法是:在分库中创建一个流水表,当流失处理完成以后,不是去更新老表状态,而是插入分库流水表中、

    这样做的好处:

    一般会对流水做唯一索引,那么如果流水重复多次执行的时候,插入分库流水表的时候肯定由于唯一索引检测不通过,整个事务就会回滚(当然也可以在处理流水事前应该再做一下幂等性判断)

    这样通过判断主库流水是否在分库中就能判断一条流水是否执行完毕

    22e0de1162263bbf105a4922eaea3fc2.png

    三、流水处理器基本框架

    流水处理器其实不包含任何业务相关的处理逻辑,核心功能就是:

    通知业务接入方何时处理什么样的流水

    检验流水执行的成功

    注:流水执行器并不知道该流水表示什么逻辑,具体需要业务系统去识别后去执行相对应业务逻辑。

    8082cb189010a26859d5dcdc93b3dacd.png

    3.1 流水执行任务

    流水处理调度任务就是通过扫描待处理的流水,然后通知业务系统该执行哪一条流水。

    示意图如下:

    b07436beb441cac69f4d6b150a089dba.png

    3.2 流水校验任务

    流水校验任务就是要比较主库和分库中的流水记录,对执行未成功的流水通知业务系统进行重新处理,如果多次重试失败则发出告警。

    流程示意图:

    b1419b1220c4eeaf55b07085ccfa8ac0.png

    四、为什么不用事务消息

    由于是既有项目(互联网金融,所以是绝对不容忍有任何消息丢失或者消息处理失败)进行改造,不使用事务消息有1个原因

    需要额外引入消息队列,增加系统的复杂度,而且也需要额外的逻辑保证和消息队列通讯失败的时候处理

    其实1不算是主要原因,而是因为事务消息需要手动的commit和rollback(使用数据库不需要),那么问题来了,spring中事务是有传递性的,那我们事务消息何时提交又是个大问题,例如 A.a()本来就是一个事务, 但是另外一个事务B.b()中又调用了A.a() 那事务消息提交是放在A.a()还是B.b()中呢?

    展开全文
  • 数据库分库分表是缓解数据库服务器压力和增加并发量的途径之一,但是随着分库分表之后,也不可避免的带来了一些问题,很显而易见的问题就是如何解决分库后的查询统计。分库之后没有SQL可以用了,简单的过滤后再合并...

    数据库分库分表是缓解数据库服务器压力和增加并发量的途径之一,但是随着分库分表之后,也不可避免的带来了一些问题,很显而易见的问题就是如何解决分库后的查询统计。分库之后没有SQL可以用了,简单的过滤后再合并还可以做,但分组都会很麻烦,必须把分库分组汇总结集再分组汇总。这对很多java应用程序员来讲是个挑战。但是,数据量太大大,不分库也不行,进退两难。

    这时候,采用集算器来做后一步的汇总计算就很容易,比如刚才说的分组汇总问题,写出来只要这么几行:

      A B C
    1 =[connect("orclA"),connect("orclB")]   /连接多个数据源
    2 select   sales.dept,sum(orders.amount)amount   from orders,sales where   orders.salesID=sales.salesID group by sales.dept /分组汇总SQL
    3 fork A1 =A3.query@x(A2) /并行计算
    4 =A3.conj()   /合并结果
    5 =A4.groups(DEPT;sum(AMOUNT):AMOUNT)   /二次分组汇总
           

    这里实现分组的代码还考虑了让分库并行执行SQL。

    利用集算器实现分库汇总里包含几个典型例子来说明分库汇总的用法,跨库数据表的运算 是有关分库后统计查询的更详细解释,还有讲解视频分库后的统计查询梳理要点和难点。集算器还很容易嵌入到Java应用程序中,Java 如何调用 SPL 脚本 有使用和获得它的方法。关于集算器安装使用、获得免费授权和相关技术资料,可以参见 集算器如何使用 。

    展开全文
  • 现在有一个未分库分表的系统,未来要分库分表如何设计才可以让系统从未分库分表动态切换到分库分表上? 如何设计可以动态扩容缩容的分库分表方案? 分库分表后,ID主键如何处理? 为什么要分库分表? 说白了,...

    数据库分库分表的面试连环炮

    面试题

    • 为什么要分库分表?(设计高并发系统的时候,数据库层面该如何设计)
    • 用过哪些分库分表中间件?
    • 不同的分库分表中间件都有什么优缺点?
    • 你们具体是如何对数据库如何进行垂直拆分或水平拆分的?
    • 现在有一个未分库分表的系统,未来要分库分表,如何设计才可以让系统从未分库分表动态切换到分库分表上?
    • 如何设计可以动态扩容缩容的分库分表方案?
    • 分库分表后,ID主键如何处理?

    为什么要分库分表?

    说白了,分库分表是两回事儿,大家可别搞混了,可能是光分库不分表,也可能是光分表不分库,都有可能。我先给大家抛出来一个场景。

    假如我们现在是一个小创业公司(或者是一个BAT公司刚兴起的一个新部门),现在注册用户就20万,每天活跃用户就1万,每天单表数据量就1000,然后高峰期每秒钟并发请求最多就10。。。天,就这种系统,随便找一个有几年工作经验的,然后带几个刚培训出来的,随便干干都可以。

    结果没想到我们运气居然这么好,碰上个CEO带着我们走上了康庄大道,业务发展迅猛,过了几个月,注册用户数达到了2000万!每天活跃用户数100万!每天单表数据量10万条!高峰期每秒最大请求达到1000!同时公司还顺带着融资了两轮,紧张了几个亿人民币啊!公司估值达到了惊人的几亿美金!这是小独角兽的节奏!

    好吧,没事,现在大家感觉压力已经有点大了,为啥呢?因为每天多10万条数据,一个月就多300万条数据,现在咱们单表已经几百万数据了,马上就破千万了。但是勉强还能撑着。高峰期请求现在是1000,咱们线上部署了几台机器,负载均衡搞了一下,数据库撑1000 QPS也还凑合。但是大家现在开始感觉有点担心了,接下来咋整呢。。。。。。

    再接下来几个月,我的天,CEO太牛逼了,公司用户数已经达到1亿,公司继续融资几十亿人民币啊!公司估值达到了惊人的几十亿美金,成为了国内今年最牛逼的明星创业公司!天,我们太幸运了。

    但是我们同时也是不幸的,因为此时每天活跃用户数上千万,每天单表新增数据多达50万,目前一个表总数据量都已经达到了两三千万了!扛不住啊!数据库磁盘容量不断消耗掉!高峰期并发达到惊人的5000~8000!别开玩笑了,哥。我跟你保证,你的系统支撑不到现在,已经挂掉了!

    好吧,所以看到你这里你差不多就理解分库分表是怎么回事儿了,实际上这是跟着你的公司业务发展走的,你公司业务发展越好,用户就越多,数据量越大,请求量越大,那你单个数据库一定扛不住。

    比如你单表都几千万数据了,你确定你能抗住么?绝对不行,单表数据量太大,会极大影响你的sql执行的性能,到了后面你的sql可能就跑的很慢了。一般来说,就以我的经验来看,单表到几百万的时候,性能就会相对差一些了,你就得分表了。

    分表是啥意思?就是把一个表的数据放到多个表中,然后查询的时候你就查一个表。比如按照用户id来分表,将一个用户的数据就放在一个表中。然后操作的时候你对一个用户就操作那个表就好了。这样可以控制每个表的数据量在可控的范围内,比如每个表就固定在200万以内。

    分库是啥意思?就是你一个库一般我们经验而言,最多支撑到并发2000,一定要扩容了,而且一个健康的单库并发值你最好保持在每秒1000左右,不要太大。那么你可以将一个库的数据拆分到多个库中,访问的时候就访问一个库好了。

    这就是所谓的分库分表,为啥要分库分表?你明白了吧

    01_分库分表的由来

    用过哪些分库分表中间件?

    分类

    数据库中间件分为两类

    • proxy:中间经过一层代理,需要独立部署
    • client:在客户端就知道指定到那个数据库

    各个中间件

    这个其实就是看看你了解哪些分库分表的中间件,各个中间件的优缺点是啥?然后你用过哪些分库分表的中间件。

    比较常见的包括:cobar、TDDL、atlas、sharding-jdbc、mycat

    cobar:阿里b2b团队开发和开源的,属于proxy层方案。早些年还可以用,但是最近几年都没更新了,基本没啥人用,差不多算是被抛弃的状态吧。而且不支持读写分离、存储过程、跨库join和分页等操作。

    TDDL:淘宝团队开发的,属于client层方案。不支持join、多表查询等语法,就是基本的crud语法是ok,但是支持读写分离。目前使用的也不多,因为还依赖淘宝的diamond配置管理系统。

    atlas:360开源的,属于proxy层方案,以前是有一些公司在用的,但是确实有一个很大的问题就是社区最新的维护都在5年前了。所以,现在用的公司基本也很少了。

    sharding-jdbc:当当开源的,属于client层方案。确实之前用的还比较多一些,因为SQL语法支持也比较多,没有太多限制,而且目前推出到了2.0版本,支持分库分表、读写分离、分布式id生成、柔性事务(最大努力送达型事务、TCC事务)。而且确实之前使用的公司会比较多一些(这个在官网有登记使用的公司,可以看到从2017年一直到现在,是不少公司在用的),目前社区也还一直在开发和维护,还算是比较活跃,个人认为算是一个现在也可以选择的方案。

    mycat:基于cobar改造的,属于proxy层方案,支持的功能非常完善,而且目前应该是非常火的而且不断流行的数据库中间件,社区很活跃,也有一些公司开始在用了。但是确实相比于sharding jdbc来说,年轻一些,经历的锤炼少一些。

    总结

    所以综上所述,现在其实建议考量的,就是sharding-jdbc和mycat,这两个都可以去考虑使用。

    sharding-jdbc这种client层方案的优点在于不用部署,运维成本低,不需要代理层的二次转发请求,性能很高,但是如果遇到升级啥的需要各个系统都重新升级版本再发布,各个系统都需要耦合sharding-jdbc的依赖;

    mycat这种proxy层方案的缺点在于需要部署,自己及运维一套中间件,运维成本高,但是好处在于对于各个项目是透明的,如果遇到升级之类的都是自己中间件那里搞就行了。

    通常来说,这两个方案其实都可以选用,但是我个人建议中小型公司选用sharding-jdbc,client层方案轻便,而且维护成本低,不需要额外增派人手,而且中小型公司系统复杂度会低一些,项目也没那么多;

    但是中大型公司最好还是选用mycat这类proxy层方案,因为可能大公司系统和项目非常多,团队很大,人员充足,那么最好是专门弄个人来研究和维护mycat,然后大量项目直接透明使用即可。

    我们,数据库中间件都是自研的,也用过proxy层,后来也用过client层

    如何对数据库如何进行垂直拆分或水平拆分的?

    拆分方法

    • 垂直拆分
    • 水平拆分

    水平拆分

    水平拆分的意思,就是把一个表的数据给弄到多个库的多个表里去,但是每个库的表结构都一样,只不过每个库表放的数据是不同的,所有库表的数据加起来就是全部数据。水平拆分的意义,就是将数据均匀放更多的库里,然后用多个库来抗更高的并发,还有就是用多个库的存储容量来进行扩容。

    垂直拆分

    垂直拆分的意思,就是把一个有很多字段的表给拆分成多个表,或者是多个库上去。每个库表的结构都不一样,每个库表都包含部分字段。一般来说,会将较少的访问频率很高的字段放到一个表里去,然后将较多的访问频率很低的字段放到另外一个表里去。因为数据库是有缓存的,你访问频率高的行字段越少,就可以在缓存里缓存更多的行,性能就越好。这个一般在表层面做的较多一些。

    这个其实挺常见的,不一定我说,大家很多同学可能自己都做过,把一个大表拆开,订单表、订单支付表、订单商品表。

    还有表层面的拆分,就是分表,将一个表变成N个表,就是让每个表的数据量控制在一定范围内,保证SQL的性能。否则单表数据量越大,SQL性能就越差。一般是200万行左右,不要太多,但是也得看具体你怎么操作,也可能是500万,或者是100万。你的SQL越复杂,就最好让单表行数越少。

    好了,无论是分库了还是分表了,上面说的那些数据库中间件都是可以支持的。就是基本上那些中间件可以做到你分库分表之后,中间件可以根据你指定的某个字段值,比如说userid,自动路由到对应的库上去,然后再自动路由到对应的表里去。

    你就得考虑一下,你的项目里该如何分库分表?一般来说,垂直拆分,你可以在表层面来做,对一些字段特别多的表做一下拆分;水平拆分,你可以说是并发承载不了,或者是数据量太大,容量承载不了,你给拆了,按什么字段来拆,你自己想好;分表,你考虑一下,你如果哪怕是拆到每个库里去,并发和容量都ok了,但是每个库的表还是太大了,那么你就分表,将这个表分开,保证每个表的数据量并不是很大。

    而且这儿还有两种分库分表的方式,一种是按照range来分,就是每个库一段连续的数据,这个一般是按比如时间范围来的,但是这种一般较少用,因为很容易产生热点问题,大量的流量都打在最新的数据上了;或者是按照某个字段hash一下均匀分散,这个较为常用。

    range来分,好处在于说,后面扩容的时候,就很容易,因为你只要预备好,给每个月都准备一个库就可以了,到了一个新的月份的时候,自然而然,就会写新的库了;缺点,但是大部分的请求,都是访问最新的数据。实际生产用range,要看场景,你的用户不是仅仅访问最新的数据,而是均匀的访问现在的数据以及历史的数据

    hash分法,好处在于说,可以平均分配没给库的数据量和请求压力;坏处在于说扩容起来比较麻烦,会有一个数据迁移的这么一个过程02_数据库如何拆分

    如何让系统不停机迁移到分库分表

    现在有一个未分库分表的系统,未来要分库分表,如何设计才可以让系统从未分库分表动态切换到分库分表上?

    剖析

    你看看,你现在已经明白为啥要分库分表了,你也知道常用的分库分表中间件了,你也设计好你们如何分库分表的方案了(水平拆分、垂直拆分、分表),那问题来了,你接下来该怎么把你那个单库单表的系统给迁移到分库分表上去?所以这都是一环扣一环的,就是看你有没有全流程经历过这个过程

    假设,你现有有一个单库单表的系统,在线上在跑,假设单表有600万数据,3个库,每个库里分了4个表,每个表要放50万的数据量

    假设你已经选择了一个分库分表的数据库中间件,sharding-jdbc,mycat,都可以,你怎么把线上系统平滑地迁移到分库分表上面去

    停机迁移方案

    我先给你说一个最low的方案,就是很简单,大家伙儿凌晨12点开始运维,网站或者app挂个公告,说0点到早上6点进行运维,无法访问。。。。。。

    接着到0点,停机,系统挺掉,没有流量写入了,此时老的单库单表数据库静止了。然后你之前得写好一个导数的一次性工具,此时直接跑起来,然后将单库单表的数据哗哗哗读出来,写到分库分表里面去。

    导数完了之后,就ok了,修改系统的数据库连接配置啥的,包括可能代码和SQL也许有修改,那你就用最新的代码,然后直接启动连到新的分库分表上去。

    验证一下,ok了,完美,大家伸个懒腰,看看看凌晨4点钟的北京夜景,打个滴滴回家吧

    但是这个方案比较low,谁都能干,我们来看看高大上一点的方案

    01_长时间停机分库分表

    双写迁移方案

    这个是我们常用的一种迁移方案,比较靠谱一些,不用停机,不用看北京凌晨4点的风景

    简单来说,就是在线上系统里面,之前所有写库的地方,增删改操作,都除了对老库增删改,都加上对新库的增删改,这就是所谓双写,同时写俩库,老库和新库。

    然后系统部署之后,新库数据差太远,用之前说的导数工具,跑起来读老库数据写新库,写的时候要根据gmt_modified这类字段判断这条数据最后修改的时间,除非是读出来的数据在新库里没有,或者是比新库的数据新才会写。

    接着导万一轮之后,有可能数据还是存在不一致,那么就程序自动做一轮校验,比对新老库每个表的每条数据,接着如果有不一样的,就针对那些不一样的,从老库读数据再次写。反复循环,直到两个库每个表的数据都完全一致为止。

    接着当数据完全一致了,就ok了,基于仅仅使用分库分表的最新代码,重新部署一次,不就仅仅基于分库分表在操作了么,还没有几个小时的停机时间,很稳。所以现在基本玩儿数据迁移之类的,都是这么干了。

    02_不停机双写方案

    如何设计可以动态扩容的分库分表方案?

    思考步骤

    • 选择一个数据库中间件,调研、学习、测试
    • 设计你的分库分表的一个方案,你要分成多少个库,每个库分成多少个表,3个库每个库4个表
    • 基于选择好的数据库中间件,以及在测试环境建立好的分库分表的环境,然后测试一下能否正常进行分库分表的读写
    • 完成单库单表到分库分表的迁移,双写方案
    • 线上系统开始基于分库分表对外提供服务
    • 扩容了,扩容成6个库,每个库需要12个表,你怎么来增加更多库和表呢?

    这个是你必须面对的一个事儿,就是你已经弄好分库分表方案了,然后一堆库和表都建好了,基于分库分表中间件的代码开发啥的都好了,测试都ok了,数据能均匀分布到各个库和各个表里去,而且接着你还通过双写的方案咔嚓一下上了系统,已经直接基于分库分表方案在搞了。

    那么现在问题来了,你现在这些库和表又支撑不住了,要继续扩容咋办?这个可能就是说你的每个库的容量又快满了,或者是你的表数据量又太大了,也可能是你每个库的写并发太高了,你得继续扩容。

    停机扩容

    这个方案就跟停机迁移一样,步骤几乎一致,唯一的一点就是那个导数的工具,是把现有库表的数据抽出来慢慢倒入到新的库和表里去。但是最好别这么玩儿,有点不太靠谱,因为既然分库分表就说明数据量实在是太大了,可能多达几亿条,甚至几十亿,你这么玩儿,可能会出问题。

    从单库单表迁移到分库分表的时候,数据量并不是很大,单表最大也就两三千万

    写个工具,多弄几台机器并行跑,1小时数据就导完了

    3个库+12个表,跑了一段时间了,数据量都1亿~2亿了。光是导2亿数据,都要导个几个小时,6点,刚刚导完数据,还要搞后续的修改配置,重启系统,测试验证,10点才可以搞完

    优化后的方案

    一开始上来就是32个库,每个库32个表,1024张表

    我可以告诉各位同学说,这个分法,第一,基本上国内的互联网肯定都是够用了,第二,无论是并发支撑还是数据量支撑都没问题

    每个库正常承载的写入并发量是1000,那么32个库就可以承载32 * 1000 = 32000的写并发,如果每个库承载1500的写并发,32 * 1500 = 48000的写并发,接近5万/s的写入并发,前面再加一个MQ,削峰,每秒写入MQ 8万条数据,每秒消费5万条数据。

    有些除非是国内排名非常靠前的这些公司,他们的最核心的系统的数据库,可能会出现几百台数据库的这么一个规模,128个库,256个库,512个库,1024张表,假设每个表放500万数据,在MySQL里可以放50亿条数据

    每秒的5万写并发,总共50亿条数据,对于国内大部分的互联网公司来说,其实一般来说都够了

    谈分库分表的扩容,第一次分库分表,就一次性给他分个够,32个库,1024张表,可能对大部分的中小型互联网公司来说,已经可以支撑好几年了

    一个实践是利用32 * 32来分库分表,即分为32个库,每个库里一个表分为32张表。一共就是1024张表。根据某个id先根据32取模路由到库,再根据32取模路由到库里的表。

    刚开始的时候,这个库可能就是逻辑库,建在一个数据库上的,就是一个mysql服务器可能建了n个库,比如16个库。后面如果要拆分,就是不断在库和mysql服务器之间做迁移就可以了。然后系统配合改一下配置即可。

    比如说最多可以扩展到32个数据库服务器,每个数据库服务器是一个库。如果还是不够?最多可以扩展到1024个数据库服务器,每个数据库服务器上面一个库一个表。因为最多是1024个表么。

    这么搞,是不用自己写代码做数据迁移的,都交给dba来搞好了,但是dba确实是需要做一些库表迁移的工作,但是总比你自己写代码,抽数据导数据来的效率高得多了。

    01_分库分表扩容方案

    哪怕是要减少库的数量,也很简单,其实说白了就是按倍数缩容就可以了,然后修改一下路由规则。

    对2 ^ n取模
    
    orderId 模 32 = 库
    
    orderId / 32 模 32 = 表
    
    259      3        8
    
    1189     5        5
    
    352      0        11
    
    4593     17       15
    

    总结

    • 设定好几台数据库服务器,每台服务器上几个库,每个库多少个表,推荐是32库 * 32表,对于大部分公司来说,可能几年都够了
    • 路由的规则,orderId 模 32 = 库,orderId / 32 模 32 = 表
    • 扩容的时候,申请增加更多的数据库服务器,装好mysql,倍数扩容,4台服务器,扩到8台服务器,16台服务
    • 由dba负责将原先数据库服务器的库,迁移到新的数据库服务器上去,很多工具,库迁移,比较便捷
    • 我们这边就是修改一下配置,调整迁移的库所在数据库服务器的地址
    • 重新发布系统,上线,原先的路由规则变都不用变,直接可以基于2倍的数据库服务器的资源,继续进行线上系统的提供服务

    分库分表后ID主键如何处理

    前言

    其实这是分库分表之后你必然要面对的一个问题,就是id咋生成?因为要是分成多个表之后,每个表都是从1开始累加,那肯定不对啊,需要一个全局唯一的id来支持。所以这都是你实际生产环境中必须考虑的问题。

    数据库自增ID

    这个就是说你的系统里每次得到一个id,都是往一个库的一个表里插入一条没什么业务含义的数据,然后获取一个数据库自增的一个id。拿到这个id之后再往对应的分库分表里去写入。

    这个方案的好处就是方便简单,谁都会用;缺点就是单库生成自增id,要是高并发的话,就会有瓶颈的;如果你硬是要改进一下,那么就专门开一个服务出来,这个服务每次就拿到当前id最大值,然后自己递增几个id,一次性返回一批id,然后再把当前最大id值修改成递增几个id之后的一个值;但是无论怎么说都是基于单个数据库。

    适合的场景:你分库分表就俩原因,要不就是单库并发太高,要不就是单库数据量太大;除非是你并发不高,但是数据量太大导致的分库分表扩容,你可以用这个方案,因为可能每秒最高并发最多就几百,那么就走单独的一个库和表生成自增主键即可。

    并发很低,几百/s,但是数据量大,几十亿的数据,所以需要靠分库分表来存放海量的数据01_分库分表的id主键问题

    UUID

    好处就是本地生成,不要基于数据库来了;不好之处就是,uuid太长了,作为主键性能太差了,不适合用于主键。

    适合的场景:如果你是要随机生成个什么文件名了,编号之类的,你可以用uuid,但是作为主键是不能用uuid的。

    获取系统时间戳

    这个就是获取当前时间即可,但是问题是,并发很高的时候,比如一秒并发几千,会有重复的情况,这个是肯定不合适的。基本就不用考虑了。

    适合的场景:一般如果用这个方案,是将当前时间跟很多其他的业务字段拼接起来,作为一个id,如果业务上你觉得可以接受,那么也是可以的。你可以将别的业务字段值跟当前时间拼接起来,组成一个全局唯一的编号,订单编号,时间戳 + 用户id + 业务含义编码

    Snowflake算法(雪花算法)

    twitter开源的分布式id生成算法,就是把一个64位的long型的id,1个bit是不用的,用其中的41 bit作为毫秒数,用10 bit作为工作机器id,12 bit作为序列号

    1 bit:不用,为啥呢?因为二进制里第一个bit为如果是1,那么都是负数,但是我们生成的id都是正数,所以第一个bit统一都是0

    41 bit:表示的是时间戳,单位是毫秒。41 bit可以表示的数字多达2^41 - 1,也就是可以标识2 ^ 41 - 1个毫秒值,换算成年就是表示69年的时间。

    10 bit:记录工作机器id,代表的是这个服务最多可以部署在2^10台机器上哪,也就是1024台机器。但是10 bit里5个bit代表机房id,5个bit代表机器id。意思就是最多代表2 ^ 5个机房(32个机房),每个机房里可以代表2 ^ 5个机器(32台机器)。

    12 bit:这个是用来记录同一个毫秒内产生的不同id,12 bit可以代表的最大正整数是2 ^ 12 - 1 = 4096,也就是说可以用这个12bit代表的数字来区分同一个毫秒内的4096个不同的id

    64位的long型的id,64位的long -> 二进制

    0 | 0001100 10100010 10111110 10001001 01011100 00 | 10001 | 1 1001 | 0000 00000000

    2018-01-01 10:00:00 -> 做了一些计算,再换算成一个二进制,41bit来放 -> 0001100 10100010 10111110 10001001 ``

    机房id,17 -> 换算成一个二进制 -> 10001

    机器id,25 -> 换算成一个二进制 -> 11001

    snowflake算法服务,会判断一下,当前这个请求是否是,机房17的机器25,在2175/11/7 12:12:14时间点发送过来的第一个请求,如果是第一个请求

    假设,在2175/11/7 12:12:14时间里,机房17的机器25,发送了第二条消息,snowflake算法服务,会发现说机房17的机器25,在2175/11/7 12:12:14时间里,在这一毫秒,之前已经生成过一个id了,此时如果你同一个机房,同一个机器,在同一个毫秒内,再次要求生成一个id,此时我只能把加1

    比如我们来观察上面的那个,就是一个典型的二进制的64位的id,换算成10进制就是910499571847892992。

    02_snowflake算法

    算法

    public class IdWorker{
    
        private long workerId;
        private long datacenterId;
        private long sequence;
    
        public IdWorker(long workerId, long datacenterId, long sequence){
            // sanity check for workerId
    // 这儿不就检查了一下,要求就是你传递进来的机房id和机器id不能超过32,不能小于0
            if (workerId > maxWorkerId || workerId < 0) {
                throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0",maxWorkerId));
            }
            if (datacenterId > maxDatacenterId || datacenterId < 0) {
                throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0",maxDatacenterId));
            }
            System.out.printf("worker starting. timestamp left shift %d, datacenter id bits %d, worker id bits %d, sequence bits %d, workerid %d",
                    timestampLeftShift, datacenterIdBits, workerIdBits, sequenceBits, workerId);
    
            this.workerId = workerId;
            this.datacenterId = datacenterId;
            this.sequence = sequence;
        }
    
        private long twepoch = 1288834974657L;
    
        private long workerIdBits = 5L;
        private long datacenterIdBits = 5L;
        private long maxWorkerId = -1L ^ (-1L << workerIdBits); // 这个是二进制运算,就是5 bit最多只能有31个数字,也就是说机器id最多只能是32以内
        private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); // 这个是一个意思,就是5 bit最多只能有31个数字,机房id最多只能是32以内
        private long sequenceBits = 12L;
    
        private long workerIdShift = sequenceBits;
        private long datacenterIdShift = sequenceBits + workerIdBits;
        private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
        private long sequenceMask = -1L ^ (-1L << sequenceBits);
    
        private long lastTimestamp = -1L;
    
        public long getWorkerId(){
            return workerId;
        }
    
        public long getDatacenterId(){
            return datacenterId;
        }
    
        public long getTimestamp(){
            return System.currentTimeMillis();
        }
    
    public synchronized long nextId() {
    // 这儿就是获取当前时间戳,单位是毫秒
            long timestamp = timeGen();
    
            if (timestamp < lastTimestamp) {
                System.err.printf("clock is moving backwards.  Rejecting requests until %d.", lastTimestamp);
                throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds",
                        lastTimestamp - timestamp));
            }
    
    // 0
    // 在同一个毫秒内,又发送了一个请求生成一个id,0 -> 1
    
            if (lastTimestamp == timestamp) {
                sequence = (sequence + 1) & sequenceMask; // 这个意思是说一个毫秒内最多只能有4096个数字,无论你传递多少进来,这个位运算保证始终就是在4096这个范围内,避免你自己传递个sequence超过了4096这个范围
                if (sequence == 0) {
                    timestamp = tilNextMillis(lastTimestamp);
                }
            } else {
                sequence = 0;
            }
    
    // 这儿记录一下最近一次生成id的时间戳,单位是毫秒
            lastTimestamp = timestamp;
    
    // 这儿就是将时间戳左移,放到41 bit那儿;将机房id左移放到5 bit那儿;将机器id左移放到5 bit那儿;将序号放最后10 bit;最后拼接起来成一个64 bit的二进制数字,转换成10进制就是个long型
            return ((timestamp - twepoch) << timestampLeftShift) |
                    (datacenterId << datacenterIdShift) |
                    (workerId << workerIdShift) |
                    sequence;
        }
    
    0 | 0001100 10100010 10111110 10001001 01011100 00 | 10001 | 1 1001 | 0000 00000000
    
    
        private long tilNextMillis(long lastTimestamp) {
            long timestamp = timeGen();
            while (timestamp <= lastTimestamp) {
                timestamp = timeGen();
            }
            return timestamp;
        }
    
        private long timeGen(){
            return System.currentTimeMillis();
        }
    
        //---------------测试---------------
        public static void main(String[] args) {
            IdWorker worker = new IdWorker(1,1,1);
            for (int i = 0; i < 30; i++) {
                System.out.println(worker.nextId());
            }
        }
    
    }
    

    怎么说呢,大概这个意思吧,就是说41 bit,就是当前毫秒单位的一个时间戳,就这意思;然后5 bit是你传递进来的一个机房id(但是最大只能是32以内),5 bit是你传递进来的机器id(但是最大只能是32以内),剩下的那个10 bit序列号,就是如果跟你上次生成id的时间还在一个毫秒内,那么会把顺序给你累加,最多在4096个序号以内。

    所以你自己利用这个工具类,自己搞一个服务,然后对每个机房的每个机器都初始化这么一个东西,刚开始这个机房的这个机器的序号就是0。然后每次接收到一个请求,说这个机房的这个机器要生成一个id,你就找到对应的Worker,生成。

    他这个算法生成的时候,会把当前毫秒放到41 bit中,然后5 bit是机房id,5 bit是机器id,接着就是判断上一次生成id的时间如果跟这次不一样,序号就自动从0开始;要是上次的时间跟现在还是在一个毫秒内,他就把seq累加1,就是自动生成一个毫秒的不同的序号。

    这个算法那,可以确保说每个机房每个机器每一毫秒,最多生成4096个不重复的id。

    利用这个snowflake算法,你可以开发自己公司的服务,甚至对于机房id和机器id,反正给你预留了5 bit + 5 bit,你换成别的有业务含义的东西也可以的。

    这个snowflake算法相对来说还是比较靠谱的,所以你要真是搞分布式id生成,如果是高并发啥的,那么用这个应该性能比较好,一般每秒几万并发的场景,也足够你用了。

    展开全文
  • 2、如何分库分表? 3、如何不停机分库分表? 4、分库分表扩容方案 5、分库分表后全局id咋生成? uuid 获取系统当前时间 6、为什么要进行读写分离? 7、数据库主从复制的原理? 8、什么叫数据库主从复制的...
  •  分库分表之后,id主键如何处理?  2 考点分析  其实这是分库分表之后你必然要面对的一个问题,就是id咋生成?  要是分成多个表之后,每个表都是从1开始累加,那肯定不对啊,需要一个全局唯一的id来支持!  ...
  • 点击上方“芋道源码”,选择“置顶公众号”技术文章第一时间送达!源码精品专栏精尽 Dubbo 原理与源码69 篇精尽 Netty 原理与源码61 篇中文详细注释的开源项目Java 并...
  • 如何做到永不迁移数据和避免热点? ——转自公众号:Java后端技术 | 本文地址:www.toutiao.com/i6677459303055491597 一、前言 一般来说,数据拆分,有两种: 垂直拆分,从业务角度进行拆分多个。如下图,...
  • 移动互联网时代,海量的用户数据每天都在产生,基于用户使用数据等这样的分析,都需要依靠数据统计和分析,当数据量小时,数据库方面的优化显得不太重要,一旦数据量越来越大,系统响应会变慢,TPS直线下降,直至...
  • UUID使用Java自己生成的64位的序列码。数字字母混合,无顺序。很难重复 无序的id做为主键给索引重建带来了性能浪费 snowFlake 算法 根据时间戳 + 服务器号 + 自增序列号生成全局一致性id 优点: 毫秒数在高位,...
  • 分库分表的实现方案,一般分为两种 1、增加一个中间层,中间层实现 MySQL 客户端协议,可以做到应用程序无感知地与中间层交互。由于是基于协议层的代理,可以做到支持多语言,但需要多启动一个进程、SQL 的解析也...
  • 问:分库分表之后,id 主键如何处理? 面试官心理分析 其实这是分库分表之后你必然要面对的一个问题,就是 id 咋生成?因为要是分成多个表之后,每个表都是从 1 开始累加,那肯定不对啊,需要一个全局唯一的 id 来...
  • 专注于Java领域优质技术,欢迎关注作者: MQ4096 来自:OceanBase技术闲谈概述分布式数据库已经流行好多年,产品非常众多,其中分布式数据库中间件使用场景最广。本文主要是总结如何基于分布式数据库中间件做数据库...
  • 分库分表 和 新型SQL

    2021-01-26 14:52:31
    最近与同行科技交流,经常被问到分库分表与分布式数据库如何选择,网上也有很多关于中间件+传统关系数据库(分库分表)与NewSQL分布式数据库的文章,但有些观点与判断是我觉得是偏激的,脱离环境去评价方案好坏其实...
  • 点击上方“Java面试题精选”,关注公众号面试刷图,查缺补漏>>号外:往期面试题,10篇为一个单位归置到本公众号菜单栏->...另一方面,在分库分表以后还需要保证分库分表的和主库的事务一致性。这片文章介...
  • 问:分库分表之后,id 主键如何处理? 面试官心理分析 其实这是分库分表之后你必然要面对的一个问题,就是 id 咋生成?因为要是分成多个表之后,每个表都是从 1 开始累加,那肯定不对啊,需要一个全局唯一的 id 来...
  • 0 Github1 面试题分库分表之后,id主键如何处理?2 考点分析其实这是分库分表之后你必然要面对的一个问题,就是id咋生成?要是分成多个表之后,每个表都是从1开始累加,那肯定不对啊,需要一个全局唯一的id来支持!...
  • 第一范式:列不可再 第二范式:行可以唯一区分,主键约束 第三范式:表的非主属性不能依赖与其他表的非主属性 外键约束 且三大范式是一级一级依赖的,第二范式建立在第一范式上,第三范式建立第一第二范式上。 2、...
  • 0 Github1 面试题分库分表之后,id主键如何处理?2 考点分析其实这是分库分表之后你必然要面对的一个问题,就是id咋生成?要是分成多个表之后,每个表都是从1开始累加,那肯定不对啊,需要一个全局唯一的id来支持!...
  • MySQL分库分表

    千次阅读 2020-07-14 13:31:46
    MySQL分库分表 互联网系统需要处理大量用户的请求。比如微信日活用户破10亿,海量的用户每天产生海量的数量;美团外卖,每天都是几千万的订单,那这些系统的用户表、订单表、交易流水表等是如何处理呢? 数据量只增...
  • 点击上方“Java之间”,选择“置顶或者星标”你关注的就是我关心的!作者:丁浪在谈论数据库架构和数据库优化的时候,我们经常会听到“分库分表”、“分片”、“Sharding...
  • 分库分表如何做到永不迁移数据和避免热点? 本文转载自【微信公众号:java进阶架构师,ID:java_jiagoushi】经微信公众号授权转载,如需转载与原文作者联系一、前言中大型项目中,一旦遇到数据量比较大,小伙伴应该...
  • 及时获取有趣有料的技术文章 本文来源:...在业务Service来看就是,可用数据库连接少甚至无连接可用。接下来就可以想象了吧(并发量、吞吐量、崩溃)。1、IO瓶颈第一种:磁盘读IO瓶颈,热点数据太多,数据库缓...
  • 读写分离 mysql读写分离实际利用的是主从复制架构,主数据库主要处理写操作,...问题2,使用分库分表 实现 proxy代理层(shardingproxy、mysqlproxy、mycat、atlas等) 应用层(java:sharding-jdbc、TDDL等) 分库
  • 本文选自:advanced-java作者:yanglbme问:分库分表之后,id 主键如何处理?# 面试官心理分析其实这是分库分表之后你必然要面对的一个问题,就是 id 咋生成?因为要是分成多个表之后,每个表都是从 1 开始累加,那...
  • 本文选自:advanced-java作者:yanglbme问:分库分表之后,id 主键如何处理?# 面试官心理分析其实这是分库分表之后你必然要面对的一个问题,就是 id 咋生成?因为要是分成多个表之后,每个表都是从 1 开始累加,那...
  • 本文选自:advanced-java作者:yanglbme 问:分库分表之后,id 主键如何处理?面试官心理分析其实这是分库分表之后你必然要面对的一个问题,就是 id 咋生成?因为要是分成多个表之后,每个表都是从 1 开始累加,那...
  • 本文已经收录自 JavaGuide (60k+ Star【Java学习+面试指南】 一份涵盖大部分Java程序员所需要掌握的核心知识。)本文授权转载自:https://juejin.im/post/5d6fc8eff265da03ef7a324b ,作者:1点25。ID是数据的唯一...
  • 点击关注公众号,Java干货及时送达前言中大型项目中,一旦遇到数据量比较大,小伙伴应该都知道就应该对数据进行拆分了。有垂直和水平两种。垂直拆分比较简单,也就是本来一个数据库,数据量大之后,...

空空如也

空空如也

1 2 3 4 5 ... 7
收藏数 131
精华内容 52
关键字:

java数据库如何分库分表

java 订阅