阅读 159

分表分库方案实现

前言:之所以要研究分表分库这个技术点,是因为在开发的过程中,因为分表分库踩了个小坑,虽然坑不算大,但是还是调试了很久。就是我在做推荐文章进入好友动态需求的时候,这个文章本身是一种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的一些实现

作者:HannahLi_9f1c

原文链接:https://www.jianshu.com/p/23e960e714f3

文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐