精华内容
下载资源
问答
  • 分布式主键 Leaf-segment

    2019-09-28 15:27:33
    对于MySQL性能问题,可用如下方案解决:在分布式系统中我们可以多部署几台机器,每台机器设置不同的初始值,且步长和机器数相等。比如有两台机器。设置步长step为2,TicketServer1的初始值为1(1,3,5,7,9,11…...

    数据库生成

    以MySQL举例,利用给字段设置auto_increment_incrementauto_increment_offset来保证ID自增,每次业务使用下列SQL读写MySQL得到ID号。

    begin;
    REPLACE INTO Tickets64 (stub) VALUES ('a'); SELECT LAST_INSERT_ID(); commit; 

    image

     

    这种方案的优缺点如下:

    优点:

    • 非常简单,利用现有数据库系统的功能实现,成本小,有DBA专业维护。
    • ID号单调自增,可以实现一些对ID有特殊要求的业务。

    缺点:

    • 强依赖DB,当DB异常时整个系统不可用,属于致命问题。配置主从复制可以尽可能的增加可用性,但是数据一致性在特殊情况下难以保证。主从切换时的不一致可能会导致重复发号。
    • ID发号性能瓶颈限制在单台MySQL的读写性能。

    对于MySQL性能问题,可用如下方案解决:在分布式系统中我们可以多部署几台机器,每台机器设置不同的初始值,且步长和机器数相等。比如有两台机器。设置步长step为2,TicketServer1的初始值为1(1,3,5,7,9,11…)、TicketServer2的初始值为2(2,4,6,8,10…)。这是Flickr团队在2010年撰文介绍的一种主键生成策略(Ticket Servers: Distributed Unique Primary Keys on the Cheap )。如下所示,为了实现上述方案分别设置两台机器对应的参数,TicketServer1从1开始发号,TicketServer2从2开始发号,两台机器每次发号之后都递增2。

    TicketServer1:
    auto-increment-increment = 2
    auto-increment-offset = 1
    
    TicketServer2:
    auto-increment-increment = 2
    auto-increment-offset = 2
    

    假设我们要部署N台机器,步长需设置为N,每台的初始值依次为0,1,2…N-1那么整个架构就变成了如下图所示:

    image

    image

     

    这种架构貌似能够满足性能的需求,但有以下几个缺点:

    • 系统水平扩展比较困难,比如定义好了步长和机器台数之后,如果要添加机器该怎么做?假设现在只有一台机器发号是1,2,3,4,5(步长是1),这个时候需要扩容机器一台。可以这样做:把第二台机器的初始值设置得比第一台超过很多,比如14(假设在扩容时间之内第一台不可能发到14),同时设置步长为2,那么这台机器下发的号码都是14以后的偶数。然后摘掉第一台,把ID值保留为奇数,比如7,然后修改第一台的步长为2。让它符合我们定义的号段标准,对于这个例子来说就是让第一台以后只能产生奇数。扩容方案看起来复杂吗?貌似还好,现在想象一下如果我们线上有100台机器,这个时候要扩容该怎么做?简直是噩梦。所以系统水平扩展方案复杂难以实现。
    • ID没有了单调递增的特性,只能趋势递增,这个缺点对于一般业务需求不是很重要,可以容忍。
    • 数据库压力还是很大,每次获取ID都得读写一次数据库,只能靠堆机器来提高性能。

    Leaf这个名字是来自德国哲学家、数学家莱布尼茨的一句话: >There are no two identical leaves in the world > “世界上没有两片相同的树叶”

    综合对比上述几种方案,每种方案都不完全符合我们的要求。所以Leaf分别在上述第二种和第三种方案上做了相应的优化,实现了Leaf-segment和Leaf-snowflake方案。

    Leaf-segment数据库方案

    第一种Leaf-segment方案,在使用数据库的方案上,做了如下改变: - 原方案每次获取ID都得读写一次数据库,造成数据库压力大。改为利用proxy server批量获取,每次获取一个segment(step决定大小)号段的值。用完之后再去数据库获取新的号段,可以大大的减轻数据库的压力。 - 各个业务不同的发号需求用biz_tag字段来区分,每个biz-tag的ID获取相互隔离,互不影响。如果以后有性能需求需要对数据库扩容,不需要上述描述的复杂的扩容操作,只需要对biz_tag分库分表就行。

    数据库表设计如下:

    +-------------+--------------+------+-----+-------------------+-----------------------------+
    | Field       | Type         | Null | Key | Default           | Extra                       |
    +-------------+--------------+------+-----+-------------------+-----------------------------+
    | biz_tag     | varchar(128) | NO   | PRI |                   |                             |
    | max_id      | bigint(20)   | NO   |     | 1                 |                             |
    | step        | int(11)      | NO   |     | NULL              |                             |
    | desc        | varchar(256) | YES  |     | NULL              |                             |
    | update_time | timestamp    | NO   |     | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
    +-------------+--------------+------+-----+-------------------+-----------------------------+ 

    重要字段说明:biz_tag用来区分业务,max_id表示该biz_tag目前所被分配的ID号段的最大值,step表示每次分配的号段长度。原来获取ID每次都需要写数据库,现在只需要把step设置得足够大,比如1000。那么只有当1000个号被消耗完了之后才会去重新读写一次数据库。读写数据库的频率从1减小到了1/step,大致架构如下图所示:

    image

    image

     

    test_tag在第一台Leaf机器上是1~1000的号段,当这个号段用完时,会去加载另一个长度为step=1000的号段,假设另外两台号段都没有更新,这个时候第一台机器新加载的号段就应该是3001~4000。同时数据库对应的biz_tag这条数据的max_id会从3000被更新成4000,更新号段的SQL语句如下:

    Begin
    UPDATE table SET max_id=max_id+step WHERE biz_tag=xxx SELECT tag, max_id, step FROM table WHERE biz_tag=xxx Commit 

    这种模式有以下优缺点:

    优点:

    • Leaf服务可以很方便的线性扩展,性能完全能够支撑大多数业务场景。
    • ID号码是趋势递增的8byte的64位数字,满足上述数据库存储的主键要求。
    • 容灾性高:Leaf服务内部有号段缓存,即使DB宕机,短时间内Leaf仍能正常对外提供服务。
    • 可以自定义max_id的大小,非常方便业务从原有的ID方式上迁移过来。

    缺点:

    • ID号码不够随机,能够泄露发号数量的信息,不太安全。
    • TP999数据波动大,当号段使用完之后还是会hang在更新数据库的I/O上,tg999数据会出现偶尔的尖刺。
    • DB宕机会造成整个系统不可用。

    双buffer优化

    对于第二个缺点,Leaf-segment做了一些优化,简单的说就是:

    Leaf 取号段的时机是在号段消耗完的时候进行的,也就意味着号段临界点的ID下发时间取决于下一次从DB取回号段的时间,并且在这期间进来的请求也会因为DB号段没有取回来,导致线程阻塞。如果请求DB的网络和DB的性能稳定,这种情况对系统的影响是不大的,但是假如取DB的时候网络发生抖动,或者DB发生慢查询就会导致整个系统的响应时间变慢。

    为此,我们希望DB取号段的过程能够做到无阻塞,不需要在DB取号段的时候阻塞请求线程,即当号段消费到某个点时就异步的把下一个号段加载到内存中。而不需要等到号段用尽的时候才去更新号段。这样做就可以很大程度上的降低系统的TP999指标。详细实现如下图所示:

    image

    image

     

    采用双buffer的方式,Leaf服务内部有两个号段缓存区segment。当前号段已下发10%时,如果下一个号段未更新,则另启一个更新线程去更新下一个号段。当前号段全部下发完后,如果下个号段准备好了则切换到下个号段为当前segment接着下发,循环往复。

    • 每个biz-tag都有消费速度监控,通常推荐segment长度设置为服务高峰期发号QPS的600倍(10分钟),这样即使DB宕机,Leaf仍能持续发号10-20分钟不受影响。

    • 每次请求来临时都会判断下个号段的状态,从而更新此号段,所以偶尔的网络抖动不会影响下个号段的更新。

    Leaf高可用容灾

    对于第三点“DB可用性”问题,我们目前采用一主两从的方式,同时分机房部署,Master和Slave之间采用半同步方式[5]同步数据。同时使用公司Atlas数据库中间件(已开源,改名为DBProxy)做主从切换。当然这种方案在一些情况会退化成异步模式,甚至在非常极端情况下仍然会造成数据不一致的情况,但是出现的概率非常小。如果你的系统要保证100%的数据强一致,可以选择使用“类Paxos算法”实现的强一致MySQL方案,如MySQL 5.7前段时间刚刚GA的MySQL Group Replication。但是运维成本和精力都会相应的增加,根据实际情况选型即可。

    image

     

    同时Leaf服务分IDC部署,内部的服务化框架是“MTthrift RPC”。服务调用的时候,根据负载均衡算法会优先调用同机房的Leaf服务。在该IDC内Leaf服务不可用的时候才会选择其他机房的Leaf服务。同时服务治理平台OCTO还提供了针对服务的过载保护、一键截流、动态流量分配等对服务的保护措施。

    Leaf-segment方案可以生成趋势递增的ID,同时ID号是可计算的,不适用于订单ID生成场景,比如竞对在两天中午12点分别下单,通过订单id号相减就能大致计算出公司一天的订单量,这个是不能忍受的。

     

    原文:https://tech.meituan.com/2017/04/21/mt-leaf.html

     

    转载于:https://www.cnblogs.com/yx88/p/11285639.html

    展开全文
  • 美团 Leaf分布式ID解决方案

    千次阅读 2019-08-28 21:47:56
    其中参考博客就是美团的分布式ID leaf的链接,可以直接跳转去看。 Leaf-segment 数据库方案 这里采用的是从数据库读取,每次从数据库里读取id起始点和步长,比如读取id为1000,步长为1000,那么可以生成的分布式id...

    前言

    看了一下美团的分布式ID的解决方案,谈谈自己的理解和思考。其中参考博客就是美团的分布式ID leaf的链接,可以直接跳转去看。

    Leaf-segment 数据库方案

    这里采用的是从数据库读取,每次从数据库里读取id起始点和步长,比如读取id为1000,步长为1000,那么可以生成的分布式id范围为1000 - 2000 。但不仅仅是这么简单的数字,一般形式如下:

    biz_tag + id
    
    • biz_tag:业务标识符,一般为字符串,表明业务
    • id:就是我们说的id数值
      上述两个合在一起就是一个分布式ID,所以我们的分布式ID 数据库表设计为:
      | 表字段 | 解释 |
      | ---- | ---- |
      |id| 主键id ,该条记录的id |
      | biz_tag | 业务标识符,一般为字符串,表明业务 |
      | max_id | 就是我们说的id数值,也是起始点 |
      | step | 步长,用来表明范围 |
      | desc | 描述信息 |
      | update_time | 更新时间 |

    每次先更新再读取数据:

    Begin
    UPDATE table SET max_id=max_id+step WHERE biz_tag=xxx    //   先更新
    SELECT tag, max_id, step FROM table WHERE biz_tag=xxx       //   查询
    Commit
    

    优点:

    • Leaf服务可以很方便的线性扩展,性能完全能够支撑大多数业务场景,比如业务扩展,可能其他场景也会用到。
    • ID号码是趋势递增的8byte的64位数字,满足上述数据库存储的主键要求。
    • 容灾性高:Leaf服务内部有号段缓存,即使DB宕机,短时间内Leaf仍能正常对外提供服务。
    • 可以自定义max_id的大小,非常方便业务从原有的ID方式上迁移过来。

    缺点:

    • ID号码不够随机,能够泄露发号数量的信息,不太安全,连续增长的,下一个id可以被预见。
    • TP999数据波动大,当号段使用完之后还是会hang在更新数据库的 I/O 上,tg 999 数据会出现偶尔的尖刺。
    • DB宕机会造成整个系统不可用,其实可以持续一段时间,不至于立马不可用,因为有缓存,缓存中有id范围,只有当id被用完了,从数据库中读取新的id时才会导致系统不可用。
    • 当id用完了,要读取数据库时,这个时候大量请求来了,都会堵塞。

    双buffer优化 - Leaf-segment

    针对前一个Leaf - segment 方案中,有大量请求时存在堵塞情况,有人就提出了双buffer的方法,来优化这个逻辑,双buffer听起来很奇怪,简单描述说就是两个数组,每个数组里都有id初始值和步长,用完了一个数组,就用另一个数组,大概流程就是:

    • 1、在A数组里生成id
    • 2、当A数组还剩下80%的 id 可用的时候哦,查看B数组是否有可用Id,
      如果没有,就异步线层去数据库里读,
      如果有,就没事
    • 3、A数组Id用完了就读取B数组,同样也去更新A数组内容。
    • 4、 这样来回循环


    每个biz-tag都有消费速度监控,通常推荐segment长度设置为服务高峰期发号QPS的600倍(10分钟),这样即使DB宕机,Leaf 仍能持续发号10-20分钟不受影响。

    每次请求来临时都会判断下个号段的状态,从而更新此号段,所以偶尔的网络抖动不会影响下个号段的更新。

    Leaf-snowflake服务

    Leaf-segment方案可以生成趋势递增的ID,同时ID号是可计算的,不适用于订单ID生成场景,比如对在两天中午12点分别下单,通过订单id号相减就能大致计算出公司一天的订单量,这个是不能忍受的。面对这一问题,我们提供了 Leaf-snowflake 方案。Leaf-snowflake方案完全沿用snowflake方案的bit位设计,即是“1+41+10+12”的方式组装ID号。

    Leaf服务规模较大,动手配置成本太高。所以使用Zookeeper持久顺序节点的特性自动对snowflake节点配置wokerID,然后把IP + Port 作为临时节点,有可能wokerID下面有多个机器,每个机器都有一个IP。

    其中也讨论了关于时钟回拨的问题,将系统时间写入到zookeeper中,代码对时间进行判断是否有回拨问题。但是我很奇怪它将系统时间写入到workID下面,而不是机器IP下面,不过猜想有可能是因为系统时间只要有一个就行了,不需要多个,因为对于workID来说,业务来讲只要有一个时间戳就行,如果workID是一个集群,那么处理这个请求的也只有一台机器。所以有可能是基于这个考虑记录到workID下。

    参考博客

    Leaf——美团点评分布式ID生成系统

    展开全文
  • 9种分布式ID生成之美团(Leaf)实战

    千次阅读 2020-02-28 15:34:51
    整理了一些Java方面的架构、面试资料(微服务、集群、分布式、中间件等),有需要的小伙伴可以关注公众号【程序员内点事】,无套路自行领取 更多优选 一口气说出 9种 分布式ID生成方式,面试官有点懵了 面试总被问...

    整理了一些Java方面的架构、面试资料(微服务、集群、分布式、中间件等),有需要的小伙伴可以关注公众号【程序员内点事】,无套路自行领取

    更多优选

    引言

    前几天写过一篇《一口气说出 9种 分布式ID生成方式,面试官有点懵了》,里边简单的介绍了九种分布式ID生成方式,但是对于像美团(Leaf)滴滴(Tinyid)百度(uid-generator)都是一笔带过。而通过读者留言发现,大家普遍对他们哥三更感兴趣,所以后边会结合实战,详细的对三种分布式ID生成器学习,今天先啃下美团(Leaf)

    不了解分布式ID的同学,先行去看《一口气说出 9种 分布式ID生成方式,面试官有点懵了》温习一下基础知识,这里就不再赘述了

    美团(Leaf)

    Leaf是美团推出的一个分布式ID生成服务,名字取自德国哲学家、数学家莱布尼茨的一句话:“There are no two identical leaves in the world.”(“世界上没有两片相同的树叶”),取个名字都这么有寓意,美团程序员牛掰啊!

    Leaf的优势:高可靠低延迟全局唯一等特点。

    目前主流的分布式ID生成方式,大致都是基于数据库号段模式雪花算法(snowflake),而美团(Leaf)刚好同时兼具了这两种方式,可以根据不同业务场景灵活切换。

    接下来结合实战,详细的介绍一下LeafLeaf-segment号段模式Leaf-snowflake模式

    一、 Leaf-segment号段模式

    Leaf-segment号段模式是对直接用数据库自增ID充当分布式ID的一种优化,减少对数据库的频率操作。相当于从数据库批量的获取自增ID,每次从数据库取出一个号段范围,例如 (1,1000] 代表1000个ID,业务服务将号段在本地生成1~1000的自增ID并加载到内存.。

    大致的流程入下图所示:
    在这里插入图片描述
    号段耗尽之后再去数据库获取新的号段,可以大大的减轻数据库的压力。对max_id字段做一次update操作,update max_id= max_id + step,update成功则说明新号段获取成功,新的号段范围是(max_id ,max_id +step]。

    由于依赖数据库,我们先设计一下表结构:

    CREATE TABLE `leaf_alloc` (
      `biz_tag` varchar(128) NOT NULL DEFAULT '' COMMENT '业务key',
      `max_id` bigint(20) NOT NULL DEFAULT '1' COMMENT '当前已经分配了的最大id',
      `step` int(11) NOT NULL COMMENT '初始步长,也是动态调整的最小步长',
      `description` varchar(256) DEFAULT NULL COMMENT '业务key的描述',
      `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '数据库维护的更新时间',
      PRIMARY KEY (`biz_tag`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    

    预先插入一条测试的业务数据

    INSERT INTO `leaf_alloc` (`biz_tag`, `max_id`, `step`, `description`, `update_time`) VALUES ('leaf-segment-test', '0', '10', '测试', '2020-02-28 10:41:03');
    
    • biz_tag:针对不同业务需求,用biz_tag字段来隔离,如果以后需要扩容时,只需对biz_tag分库分表即可

    • max_id:当前业务号段的最大值,用于计算下一个号段

    • step:步长,也就是每次获取ID的数量

    • description:对于业务的描述,没啥好说的

    将Leaf项目下载到本地:https://github.com/Meituan-Dianping/Leaf

    修改一下项目中的leaf.properties文件,添加数据库配置

    leaf.name=com.sankuai.leaf.opensource.test
    leaf.segment.enable=true
    leaf.jdbc.url=jdbc:mysql://127.0.0.1:3306/xin-master?useUnicode=true&characterEncoding=utf8
    leaf.jdbc.username=junkang
    leaf.jdbc.password=junkang
    
    leaf.snowflake.enable=false
    

    注意leaf.snowflake.enableleaf.segment.enable 是无法同时开启的,否则项目将无法启动。

    配置相当的简单,直接启动LeafServerApplication后就OK了,接下来测试一下,leaf是基于Http请求的发号服务, LeafController 中只有两个方法,一个号段接口,一个snowflake接口,key就是数据库中预先插入的业务biz_tag

    
    @RestController
    public class LeafController {
        private Logger logger = LoggerFactory.getLogger(LeafController.class);
    
        @Autowired
        private SegmentService segmentService;
        @Autowired
        private SnowflakeService snowflakeService;
    
        /**
         * 号段模式
         * @param key
         * @return
         */
        @RequestMapping(value = "/api/segment/get/{key}")
        public String getSegmentId(@PathVariable("key") String key) {
            return get(key, segmentService.getId(key));
        }
    
        /**
         * 雪花算法模式
         * @param key
         * @return
         */
        @RequestMapping(value = "/api/snowflake/get/{key}")
        public String getSnowflakeId(@PathVariable("key") String key) {
            return get(key, snowflakeService.getId(key));
        }
    
        private String get(@PathVariable("key") String key, Result id) {
            Result result;
            if (key == null || key.isEmpty()) {
                throw new NoKeyException();
            }
            result = id;
            if (result.getStatus().equals(Status.EXCEPTION)) {
                throw new LeafServerException(result.toString());
            }
            return String.valueOf(result.getId());
        }
    }
    

    访问:http://127.0.0.1:8080/api/segment/get/leaf-segment-test,结果正常返回,感觉没毛病,但当查了一下数据库表中数据时发现了一个问题。
    在这里插入图片描述
    在这里插入图片描述
    通常在用号段模式的时候,取号段的时机是在前一个号段消耗完的时候进行的,可刚刚才取了一个ID,数据库中却已经更新了max_id,也就是说leaf已经多获取了一个号段,这是什么鬼操作?
    在这里插入图片描述

    Leaf为啥要这么设计呢?

    Leaf 希望能在DB中取号段的过程中做到无阻塞!

    当号段耗尽时再去DB中取下一个号段,如果此时网络发生抖动,或者DB发生慢查询,业务系统拿不到号段,就会导致整个系统的响应时间变慢,对流量巨大的业务,这是不可容忍的。

    所以Leaf在当前号段消费到某个点时,就异步的把下一个号段加载到内存中。而不需要等到号段用尽的时候才去更新号段。这样做很大程度上的降低了系统的风险。

    那么某个点到底是什么时候呢?

    这里做了一个实验,号段设置长度为step=10max_id=1
    在这里插入图片描述
    当我拿第一个ID时,看到号段增加了,1/10
    在这里插入图片描述
    在这里插入图片描述
    当我拿第三个Id时,看到号段又增加了,3/10
    在这里插入图片描述
    在这里插入图片描述
    Leaf采用双buffer的方式,它的服务内部有两个号段缓存区segment。当前号段已消耗10%时,还没能拿到下一个号段,则会另启一个更新线程去更新下一个号段。

    简而言之就是Leaf保证了总是会多缓存两个号段,即便哪一时刻数据库挂了,也会保证发号服务可以正常工作一段时间。

    在这里插入图片描述
    通常推荐号段(segment)长度设置为服务高峰期发号QPS的600倍(10分钟),这样即使DB宕机,Leaf仍能持续发号10-20分钟不受影响。

    优点:

    • Leaf服务可以很方便的线性扩展,性能完全能够支撑大多数业务场景。
    • 容灾性高:Leaf服务内部有号段缓存,即使DB宕机,短时间内Leaf仍能正常对外提供服务。

    缺点:

    • ID号码不够随机,能够泄露发号数量的信息,不太安全。
    • DB宕机会造成整个系统不可用(用到数据库的都有可能)。

    二、Leaf-snowflake

    Leaf-snowflake基本上就是沿用了snowflake的设计,ID组成结构:正数位(占1比特)+ 时间戳(占41比特)+ 机器ID(占5比特)+ 机房ID(占5比特)+ 自增值(占12比特),总共64比特组成的一个Long类型。

    Leaf-snowflake不同于原始snowflake算法地方,主要是在workId的生成上,Leaf-snowflake依靠Zookeeper生成workId,也就是上边的机器ID(占5比特)+ 机房ID(占5比特)。Leaf中workId是基于ZooKeeper的顺序Id来生成的,每个应用在使用Leaf-snowflake时,启动时都会都在Zookeeper中生成一个顺序Id,相当于一台机器对应一个顺序节点,也就是一个workId。

    在这里插入图片描述
    Leaf-snowflake启动服务的过程大致如下:

    • 启动Leaf-snowflake服务,连接Zookeeper,在leaf_forever父节点下检查自己是否已经注册过(是否有该顺序子节点)。
    • 如果有注册过直接取回自己的workerID(zk顺序节点生成的int类型ID号),启动服务。
    • 如果没有注册过,就在该父节点下面创建一个持久顺序节点,创建成功后取回顺序号当做自己的workerID号,启动服务。

    Leaf-snowflake对Zookeeper是一种弱依赖关系,除了每次会去ZK拿数据以外,也会在本机文件系统上缓存一个workerID文件。一旦ZooKeeper出现问题,恰好机器出现故障需重启时,依然能够保证服务正常启动。

    启动Leaf-snowflake模式也比较简单,起动本地ZooKeeper,修改一下项目中的leaf.properties文件,关闭leaf.segment模式,启用leaf.snowflake模式即可。

    leaf.segment.enable=false
    #leaf.jdbc.url=jdbc:mysql://127.0.0.1:3306/xin-master?useUnicode=true&characterEncoding=utf8
    #leaf.jdbc.username=junkang
    #leaf.jdbc.password=junkang
    
    leaf.snowflake.enable=true
    leaf.snowflake.zk.address=127.0.0.1
    leaf.snowflake.port=2181
    
        /**
         * 雪花算法模式
         * @param key
         * @return
         */
        @RequestMapping(value = "/api/snowflake/get/{key}")
        public String getSnowflakeId(@PathVariable("key") String key) {
            return get(key, snowflakeService.getId(key));
        }
    
    

    测试一下,访问:http://127.0.0.1:8080/api/snowflake/get/leaf-segment-test
    在这里插入图片描述
    优点:

    • ID号码是趋势递增的8byte的64位数字,满足上述数据库存储的主键要求。

    缺点:

    • 依赖ZooKeeper,存在服务不可用风险(实在不知道有啥缺点了)

    三、Leaf监控

    请求地址:http://127.0.0.1:8080/cache

    针对服务自身的监控,Leaf提供了Web层的内存数据映射界面,可以实时看到所有号段的下发状态。比如每个号段双buffer的使用情况,当前ID下发到了哪个位置等信息都可以在Web界面上查看。

    在这里插入图片描述

    总结

    对于Leaf具体使用哪种模式,还是根据具体的业务场景使用,本文并没有对Leaf源码做过多的分析,因为Leaf 代码量简洁很好阅读。后续还会把其他几种分布式ID生成器,依次结合实战介绍给大家,欢迎大家关注。


    今天就说这么多,如果本文对您有一点帮助,希望能得到您一个点赞👍哦

    您的认可才是我写作的动力!


    整理了一些Java方面的架构、面试资料(微服务、集群、分布式、中间件等),有需要的小伙伴可以关注公众号【程序员内点事】,无套路自行领取

    展开全文
  • 分库分表与分布式主键 分库分表Apache ShardingSphere 为什么要分库分表 分库分表使用分类 Sharding JDBC Sharding Proxy 分布式主键 为什么使用分布式主键 分布式主键方案 UUID SNOWFLAKE Leaf使用 分库分表Apache ...

    分库分表Apache ShardingSphere

    为什么要分库分表

    1. mysql索引采用 B + Tree
    2. 系统从硬盘读取数据到内存是以磁盘块(block)为基本单位,Mysql的innodb引擎有页的概念,默认每页大小为16KB(show variables like ‘innodb_page_size’; 可通过该命令查看),磁盘块往往没有达到16KB,innodb每次会读取若干连续磁盘块达到16KB,即读取是以页(16KB)为单位
    3. 索引一般为4个字节或8个字节,主键一般使用bigint 8字节,即1页可以读取16KB/(8+6)=1170个主键,叶子节点存放数据(按单行1KB算,存16个),深度为3(innodb默认根节点在内存中,读取硬盘进行2次I/O操作)的话,能存储1170117016≈2000W数据,再多数据会增加深度,影响查询效率
    4. 总上分库分表,增加效率

    分库分表使用分类

    1. Sharding JDBC 代码层面维护库与表的关系
    2. Sharding Proxy 代理方式,正常使用SQL,经过Proxy转化后操作
    3. K8S 直接使用newSQL(如TIDB)
    Sharding-JDBCSharding-Proxy
    数据库任意MySQL
    连接消耗数
    异构语言仅 Java任意
    性能损耗低损耗略高
    无中心化
    静态入口

    Sharding JDBC

    1. 执行sql脚本,创建库和表(库放在不同服务器上可提供瓶颈)
    CREATE DATABASE `myshop-0` DEFAULT CHARACTER SET utf8 ;
    USE `myshop-0`;
    CREATE TABLE tb_order_0 (id BIGINT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY, order_id BIGINT(20) NOT NULL, user_id BIGINT(20) NOT NULL);
    CREATE TABLE tb_order_1 (id BIGINT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY, order_id BIGINT(20) NOT NULL, user_id BIGINT(20) NOT NULL);
    CREATE TABLE tb_order_item_0 (id BIGINT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY, user_id BIGINT(20) NOT NULL, order_id BIGINT(20) NOT NULL, order_item_id BIGINT(20) NOT NULL);
    CREATE TABLE tb_order_item_1 (id BIGINT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY, user_id BIGINT(20) NOT NULL, order_id BIGINT(20) NOT NULL, order_item_id BIGINT(20) NOT NULL);
    
    CREATE DATABASE `myshop-1` DEFAULT CHARACTER SET utf8 ;
    USE `myshop-1`;
    CREATE TABLE tb_order_0 (id BIGINT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY, order_id BIGINT(20) NOT NULL, user_id BIGINT(20) NOT NULL);
    CREATE TABLE tb_order_1 (id BIGINT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY, order_id BIGINT(20) NOT NULL, user_id BIGINT(20) NOT NULL);
    CREATE TABLE tb_order_item_0 (id BIGINT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY, user_id BIGINT(20) NOT NULL, order_id BIGINT(20) NOT NULL, order_item_id BIGINT(20) NOT NULL);
    CREATE TABLE tb_order_item_1 (id BIGINT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY, user_id BIGINT(20) NOT NULL, order_id BIGINT(20) NOT NULL, order_item_id BIGINT(20) NOT NULL);
    
    
    1. 添加依赖
    		<dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>2.0.1</version>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <!-- MySQL 驱动的版本号必须是 5.1.48 -->
                <version>5.1.48</version>
            </dependency>
            <dependency>
                <groupId>org.apache.shardingsphere</groupId>
                <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
                <version>4.0.0-RC3</version>
            </dependency>
    
    1. 配置分库分表规则
      application.yml
    spring:
      main:
        allow-bean-definition-overriding: true
      application:
        name: sharding-jdbc
      shardingsphere:
        # 属性配置
        props:
          # 是否开启 SQL 显示,默认值: false
          sql:
            show: true
        # 数据源配置,可配置多个
        datasource:
          # 本案例中配置了两个数据源,分别对应刚才创建的两个 MySQL 容器
          names: db0,db1
          db0:
            type: com.zaxxer.hikari.HikariDataSource
            driver-class-name: com.mysql.jdbc.Driver
            jdbc-url: jdbc:mysql://192.168.1.150:3306/myshop-0?useUnicode=true&characterEncoding=utf-8&serverTimezone=Hongkong&useSSL=false
            username: root
            password: '123456'
            hikari:
              minimum-idle: 5
              idle-timeout: 600000
              maximum-pool-size: 10
              auto-commit: true
              pool-name: MyHikariCP
              max-lifetime: 1800000
              connection-timeout: 30000
              connection-test-query: SELECT 1
          db1:
            type: com.zaxxer.hikari.HikariDataSource
            driver-class-name: com.mysql.jdbc.Driver
            jdbc-url: jdbc:mysql://192.168.1.150:3306/myshop-1?useUnicode=true&characterEncoding=utf-8&serverTimezone=Hongkong&useSSL=false
            username: root
            password: '123456'
            hikari:
              minimum-idle: 5
              idle-timeout: 600000
              maximum-pool-size: 10
              auto-commit: true
              pool-name: MyHikariCP
              max-lifetime: 1800000
              connection-timeout: 30000
              connection-test-query: SELECT 1
        # 分片规则配置
        sharding:
          # 绑定表规则列表
          binding-tables: tb_order,tb_order_item
          # 默认数据库分片策略,同分库策略
          default-database-strategy:
            inline:
              # 分片算法行表达式,需符合 groovy 语法
              # 此处根据 user_id 分片
              # 如果 user_id 为奇数则落入奇数库即 db1 匹配的数据源
              # 如果 user_id 为偶数则落入偶数库即 db0 匹配的数据源
              algorithm-expression: db$->{user_id % 2}
              # 分片列名称
              sharding-column: user_id
          # 数据分片规则配置,可配置多个
          tables:
            # 逻辑表名称
            tb_order:
              # 由数据源名 + 表名组成,以小数点分隔
              actual-data-nodes: db$->{0..1}.tb_order_$->{0..1}
              # 分表策略,同分库策略
              table-strategy:
                inline:
                  # 此处根据 order_id 分片
                  # 如果 order_id 为奇数则落入奇数表即 tb_order_1
                  # 如果 order_id 为偶数则落入偶数表即 tb_order_0
                  algorithm-expression: tb_order_$->{order_id % 2}
                  # 分片列名称
                  sharding-column: order_id
            tb_order_item:
              actual-data-nodes: db$->{0..1}.tb_order_item_$->{0..1}
              table-strategy:
                inline:
                  algorithm-expression: tb_order_item_$->{order_id % 2}
                  sharding-column: order_id
    mybatis:
      mapper-locations: classpath:mapper/*.xml
    
    1. 其余和原有操作一样,即可分库分表,但是会产生主键冲突问题,因此需要分布式主键

    Sharding Proxy

    分布式主键

    为什么使用分布式主键

    多数据源时,会产生主键冲突问题

    分布式主键方案

    UUID

    优点: 性能高,本地生产,没有网络消耗
    缺点: Mysql官方建议主键越短越好(UUID 36字符串长度),在Innodb引擎下,会引起数据频繁移动,验证影响性能。

    SNOWFLAKE

    雪花算法是由 Twitter 公布的分布式主键生成算法,它能够保证不同进程主键的不重复性,以及相同进程主键的有序性。
    美团Leaf是基于雪花算法的一个分布式主键中间件。

    Leaf使用

    Leaf一键搭建

    展开全文
  • 而在分片场景下,问题就变得有点复杂,我们不能依靠单个实例上的自增键来实现不同数据节点之间的全局唯一主键,这时分布式主键的需求就应运而生。ShardingSphere 作为一款优秀的分库分表开源软件,同样提供了分布式...
  • 分布式主键ID

    2021-03-30 18:07:56
    目录 分布式ID 满足条件 目前主流生成方式 ...美团(Leaf) 号段模式 snowflake算法模式 实现方式 滴滴(Tinyid) 接入方式 总结 分布式ID 一般在业务数据不大时,如mysql在100W以下时,单库单表
  • 简单介绍两种开源分布式主键生成器,UidGenerator和Leaf
  • 而在分片场景下,问题就变得有点复杂,我们不能依靠单个实例上的自增键来实现不同数据节点之间的全局唯一主键,这时分布式主键的需求就应运而生。ShardingSphere 作为一款优秀的分库分表开源软件,同样提供了分布式...
  • 分布式主键简介 ​ 在分布式环境下,由于分库分表导致数据水平拆分后无法使用单表自增主键,因此我们需要一种全局唯一id生成策略作为分布式主键。当前的解决方案有: UUID(Universally Unique Identifie) ...
  • 分库分表当然也不例外,除非库、表数据量大到一定程度,现有高可用架构已无法支撑,否则不建议大家做分库分表,因为做了数据分片后,你会发现自己踏上了一段踩坑之路,而分布式主键ID 就是遇到的第一个坑。...
  • 分布式系统中不同机器产生的id必须不同。可以使用snowflake保证id唯一。 snowflake原理 算法核心: 把时间戳、工作机器Id、序列号组合在一起。 除了最高位bit标记不可用之外,其余三组bit占位均可浮动,看具体...
  • 分布式主键 Leaf-snowflake

    千次阅读 2019-09-28 15:27:22
    Leaf-snowflake方案完全沿用snowflake方案的bit位设计,即是“1+41+10+12”的方式组装ID号。对于workerID的分配,当服务集群数量较小的情况下,完全可以手动配置。Leaf服务规模较大,动手配置成本太高。所以使用...
  • 美团(Leaf分布式ID算法(实战)

    千次阅读 2020-03-18 14:28:35
    目录 美团(Leaf) 一、 Leaf-segment号段模式 Leaf-segment为啥要这么设计呢? 那么某个点到底是什么时候...Leaf是美团推出的一个分布式ID生成服务,名字取自德国哲学家、数学家莱布尼茨的一句话:“There are...
  • 分库分表当然也不例外,除非库、表数据量持续增加,大到一定程度,以至于现有高可用架构已无法支撑,否则不建议大家做分库分表,因为做了数据分片后,你会发现自己踏上了一段踩坑之路,而分布式主键 ID 就是遇到的第...
  • Leaf——美团点评分布式ID生成系统

    千次阅读 2018-02-25 10:23:28
    背景在复杂分布式系统中,往往需要对大量的数据和消息进行唯一标识。如在美团点评的金融、支付、餐饮、酒店、猫眼电影等产品的系统中,数据日渐增长,对数据分库分表后需要有一个唯一ID来标识一条数据或消息,数据库...
  • mysql分布式id方案

    2020-11-20 09:58:20
    在mysql中大家一般用uuid或者自增主键来做唯一标识,uuid因为无序并且过长所以不适合做唯一标识,而自增主键因为数据库分库分表自增很容易出现id冲突,所以需要一个单独的逻辑或机制来生成唯一ID,这就叫做分布式id...
  • 趋势递增:在MySQL InnoDB引擎中使用的是聚集索引,由于多数RDBMS使用B-tree的数据结构来存储索引数据,在主键的选择上面我们应该尽量使用有序的主键保证写入性能。 信息安全:如果ID是连续的,恶意用户的扒取工作...
  • 如果在面试中被问及分布式唯一标识,却没有答道雪花算法,那么就有点...美团(Leaf) 百度(uid-generator) 这两个也是业界比较知名的实现雪花算法的工具. 然而还有一个工具类,它就是 hutool工具中的雪花算法 <dependenc
  • 分布式主键生成学习

    2018-09-02 15:40:00
    https://tech.meituan.com/MT_Leaf.html 转载于:https://www.cnblogs.com/codeLei/p/9574027.html

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,495
精华内容 598
关键字:

leaf分布式主键