精华内容
下载资源
问答
  • 分片

    千次阅读 2017-11-26 21:19:36
    MyCat分片-海量数据存储解决方案1 什么是分片简单来说,就是指通过某种特定的条件,将我们存放在同一个数据库中的数据分散存放到多个数据库(主机)上面,以达到分散单台设备负载的效果。 数据的切分(Sharding)...

    MyCat分片-海量数据存储解决方案

    1 什么是分片

    简单来说,就是指通过某种特定的条件,将我们存放在同一个数据库中的数据分散存放到多个数据库(主机)上面,以达到分散单台设备负载的效果。
    数据的切分(Sharding)根据其切分规则的类型,可以分为两种切分模式。

    (1)一种是按照不同的表(或者Schema)来切分到不同的数据库(主机)之上,这种切分可以称之为数据的垂直(纵向)切分

    (2)另外一种则是根据表中的数据的逻辑关系,将同一个表中的数据按照某种条件拆分到多台数据库(主机)上面,这种切分称之为数据的水平(横向)切分。

    MyCat分片策略:

    2 分片相关的概念

    逻辑库(schema) :

    通常对实际应用来说,并不需要知道中间件的存在,业务开发人员只需要知道数据库的概念,所以数据库中间件可以被看做是一个或多个数据库集群构成的逻辑库。

    • 逻辑表(table):

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

    • 分片表:

    是指那些原有的很大数据的表,需要切分到多个数据库的表,这样,每个分片都有一部分数据,所有分片构成了完整的数据。 总而言之就是需要进行分片的表。

    • 非分片表:

    一个数据库中并不是所有的表都很大,某些表是可以不用进行切分的,非分片是相对分片表来说的,就是那些不需要进行数据切分的表。

    • 分片节点(dataNode)

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

    • 节点主机(dataHost)

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

    • 分片规则(rule)

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

    3 MyCat分片配置

    (1)配置schema.xml

    schema.xml作为MyCat中重要的配置文件之一,管理着MyCat的逻辑库、逻辑表以及对应的分片规则、DataNode以及DataSource。弄懂这些配置,是正确使用MyCat的前提。这里就一层层对该文件进行解析。

    schema 标签用于定义MyCat实例中的逻辑库

    Table 标签定义了MyCat中的逻辑表
    rule用于指定分片规则,auto-sharding-long的分片规则是按ID值的范围进行分片 1-5000000 为第1片
    5000001-10000000 为第2片….

    dataNode 标签定义了MyCat中的数据节点,也就是我们通常说所的数据分片。

    dataHost标签在mycat逻辑库中也是作为最底层的标签存在,直接定义了具体的数据库实例、读写分离配置和心跳语句。

    在服务器上创建3个数据库,分别是db1 db2 db3

    修改schema.xml如下:

    <?xml version="1.0"?>
    <!DOCTYPE mycat:schema SYSTEM "schema.dtd">
    <mycat:schema xmlns:mycat="http://org.opencloudb/">
    <schema name="MYSCHEMA" checkSQLschema="false" sqlMaxLimit="100">
        <table name="tb_test" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" />
    </schema>
    <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>
        <writeHost host="hostM1" url="192.168.25.142:3306" user="root"
            password="123456">
        </writeHost>
    </dataHost> 
    </mycat:schema>
    

    (2)配置 server.xml

    server.xml几乎保存了所有mycat需要的系统配置信息。最常用的是在此配置用户名、密码及权限。在system中添加UTF-8字符集设置,否则存储中文会出现问号

    <property name="charset">utf8</property>
    

    修改user的设置 , 我们这里为 数据库设置了两个用户

    <user name="test">
        <property name="password">test</property>
        <property name="schemas">MYSCHEMA</property>
    </user>
    <user name="root">
        <property name="password">123456</property>
        <property name="schemas">MYSCHEMAB</property>
    </user>
    

    4 MyCat分片测试

    进入mycat ,执行下列语句创建一个表:

    CREATE TABLE tb_test (
      id BIGINT(20) NOT NULL,
      title VARCHAR(100) NOT NULL ,
      PRIMARY KEY (id)
    ) ENGINE=INNODB DEFAULT CHARSET=utf8 
    

    创建后你会发现,MyCat会自动将你的表转换为大写,这一点与Oracle有些类似。

    我们再查看MySQL的3个库,发现表都自动创建好啦。好神奇。

    接下来是插入表数据,注意,在写INSERT语句时一定要写把字段列表写出来,否则会出现下列错误提示:
    错误代码: 1064
    partition table, insert must provide ColumnList
    我们试着插入一些数据:

    INSERT INTO TB_TEST(ID,TITLE) VALUES(1,'goods1');
    INSERT INTO TB_TEST(ID,TITLE) VALUES(2,'goods2');
    INSERT INTO TB_TEST(ID,TITLE) VALUES(3,'goods3');
    

    我们会发现这些数据被写入到第一个节点中了,那什么时候数据会写到第二个节点中呢?

    我们插入下面的数据就可以插入第二个节点了

    INSERT INTO TB_TEST(ID,TITLE) VALUES(5000001,'goods5000001');
    

    因为我们采用的分片规则是每节点存储500万条数据,所以当ID大于5000000则会存储到第二个节点上。

    目前只设置了两个节点,如果数据大于1000万条,会怎么样呢?执行下列语句测试一下

    INSERT INTO TB_TEST(ID,TITLE) VALUES(10000001,'goods10000001');
    

    5 MyCat分片规则

    rule.xml用于定义分片规则 ,我们这里讲解两种最常见的分片规则

    (1)按主键范围分片rang-long

    在配置文件中我们找到

    <tableRule name="auto-sharding-long">
        <rule>
            <columns>id</columns>
            <algorithm>rang-long</algorithm>
        </rule>
    </tableRule>
    

    tableRule 是定义具体某个表或某一类表的分片规则名称 columns用于定义分片的列 algorithm代表算法名称 我们接着找rang-long的定义

    <function name="rang-long"
        class="org.opencloudb.route.function.AutoPartitionByLong">
        <property name="mapFile">autopartition-long.txt</property>
    </function>
    

    Function用于定义算法 mapFile 用于定义算法需要的数据,我们打开autopartition-long.txt

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

    (2)一致性哈希murmur
    当我们需要将数据平均分在几个分区中,需要使用一致性hash规则
    我们找到function的name为murmur 的定义,将count属性改为3,因为我要将数据分成3片

    <function name="murmur"
        class="org.opencloudb.route.function.PartitionByMurmurHash">
        <property name="seed">0</property><!-- 默认是0 -->
        <property name="count">3</property><!-- 要分片的数据库节点数量,必须指定,否则没法分片 -->
        <property name="virtualBucketTimes">160</property><!-- 一个实际的数据库节点被映射为这么多虚拟节点,默认是160倍,也就是虚拟节点数是物理节点数的160倍 -->
        <!-- <property name="weightMapFile">weightMapFile</property> 节点的权重,没有指定权重的节点默认是1。以properties文件的格式填写,以从0开始到count-1的整数值也就是节点索引为key,以节点权重值为值。所有权重值必须是正整数,否则以1代替 -->
        <!-- <property name="bucketMapPath">/etc/mycat/bucketMapPath</property> 
            用于测试时观察各物理节点与虚拟节点的分布情况,如果指定了这个属性,会把虚拟节点的murmur hash值与物理节点的映射按行输出到这个文件,没有默认值,如果不指定,就不会输出任何东西 -->
    </function>
    

    我们再配置文件中可以找到表规则定义

    <tableRule name="sharding-by-murmur">
        <rule>
            <columns>id</columns>
            <algorithm>murmur</algorithm>
        </rule>
    </tableRule>
    

    但是这个规则指定的列是id ,如果我们的表主键不是id ,而是order_id ,那么我们应该重新定义一个tableRule:

    <tableRule name="sharding-by-murmur-order">
        <rule>
            <columns>order_id</columns>
            <algorithm>murmur</algorithm>
        </rule>
    </tableRule>
    

    在schema.xml中配置逻辑表时,指定规则为sharding-by-murmur-order

    <table name="tb_order" dataNode="dn1,dn2,dn3" rule="sharding-by-murmur-order" />
    

    最后可插入一些数据查看分片效果

    展开全文
  • Elasticsearch究竟要设置多少分片数?

    万次阅读 多人点赞 2017-09-24 22:34:00
    在构建Elasticsearch集群的初期如果集群分片设置不合理,可能在项目的中后期就会出现性能问题。Elasticsearch是一个非常通用的平台,支持各种各样的用例,并且为数据组织和复制策略提供了巨大灵活性。这种灵活性...

    Elasticsearch 最少必要知识实战教程直播回放

    0、引言

    本文翻译自Elasticsearch20170918热乎的官方博客,原作者:Christian Dahlqvist。 在构建Elasticsearch集群的初期如果集群分片设置不合理,可能在项目的中后期就会出现性能问题。

    Elasticsearch是一个非常通用的平台,支持各种各样的用例,并且为数据组织和复制策略提供了巨大灵活性。这种灵活性使得作为ELK新手的你将数据组织成索引和分片变得困难。虽然不一定会在首次启动时出现问题,但由于数据量随时间的推移,可能会导致性能问题。集群所拥有的数据越多,纠正问题就越困难,甚至有时可能需要重新索引大量数据。

    当我们遇到遭遇性能问题的用户时,可以追溯到关于数据索引的数据和群集数量的问题并不罕见。 对于涉及multi-tenancy或使用基于时间的索引的用户尤其如此。 在与用户讨论这个问题时(会议、论坛形式),引申出的一些最常见的问题是:

    1)“我应该有多少个分片?”
    2)“我的分片应该有多大”?
    

    这篇博客文章旨在帮助您回答这些问题,并为使用基于时间的索引的使用案例( 日志记录或安全分析 )提供实用的指导。

    1、什么是分片?

    在开始之前,让我们约定文章中用到的一些概念和术语。
    Elasticsearch中的数据组织成索引。每一个索引由一个或多个分片组成。每个分片是Luncene索引的一个实例,你可以把实例理解成自管理的搜索引擎,用于在Elasticsearch集群中对一部分数据进行索引和处理查询。

    【刷新】当数据写入分片时,它会定期地发布到磁盘上的新的不可变的Lucene段中,此时它可用于查询。——这被称为刷新。更详细的解读请参考:
    http://t.cn/R05e3YR

    【合并】随着分段数(segment)的增长,这些segment被定期地整合到较大的segments。 这个过程被称为合并(merging)。

    由于所有段都是不可变的, 因为新的合并段需要创建,旧的分段将被删除 ,这意味着所使用的磁盘空间通常在索引时会波动。 合并可能资源相当密集,特别是在磁盘I/O方面。

    分片是Elasticsearch在集群周围分发数据的单位。 Elasticsearch在重新平衡数据时 (例如 发生故障后) 移动分片的速度 取决于分片的大小和数量以及网络和磁盘性能。

    提示:避免有非常大的分片,因为大的分片可能会对集群从故障中恢复的能力产生负面影响。 对于多大的分片没有固定的限制,但是分片大小为50GB通常被界定为适用于各种用例的限制

    2、索引有效期( retention period )

    由于段是不可变的,更新文档需要Elasticsearch首先查找现有文档,然后将其标记为已删除,并添加更新的版本。删除文档还需要找到文档并将其标记为已删除。因此,删除的文档将继续占据磁盘空间和一些系统资源,直到它们被合并,这将消耗大量的系统资源。

    Elasticsearch允许从文件系统直接删除完整索引,而不必明确地必须单独删除所有记录。这是迄今为止从Elasticsearch删除数据的最有效的方式。

    ***提示:*尽可能使用基于时间的索引来管理数据。根据保留期(retention period,可以理解成有效期)将数据分组。基于时间的索引还可以轻松地随时间改变主分片和副本分片的数量(以为要生成的下一个索引进行更改)。这简化了适应不断变化的数据量和需求。

    3、索引和分片不是空闲的?

    【集群状态】对于每个Elasticsearch索引,其映射和状态的信息都存储在集群状态。 这些集群状态信息保存在内存中以便快速访问。 因此,如果在集群中拥有大量索引,可能导致大的集群状态(特别是如果映射较大)。 所有更新集群状态操作为了在集群中保证一致性,需要通过单个线程完成,因此更新速度将变慢。

    提示:*为了减少索引数量并避免大的乃至非常庞大的映射,请考虑将相同索引结构的数据存储在相同的索引中*,而不是基于数据的来源将数据分割成独立的索引。 在每个索引的索引数量和映射大小之间找到一个很好的平衡很重要。**

    每个分片都有数据需要保存在内存中并使用堆空间。 这包括在分片级别保存信息的数据结构,也包括在段级别的数据结构,以便定义数据驻留在磁盘上的位置。 这些数据结构的大小不是固定的,并且将根据用例而有所不同。

    然而,段相关开销的一个重要特征是它与分段的大小不成正比。 这意味着与较小的段相比,较大的段的每个数据量具有较少的开销,且这种差异很大。

    【堆内存的重要性】为了能够每个节点存储尽可能多的数据,重要的是尽可能多地管理堆内存使用量并减少其开销。 节点拥有的堆空间越多,它可以处理的数据和分片越多。

    因此,索引和分片从集群的角度看待不是空闲的,因为每个索引和分片都有一定程度的资源开销。

    提示1:小分片会导致小分段(segment),从而增加开销。目的是保持平均分片大小在几GB和几十GB之间。对于具有基于时间的数据的用例,通常看到大小在20GB和40GB之间的分片*。*

    提示2:由于每个分片的开销取决于分段数和大小,通过强制操作迫使较小的段合并成较大的段可以减少开销并提高查询性能。一旦没有更多的数据被写入索引,这应该是理想的。请注意,这是一个消耗资源的(昂贵的)操作,较为理想的处理时段应该在非高峰时段执行。

    提示3:您可以在集群节点上保存的分片数量与您可用的堆内存大小成正比,但这在Elasticsearch中没有的固定限制。 一个很好的经验法则是:确保每个节点的分片数量保持在低于每1GB堆内存对应集群的分片在20-25之间。 因此,具有30GB堆内存的节点最多可以有600-750个分片,但是进一步低于此限制,您可以保持更好。 这通常会帮助群体保持处于健康状态。

    4、分片的大小如何影响性能?

    在Elasticsearch中,每个查询在每个分片的单个线程中执行。然而,可以并行处理多个分片,并可以在相同分片上执行多个查询和聚合。

    【小分片的利弊】这意味着,在不涉及高速缓存时,最小查询延迟将取决于数据、查询的类型、分片的大小。查询大量小分片将使得每个分片的处理速度更快,但是随着更多的任务需要按顺序排队和处理,它不一定要比查询较小数量的更大的分片更快。如果有多个并发查询,则有很多小碎片也会降低查询吞吐量。

    提示:从查询性能角度确定最大分片大小的最佳方法是使用逼真的数据和查询进行基准测试(真实数据而非模拟数据)。 始终使用查询和索引负载进行基准测试,代表节点在生产中需要处理的内容,因为单个查询的优化可能会产生误导性的结果。

    5、如何管理分片大小?

    当使用基于时间的索引时,每个索引传统上都与固定的时间段相关联。 每日索引非常普遍,经常用于持有时间区间短或每日量大的数据。 这些允许数据期限期间以良好的粒度进行管理,并且可以方便地对每天更换调整volumes。

    时间周期长的数据,特别是如果每日不保存每天的索引数据,则通常会使用每周或每月的保存的碎片大小的增加。 这减少了随着时间的流逝需要存储在群集中的索引和碎片数量大小(直译有点费劲此处)。

    提示:如果使用固定期限的时间索引数据,可以根据时间周期预期数据量调整所涵盖的时间范围,以达到目标分片大小。

    【均匀更新&快速变化的索引数据对比】具有固定时间间隔的基于时间的索引在数据量合理预测并且变化缓慢的情况下工作良好。 如果索引率可以快速变化,则很难保持均匀的目标分片大小。

    为了能够更好地处理这种情况,推出了Rollover和Shrink API。 这些增加了如何管理索引和分片的灵活性,尤其适用于基于时间的索引。

    此处省略了 Rollover和Shrink API的介绍。(建议查询官网补齐概念再深入)

    6、结论

    这篇博客文章提供了有关如何在Elasticsearch中最好地管理数据的提示和实用指南。 如果您有兴趣了解更多,推荐阅读Google搜索 “Elasticsearch: the definitive guide” (有点旧,值得阅读)。

    然而,关于如何最好地在索引和分片上分发数据的许多决策将取决于用例细节,有时可能难以确定如何最佳地应用可用的建议。

    文章提及的几个核心建议清单如下,以回答文章开头的提问。

    1) “我应该有多少个分片?”
    答: 每个节点的分片数量保持在低于每1GB堆内存对应集群的分片在20-25之间。
    2) “我的分片应该有多大”?
    答:分片大小为50GB通常被界定为适用于各种用例的限制。
    

    还是读起来有点拗口,一些概念还是不够深入,不能很好的深入浅出的讲解。
    待我实践中更新吧。更多细节,欢迎讨论!

    ——————————————————————————————————
    更多ES相关实战干货经验分享,请扫描下方【铭毅天下】微信公众号二维码关注。
    (每周至少更新一篇!)

    这里写图片描述
    和你一起,死磕Elasticsearch
    ——————————————————————————————————

    2017年09月24日 22:34 于家中床前

    作者:铭毅天下
    转载请标明出处,原文地址:
    http://blog.csdn.net/laoyang360/article/details/78080602
    如果感觉本文对您有帮助,请点击‘喜欢’支持一下,并分享出去,让更多的人受益。您的支持是我坚持写作最大的动力,谢谢!

    展开全文
  • 服务层分片——RDB 分片新思路

    万次阅读 2019-04-19 09:46:14
    目前国内流行的开源数据库分片实现都基于 SQL 的改写、分发与结果归并。但这种实现存在一些无法避免的缺陷,本文试图说明白这些缺陷的由来及提供一个传统数据库分片实现的新思路。 流行分片框架实现分析 目前国内...

    目前国内流行的开源数据库分片实现都基于 SQL 的改写、分发与结果归并。但这种实现存在一些无法避免的缺陷,本文试图说明白这些缺陷的由来及提供一个传统数据库分片实现的新思路。

    流行分片框架实现分析

    目前国内流行的开源 RDB(Relation Database) 分片框架,无论是在 应用端 进行分片还是利用 中间件 进行分片, 其都基于 SQL 进行,主要的流程是:

    1. 解析上层传入的 SQL
    2. 结合对应的分表分库配置,对传入的 SQL 进行改写并分发到对应的单机数据库上
    3. 获得各个单机数据库的返回结果后,根据原 SQL 归并结果,返回用户原 SQL 期待的结果

    这种实现希望从 SQL 层提供一个屏蔽底层分片逻辑的解决方案,对上层应用来说,只有一个 RDB,这样应用可以 透明地访问多个数据库

    然而,这仅仅只是一个美丽的目标。因种种原因,目前流行的开源 SQL 层分片方案无法提供跟原生数据库一样的功能:

    • ACID 里的 A(原子性)无法保证
    • ACID 里的 C(一致性)可能被打破
    • ACID 里的 I(隔离性)与原生不一致
    • 由于 SQL 解析复杂,性能等考虑,很多数据库 SQL 不支持

    除了上面与原生数据库的差异外,读写分离在基于 SQL 的框架也无法完美实现。

    以下我们浅析一下这类基于 SQL 的分片框架不能达到上面要求的原因。

    一些经过大量魔改的商业数据库中间件能实现与单机数据库一致的 AC 特性,I 特性也能达到 RR(Reapeatable Read)级别,但其实现较为复杂,大量处理逻辑都迁移到了中间件中,也不开源,本文暂不讨论。

    原子性

    分片框架如果要保证跨分片的原子性,有两个选择。

    2PC(Two-phase Commit)

    其利用 WriteAheadLog 及锁之类的阻塞等待机制确保分布式事务的强一致性。

    最终一致

    其不要求多个分布式系统对于某个跨库事务马上达到一致的状态,只要最终某   个时间点达到一致即可。

    若使用 2PC,就像大家都听说的,因其需要同步协调处理数据,维护使用的锁,性能会降低(据说 20% 左右)。

    若存在分片节点宕机的情况,会导致与这个分片相关的事务都变慢(其他分片等待这个宕机分片的反馈,超时才回滚),甚至引起雪崩效应,导致整个集群不可用。

    同时 2PC 实现复杂度也较高,据我了解目前开源的实现中没有提供 2PC 的事务支持。

    最终一致可以实现跨分片大事务的最终的原子性。只要发起方分片能正常运作,那么客户操作就能进行。然而,在 SQL 层实现最终一致是不合适的。

    首先,能否使用最终一致是由 业务决定 的,其跟业务设计强耦合;其次基于 SQL 层做最终一致时要求跨分片的 SQL 必须可重复执行(幂等),如

    update account set account.money=1000;

    而不能是

    update account set account.money 
    = account.money + 1000;

    同时若未达到最终一致的情况下,读取了记录,并依赖于此进行了判断及记录修改的话,则会产生错误的数据。单机数据库 ACID 的意义荡然无存。

    我们至少可以推断出,这类在 SQL 层做的最终一致性 越位 了,在 SQL 层无法优雅的实现最终一致,最终一致是服务层(业务层次)的事情。

    因此开源分片框架基本都不保证 A。为了实现跨库的 Update 操作,基于 SQL 的分片框架们创造了一个概念“弱 XA”。其主要实现流程如下:

    • 执行 A 分片的 SQL

    • 执行 B 分片的 SQL

    • 执行 A 库的提交

    • 执行 B 库的提交

    其可以做到:整体事务 COMMIT 前,若有任何异常,都可完美回滚。

    但其存在的缺陷是:当 A 库提交后,B 库失败的话,整个事务就会处于不一致的状态。

    当然,交易量少的时候,这个出错的概率就比较少。但交易量变多的话,这里总会因为网络抖动等各种原因产生错误。

    一致性

    其实一致性是跟原子性有所关联的,只要原子性没能保证,那么一致性肯定是无法保证的。

    隔离级别

    隔离级别在 SQL92 里定义的是这么四个:脏读、读已提交、可重复读、可序列化。

    它们并非无关痛痒的几个数据库参数。隔离级别不一样,对应的在我们的程序里要实现业务一致性的代码逻辑也不一样。

    然而基于 SQL 的分片框架在跨分片访问时提供的事务隔离级别并非原生数据库里定义的,而是一个新的,未被广泛理解认可的事务隔离级别。

    程序员如果没能领悟这个区别,程序执行过程中很有可能就会出现程序员自己也无法理解的错误。

    SQL 兼容性

    由于 SQL 解析的复杂性及性能等因素,基于 SQL 的分片框架有很多 SQL 的解析是不支持的。并且由于 SQL 解析确实很复杂,对于其声明的支持的 SQL 存在 BUG 的可能性也很大。

    至于具体哪些 SQL 不支持,本文就不再分析,大家可以阅读相关框架文档得知。

    读写分离

    读写分离并非原生数据库里支持的功能,但一个较好的读写分离实现应该是能在读库里跑读事务的。

    然而很多人,认为进行只读操作时,加事务是没意义的。如果有这么认识的人,我觉得可能并没有透彻理解 ACID 中的 I。

    举一个例子,一个界面上,需要展示客户的余额以及他的转账记录。如果余额跟转账记录的查询不在一个 RR 级别的事务里进行的话,那么查询出来的结果就有可能出现余额跟转账记录对不上的情况!

    如果是客户 UI 查询还好,刷新一下就好了。但如果是为了生成对账文件,那不就出现大问题了?

    然而,在基于 SQL 层的分片实现中,是没有办法做到完美的读写分离的。因其无法知道这个事务究竟是读事务,还是写事务,因此在第一条 SQL 执行时无法确定要走读库还是写库。

    因此,在基于 SQL 层的分片框架中的做法基本都是:

    • 若有事务,直接走写库

    • 若无事务,直接走读库,若后续发生了 Update 操作,则线程后续操作都走写库

    由此可见,这种读写分离,是不完善的,要实现 RR 级别的读,只能走主库,或者只能返回一个隔离级别混乱的数据集。

    分片新思路――服务层分片

    正因为基于 SQL 的分片框架提供的 ACID 特性与原生的不一样,所以,在上层应用 SQL 编码过程中,必须明确经过分片框架封装后的 ACID 特性是怎样的,并根据该框架的特性,写出对应调整后的 SQL 才能得到正确可靠的代码逻辑。分片框架的特性一直影响着 SQL 的实际形式。

    因此,基于 SQL 的分片方案对应用层并不透明。

    如果要基于 SQL 层的框架写出正确可靠的代码的话,大多数情况下需要遵循的一条原则是:所有事务(包括读、写)都不能跨库。(某些对隔离级别要求不是很高的业务可以允许读跨库)

    如果要遵循上面的指导原则的话,那么,实际上,大多数情况下,我们需要的分片框架提供的仅仅只是选择一个合适的数据源而已。

    结合之前关于读写分离的分析,如果分片实现改在 Service 层,可以清晰地看到会有以下两点好处:

    • Service 层有明确的读 / 写事务信息,能实现理想的 RR 级别读写分离

    • Service 层的入参里就有供选择分片的信息,无需经过复杂的 SQL 解析就可以选定分片,完成我们对分片框架的绝大多数需求

    基于以上两点好处,本人尝试编写了一个基于 Service 层的分片框架,以下展示本人在 Service 层实现分片的一个框架的使用方法,后续我们将更详细地分析该实现的优缺点。

    基本使用方法

    Service 层

    @Service
    @ShardingContext(dataSourceSet="orderSet",
    shardingKeyEls="[user].userId",
    shardingStrategy="@modUserId",
    generateIdStrategy="@snowflaker",
    generateIdEls="[user].userId")
    public class UserServceImpl {
    @Autowired
    private UserDaoImpl userDao;
    @Transactional
    @SelectDataSource
    public void updateUser(User user){
    userDao.updateUser(user);
    }
    @Transactional
    @SelectDataSource
    @GenerateId
    public void saveUser(User user){
    userDao.saveUser(user);
    }
    @Transactional(readOnly=true)
    @SelectDataSource(keyNameEls="[userId]")
    public User findUser(int userId){
    return userDao.findUser(userId);
    }
    public List<User> findAllUsers(){
    return userDao.findAllUsers();
    }
        public double calcUserAvgAge(){
        List<User> allUsers = userDao.findAllUsers();
        return allUsers.stream().mapToInt(u->u.getAge())
    .average().getAsDouble();
        }

    @ShardingContext 表示当前的 Service 的分片上下文,就是说,如果有选择数据源、Map 到各数据库 Reduce 出结果、生成 ID 等操作时,如果某些参数没有指定,都从这个 ShardingContext 里面的配置取

    @SelectDataSource 表示为该方法内执行的 SQL 根据分片策略选择一个分片数据源,在方法结束返回前,不能更改分片数据源

    @GenerateId 表示生成 ID,并将其赋值到参数的指定位置

    @GenerateId 对应的逻辑会先执行,然后到 @SelectDataSource 然后到 @Transaction

    @Transactional(readOnly=true) 标签指定了事务是只读的,因此框架会根据 readOnly标志自动选择读库(如果有的话)

    从方法 calcUserAvgAge 可以看到在 JDK8 的 LAMBADA 表达式及 Stream 功能下,JAVA 分析处理集合数据变得极为简单,复杂度基本与 SQL 语句一致,这会大大减少我们自行加工分片数据的复杂度。

    DAO 层

    @Component
    public class UserDaoImpl {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    public void updateUser(User user){
    int update = jdbcTemplate.update
    ("UPDATE `user` SET `name`=? WHERE 
    `user_id`=?;",user.getName(),user.getUserId());
    Assert.isTrue(update == 1,"it should be updated!");
    }
    public User findUser(int userId){
    return jdbcTemplate.queryForObject
    ("SELECT * FROM user WHERE user_id = ?", 
    new Object[]{userId}, rowMapper);
    }
    @Transactional
    @MapReduce
    public List<User> findAllUsers(){
    return jdbcTemplate.query
    ("SELECT * FROM user", rowMapper);
    }
    @Transactional(readOnly=true)
    @MapReduce
    public void findAllUsers
    (ReduceResultHolder resultHolder){
       List<User> shardingUsers = jdbcTemplate.query
    ("SELECT * FROM user", rowMapper);
       resultHolder.setShardingResult(shardingUsers);
    }
    }

    @MapReduce 表示该方法将会在每个数据分片都执行一遍,然后进行数据聚合后返回。

    对于聚合前后返回的数据类型一致的方法,调用时可以直接从返回值取得聚合结果。

    对于聚合前后返回的数据类型不一致的方法,需要传入一个对象 ReduceResultHolder,调用完成后,通过该对象获得聚合结果。

    默认情况下,框架会提供一个通用 Reduce 策略,如果是数字则累加返回,如果是Collection 及其子类则合并后返回,如果是 MAP 则也是合并后返回。

    如果该策略不适合,那么用户可自行设计指定 Reduce 策略。

    @Transaction 表示每一个分片执行的 SQL 都处于一个事务中,并不是表示整个聚合操作是一个整体的事务。所以,MapReduce 最好不要进行更新操作(考虑框架层次限制 MapReduce 只允许 ReadOnly 事务,目前仍没有做限制)。

    @MapReduce 执行的操作会在 @Transaction 之前。

    优缺点分析

    针对上述实现,我们可以分析一下如此实现的优缺点。

    优点

    • 框架实现简单,无 SQL 解析改写等复杂逻辑,BUG 理论上更少,也容易排查 BUG

      个人认为这至为重要,至少这个简单的框架在使用者的可控范围内。

    • 全数据库、全 SQL 兼容

      SQL 层分片无法做到。

    • 能完美实现读写分离

      Service 层分片在 Service 开始前就能确定该事务是读事务,整个读事务都在一个读库中完成,隔离级别与数据库一致,能实现完美 RR 级别读写分离。

    • 使用注解完成分片,无业务逻辑入侵

      如果注解也算的入侵的话,那么 Spring 就彻底浸入了我们的业务代码里了。

      使用注解能显式的提醒程序员及 REVIEWER,这是在访问单库还是跨库访问,而不像基于 SQL 的分片,隐式完成了跨库这一不安全的操作。

    • 隔离级别及事务原子性等特征与使用的数据库一致,无额外学习负担,易于写出正确的程序

      框架限制了所有事务都在单库进行。

    • 无额外维护 DBProxy 可用性的负担

    • 无 SQL 解析成本,性能更高

    缺点

    • 跨库查询需要自行进行结果聚合

      • 是劣势也是优势

      • 劣势:需要完成额外的聚合代码

      • 优势:显式提示程序员这是聚合操作,且其能能更好地调优, 使用 JDK8 的 Stream 及 Lambada 表达式,能像写 SQL 一样简单地完成相关集合处理。同时合理设计的代码里,聚合操作应该只存在于少量的非核心操作中,因此不会增加太多的工作量。聚合计算出结果这部分内容实际上是可以上升成 Service 层的逻辑的(若不考虑严格的领域设计模型那一套的话)。

    • 跨库事务需要自行保证

      • 是劣势也是优势

      • 劣势:需要额外自行实现跨库事务

      • 优势:目前所有的开源分片框架实现的跨库事务都有缺陷或者说限制。因此自行采用显式的事务控制,结合业务实现最终一致性或许是更好的选择。可参考使用本人写的另外一个框架 EasyTransaction。

    • 无法实现单库分表

    • 其实,单库分表并不是必须的,这可以用数据原生的表分区来实现,性能一样,使用更便捷

    • 如果你拒绝使用数据库原生的表分区,而使用改写表名的形式做库内分片时,你最好能清楚意义在哪里,而不是想当然。

    结语

    实际上,我对基于 SQL 进行分片的框架并非拒绝,我也衷心佩服对这些开源框架做出贡献的大神们,此文仅仅给大家一些新的分片思路。若本文能给大家一些小小的启发,那么希望大家能帮忙给我的两个项目加加星,感谢。

    服务层分片框架

    分布式柔性事务框架

    展开全文
  • 首先我们要移除的分片之后再次添加此分片时会出现添加失败的情况,需要在添加的分片上登录进行删除此分片之前数据库的历史数据比如testdb,删除分片上的数据库之后就可重新添加此分片到mongos中 1、...

    首先我们要移除的分片之后再次添加此分片时会出现添加失败的情况,需要在添加的分片上登录进行删除此分片之前数据库的历史数据比如testdb,删除分片上的数据库之后就可重新添加此分片到mongos中




    1、执行RemoveShard命令 

    db.runCommand( { removeshard: "your_shard_name" } ) 
    { msg : "draining started successfully" , state: "started" , shard :"mongodb0" , ok : 1 } 
    “注意:该命令至少执行两次才能成功删除,执行到state为completed才真正删除,否则就是没用删除成功,该分片处于"draining" : true状态,该状态下不但该分片没用删除成功,而且还影响接下来删除其他分片操作,遇到该状态再执行一次removeshard即可,最好就是删除分片时一直重复执行删除命令,直到state为completed; 

    还有一个需要注意的地方就是:被成功删除的分片如果想要再加入集群时,必须将data数据目录清理干净才可以再加入集群,否则即使能加入成功也不会存储数据,集合都不会被创建 

    另外:在删除分片的时有可能整个过程出现无限"draining" : true状态,等多久还是这样,而且分片上面的块一个都没有移动到别的分片,解决办法是:在config的config数据库的shard集合中找到该分片的信息,并将draining字段由True改为False,再继续试着删除操作” 

    上面这句会立即返回,实际在后台执行。 

    2、查看迁移状态 
    我们可以反复执行上面语句,查看执行结果。 
    db.runCommand( { removeshard: "your_shard_name" } ) { msg: "draining ongoing" , state: "ongoing" , remaining: { chunks: 42, dbs : 1 }, ok: 1 } 
    从上面可以看到,正在迁移,还剩下42块没迁移完。 
    当remain为0之后,这一步就结束了。 

    3、移出非Shard数据(如果开始就知道是primary可以直接执行 步骤3和4即可,1和2不需要执行) 
    如果你要删除的Shard分片恰好是primary,那么执行这一步,否则请跳过! 
    db.runCommand( { movePrimary: "数据库名称", to: "分片名称" }) 

    这次就不是立即返回了,需要很久,然后会返回如下: 
    { "primary" : "mongodb1", "ok" : 1 } 

    4、最后的清理 
    上面步骤都完成后,还需要再执行一次RemoveShard,清理残余数据。 
    db.runCommand( { removeshard: "mongodb0" } ) 
    执行成功后,会如下结果: 
    { msg: "remove shard completed succesfully" , stage: "completed", host: "mongodb0", ok : 1 } 

    显示completed后,就可以安心的关闭mongod的进程了。
    展开全文
  • IP分片

    万次阅读 2020-09-14 23:01:16
    为了使超过此上限的ip数据报能够正常传输,IP引入了“分片”和“重组”。 当IP层接收到要发送的IP数据报时,通过查找“转发表”,会判断该数据报应该从那个本地“接口”发送以及MTU是多少。 不同的网络类型,其...
  • Mongodb分片

    千次阅读 2021-04-12 21:09:42
    Mongodb分片 参考网址 Windows搭建MongoDB分片以及复制集
  • 区块链分片:四种跨分片交易方案

    万次阅读 2019-03-19 16:13:49
    《合约分片执行中的状态问题》一文提到了,跨分片合约的难点在于:一个分片对于共同访问的状态的修改,需要及时地让另一个分片知道,否则就容易出现状态错乱。任何支持合约的分片技术,都必须要解决这个问题。目前...
  • Mongodb分片 1. 分片(sharding)是指将数据拆分,将其分散存放在不同的机器上的过程。有时也用分区(partitioning)来表示这个概念。将数据分散到不同的机器上,不需要功能强大的大型计算机就可以  存储更多的...
  • 我们知道es中保存数据的时候是有主分片和副本分片的,那么副本分片的作用有哪些呢? 1.作为备份,防止主分片崩溃 2.分担查询请求,请求会在主分片和副本分片之间均匀分布 说道第二点,那我们来回答这样一个疑问:两...
  • 最近在做web网盘的系统,网盘最基本的功能便是文件上传,但是文件上传当遇到大文件的时候,在web端按传统方式上传简直是灾难,所以大文件上传可以采用分片上传的办法。其主要思路是:1.大文件上传时进行分片;2.分片...
  • MyCat分片规则之自定义范围分片

    千次阅读 2019-09-04 21:06:48
    实现方式:范围分片,就是我们自己根据某个字段的数值范围来确定这些数据到底存放在哪一个分片上,不过需要我们提前规划好分片字段某个范围属于哪个分片。切分规则根据文件(autopartition-long.txt)配置的范围来进行...
  • mongodb分片

    千次阅读 2020-02-03 14:00:08
    什么是mongodb分片,有什么作用? mongodb分片就是将大型集合分割到不同的服务器上,它可以自动且均衡的分配数据。它一般是针对特别大的需求,比如一个集合需要非常的大,几百个G。分片到10个服务器之后,每个...
  • mycat 离散分片 -> 枚举分片

    千次阅读 2019-07-16 00:02:07
    1,枚举分片 枚举分片:通过在配置文件中配置可能的枚举id,自己配置分片,本规则适用于特定的场景,比如有些业务需要按照省份或区县来做保存,而全国省份区县固定的 2,添加配置文件 在function.xml里面进行...
  • ....分片目前被关注的热度很高,主打分片技术的公链被投资机构热捧, 分片也和Layer 2的侧链、子链、状态通道等方向一起被列入以太坊官方的扩容方案。 1.2分片的原理 分片其实是一种传统数据...
  • 来源 | Hackernoon译者| 火火酱责编 | Carol出品|区块链大本营(blockchain_camp) 以太坊是所有区块链中一直与分片概念同步的底层平...
  • clickhouse 分片

    千次阅读 2019-12-19 21:22:00
    我们知道mysql数据库如果想做分片,需要使用第三方组件,这是因为mysql在设计之初就没有太多考虑分布式等问题。而clickhouse作为新生代性能之王,分片也是必须的功能。基本上从2015年之后的各种数据库也罢,框架也罢...
  • MySQL的分片(二)——MySQL分片

    万次阅读 2018-10-18 14:25:34
    第一,了解MySQL分片可以更合理地定制分片策略,选分片字段是要讲科学的。 第二,了解MySQL分片以后如果出现故障报错,也有助于问题的排查。 第三,关系到开发过程中的代码调整,做分片后的MySQL数据库操作受到...
  • 一、分片与副本设置 1、分片(shard) Elasticsearch集群允许系统存储的数据量超过单机容量,实现这一目标引入分片策略shard。在一个索引index中,数据(document)被分片处理(sharding)到多个分片上。Elastic...
  • 自定义数字范围分片,提前规划好分片字段某个范围属于哪个分片,比如说将第一个500W的数据分片在第一个节点上面,第二个500W的数据分片在第二个节点上,依次类推 2,添加配置文件 在function.xml里面进行配置: ...
  • IP报文在分片后,遇到MTU还小于分片报文长度的路由器,会继续分片吗? 会怎么处理这种报文?
  • 1 MyCat分片规则 数据切分中重要的几条原则,其中有几条数据冗余,表分组(Table Group)。 1.1全局表 如果你的业务中有些数据类似于数据字典,比如配置文件的配置,常用业务的配置或数据量不是很大,很少变动...
  • mongoDB何时使用分片及如何分片

    千次阅读 2018-02-23 17:07:22
    暂时只有何时考虑分片,以后补上如何分片的操作。分片(sharding)是指将数据拆分,将其分散存在不同机器上的过程。那么何时才考虑分片呢,出现如下问题时就该考虑使用分片:1). 当数据量达到T级别的时候,我们的磁盘,...
  • 取模分片,简单来讲,根据数据库的主键和存储的节点数进行取模操作,然后根据取模的结果,将数据存放到对应的节点中,取模分表,可以将数据均匀的分配到各个库中。实现的步骤:1、创建数据库,2、配置schema.xml文件...
  • 干货 | 区块链分片的理念与挑战,Part-1 在连载中的第一部分,我们讨论了为什么要做区块链分片及其他的重要概念。在本文中,我们从更前沿的角度来看分片,包含分片尚未解决的两大挑战:数据有效性和数据可用性。 ...
  • MongoDB-分片片键

    千次阅读 2019-02-24 17:44:34
    MongoDB-分片片键 1.分片  分片是什么?分片就是将数据存储在多个机器上。当数据集超过单台服务器的容量,服务器的内存,磁盘IO都会有问题,即超过单台服务器的性能瓶颈。此时有两种解决方案,垂直扩展和水平...
  • 分片报文

    千次阅读 2016-05-31 20:12:11
    分片模块按照SIP+DIP+ID号+PROTO转发。前中后三片必须ID号一致,协议一致。1.分片首片配置如下:偏移位:0;字节为838;2.中片配置如下:偏移位:100;*8为800;字节为838;3.尾片配置如下:偏移位:200;*8为1600;...
  • TCP的分片和IP分片的区别

    千次阅读 2019-04-01 20:46:07
    IP分片与重组 在IP首部中,标识、标志、片偏移这三部分就是与分片的字段。 标识(Identification): 占16位。IP软件在存储器中维持一个计数器,每产生一个数据报,计数器就加1,并将此值赋给标识字段。但这个...
  • MongoDB分片

    2017-01-20 10:55:01
    分片集群 分片键 数据块 分片的优势 分片前的考虑 分片集合与非分片集合 连接分片集群 分片策略 分片集群中的分片键值空间 分片的字符比较排序规则 其它资源分片是一种将数据分散存储到多台机器的方法,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 46,373
精华内容 18,549
关键字:

分片