数据库分表

本文探讨了在大数据量存储背景下,采用分表策略解决数据读写效率低下的问题。通过计算得出每张表平均存储300-400万条数据最合理,分表数为64。利用MD5和哈希实现数据均匀分布,并强调避免跨表查询以保持性能。同时,针对手机号和车牌号的互斥定位数据,提出了将两者拼接作为索引的解决方案。
摘要由CSDN通过智能技术生成

分表思路

分表的背景

  在我们保存数据时,有时候会遇到保存大数据量的情况。而我就遇到过这种情况,比如说存储卫星定位数据。初步估计,其在3个月内的数据量大致为2亿条。

  至于为什么是3个月,这是由于该需求正处于探索阶段,先整3个月的试用期,待试用期过后再进行相应评估,是继续做,还是停掉,因而暂时也只需要存储3个月的数据量。

  而如果采用传统的单表操作,那么越往后期,该表中的数据存储量将会越来越庞大,那么其不可避免的会导致后续的数据读写效率的低下性,为了解决该问题,我采用的就是下面的分表策略。

分表的计算

   在确定了选用分表策略后,接下来就是需要分多少张表了,经过计算,平均每张表存储300-400万条数据量的情况下是最合理的,而如果分表过少,则会导致单表的数据存储量上升,进而会导致后续定位数据读写性能的下降。而如果平均每张表存储比较少的数据量,则需要创建更多的分表,那么随之而来的问题则是如何维护数量庞大的分表。

   而另外一个需要注意的事情是,如果采用分表策略,当分表数为2的n次方的分表策略是合理的,这和手机存储为什么都是2的n次方的原理一样。

   在考虑到上述条件之后,我们初步计算公式如下:

分表数*每张表数据量=2亿。
上述公式需满足如下条件:
分表数为2的n次方。
每张表数据量范围300万至400万

   上述公式的计算结果为:

分表数:64
每张表的数据量为:3125000条

分表的存储规则

   在存储数据时,我们需要将数据尽可能均匀的落到每张分表中,尽量做到每张分表的数据存储量是大致相似的,因而我很快的想到了“散列”,也就是人们常说的“哈希(Hash)”。说完就干,于是我很快的就写好测试代码,然后测试。

   经过测试,我发现很多重复数据,于是采用Set集合进行去重,然后再测,发现数据分散大致比较均匀,但是还是有部分表分到了大量的数据,可能是这些数据之间的差别比较小所致。于是我又想到了MD5,由于MD5是初始数据的微小改动,就会导致后续数据出现较大的不同,也就是说MD5其实是有将微小差别放大的功能,于是我先进行MD5计算,然后再执行Hash,最后的测试结果基本满足需求,定位数据基本上均匀的分布到每一张表中。

   由于测试用例并没有保存下来,所以说这里就不用再论述了。毕竟下面的代码中有测试用例的最终可执行版本。

分表的索引值

   我们在分表的运用中,切记,不要涉及到跨表数据查询,因为所要查询的数据一旦跨表,则会导致数据的全表扫描查询,这和不分表就没有区别了,因而在分表前,一定要考虑到,不要涉及到数据的跨表查询操作。

   我们的项目在分表时,表索引的创建比较麻烦。因为我们的数据根据业务来说,主要是有两类,其一是根据司机的手机号进行的定位,其查询必然需要依赖手机号创建定位表索引;与此同时我们还存在根据司机车牌号进行的定位,因而其查询也必然会出现需要依赖车牌号创建定位表索引。而我们的分表索引只有一个,这意味着,如果要是依赖手机号作为索引,则会导致以车牌号存储的定位数据在查询时出现跨表操作,反之亦然。这样一来,我们就很难在此处的业务逻辑中采用分表策略了,貌似现在分表策略已经进入了死胡同。

  在后续的与业务人员的沟通中,我们了解到手机号与车牌号定位数据虽然都存储在一张表中(即表中同时存在手机号与车牌号字段),但是在一条定位数据中,其并不会同时出现手机号与车牌号,也就是说手机号与车牌号是一种互斥关系,因而我将手机号与车牌号拼接起来,共同组成了一个字符串,然后再对其执行分表的索引值计算,经过实测,这样做是可以的,下面是其详细的实现代码。

源码

查询数据表索引值

  根据手机号或车牌号查询数据表索引值,并返回相应的查询结果


public ResultData locationStorageIndex(Map<String, String> param) {
   
    //根据手机号或车牌号查看定位数据存储表索引id
    Integer index = locationStorageIndex(param.get("mobile"),param.get("carNo"));
    if(null == index){
   
        return new ResultData("查询失败!",ResultData.RESULT_FAILURE);
    }
    return null == index ? new ResultData("查询失败!",ResultData.RESULT_FAILURE) : new ResultData("查询成功!",ResultData.RESULT_SUCCESS,index);
}

  调用SubmeterUtil工具类,根据手机号或车牌号生成数据表索引值


public Integer locationStorageIndex(String mobile,String carNo){
   
    //生成数据表索引值
    return SubmeterUtil.newSubmeterUtil().generateSubmeterIndex(mobile,carNo);
}

  根据手机号或车牌号生成分表索引核心工具类:SubmeterUtil

...
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.log4j.Logger;

/**
 * 分表核心工具类
 */
public class SubmeterUtil {
   

    private static final Logger logger = Logger.getLogger(SubmeterUtil.class);

    /**
     * 功能描述: 创建空的构造函数
     */
    public SubmeterUtil() {
   
    }

    /**
     * 功能描述: 获取一个新的静态实例
     */
    public static SubmeterUtil newSubmeterUtil(){
   
        return new SubmeterUtil();
    }

    
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值