美文网首页mysqlmysql分布式
分表分库方案实现

分表分库方案实现

作者: HannahLi_9f1c | 来源:发表于2021-05-22 22:08 被阅读0次

    前言:之所以要研究分表分库这个技术点,是因为在开发的过程中,因为分表分库踩了个小坑,虽然坑不算大,但是还是调试了很久。就是我在做推荐文章进入好友动态需求的时候,这个文章本身是一种type=51存储在数据库中,对应地转发这篇文章的feed的type也是51,只不过src_fid字段需要存储源文章的feed_id。


    image.png

    而推荐文章这个需求需要给feed表新增一种类型type=56,对应地在feed的分库新增一种类型type=302。后面就发现遇到坑了,因为我把分库的类型放到interaction_types中


    image.png ,然后sync_feed_db.insert_data新增一种类型的时候
    渲染的时候就查不到源文章,导致读取数据库失败。然后调试了很久,发现在读取源文章的时候读取不到,然后这条数据读取就失败了
    image.png
    image.png

    在往上找,发现是这里读取失败了,这里是feed封装的方法,怎么会读取不到呢,于是我就进去看了看里面的代码

            if (table == conf.TBNAME_FEEDS and id_field in ('id', 'src_fid')) or \
                    (table != conf.TBNAME_FEEDS and id_field == 'fid'):
                for global_fid in ids:
                    if is_explore_fid(global_fid):
                        explore_fids.append(self.convert_global_fid_to_fid(global_fid))
    
                    elif is_interaction_fid(global_fid):
                        interaction_fids.append(self.convert_global_fid_to_fid(global_fid))
                    elif is_generalcard_fid(global_fid):
                        generalcard_fids.append(self.convert_global_fid_to_fid(global_fid))
    
    image.png

    因为源文章的类型是e_id,所以在查询出来某一条feed之后,会给src_fid自动拼装上前缀,如果一条feed是e_开头,会给他的src_fid拼上e_,但是由于前面把分库的类型写成i_类型,所以去expolore库查询就会查不到。还有一个坑就是explore类型的type不能大于300,大于300就会insert失败,这里也还没找到根因。
    从这个bug中我就产生了很多疑问,为什么文章的src_fid需要放到一个库上呢?以及为什么要在代码中加上这种前缀呢?feed为什么要做分表分库,它是怎么做的呢?数据如何同步的?

    一、分表分库的原理

    当然第一步我们需要先了解一下分表分库的基本概念,分表分库上解决写请求越来越多的问题,以及数据量暴增的问题,因为对于读请求频繁的场景,可以加缓存或者用一主多从来解决,但是如果写请求过于频繁,那么压力都会集中在master上,master上面的IO,网络,连接池都会成为一种瓶颈。或者如果单表一直膨胀,那么对于磁盘来说压力山大,而且很容易带来慢查询。所以需要分表分库来解决这个问题。

    分表的方式

    1. 垂直拆分
      垂直拆分分为两种,垂直分库和垂直分表。如果单库写请求多,并且分散在多张表的情况,这种情况可以把库里面的表分散到多个库中,每个库放在不同机器上,不然还是会遇到机器的瓶颈问题。如果压力集中到一张表,那么可以大表拆小表,把不常用较长的字段迁移到扩展表,避免跨表查询的压力
    2. 水平拆分
      水平拆分是将单表的数据水平拆分到不同服务器上,可以是按照Hash、地理位置、或者用户id进行分表。水平拆分的难度比垂直拆分更大一些。
      分表分库需要解决的问题
    3. 事务支持
      在单库或者单表中,可以用MySQL的事务解决一致性问题,但是分表分库的话,就需要解决分布式事务的问题了。
    4. order by ,group by,join
      分表分库之后order by 和group by要怎么实现?以及跨库的join问题
      相关产品


      image.png

    二、实名分表分库

    为什么实名要做分表分库

    1. feed库硬盘将满,单表过大,性能下降。
    2. 慢sql难以优化

    遇到的问题怎么解决的?

    1. 如何拆分
      备选方案有几种,其中一种按照fid做hash,然后查询的时候并行地读取集群,跟业务无关。优点是逻辑简单,易于扩展,缺点是存在无fid的表,而且有fid和无fid的跨库join难以实现。还有一种是按照type来拆分,按照业务的type分在不同的库,互动类型等等,优点是业务逻辑清晰,易于拆分,缺点是跨业务的查询需要查询多个集群。最终是选择了按照type来拆分,对于同一种业务来说,很少会跨集群,这里也解释了为什么src_fid和fid的前缀要一致,这是因为要避免跨集群访问。
    2. 如何数据同步
      数据双写->老库数据迁移到新库->下掉老库的写入逻辑
    3. join,order by ,group by怎么做

    代码实现细节

    1. 将存在badge的互动消息以及渲染的数据迁移到Redis,减轻数据库压力,能够给分表分库,提供一些时间。
    2. view层新增一个fid到gfid的转换,保证在后端的代码,都是带前缀(e表示探索类型,i表示互动类型)的gfid,这样能够区分是在哪个集群。
    3. 新增一个feed_manager,提供分表分库后的数据读写方法
    4. 将对新老数据库进行数据双写封装成api,方便调用,保持数据一致。
      三、mycat的一些实现

    相关文章

      网友评论

        本文标题:分表分库方案实现

        本文链接:https://www.haomeiwen.com/subject/ftanjltx.html