数据库hash分表

作者: WANGGGGG | 来源:发表于2019-12-04 20:28 被阅读0次

前言

平常我们一直用的就是按时间进行分表,按时间分表可以减轻以时间维度的查询。但是如果查询的条件不是时间,那么当查询主表的时候,就会去遍历所有的分表,没有起到分表的优化效果。

方法

如果不能按照时间分表,我们可以采取按hash进行分表,如人员特别多的情况下,可以按照人员id进行hash分表,在查询的时候就可以根据hash到分表里面去查询人员信息了。

public class TableHashPartitionUtil {

    public TableHashPartitionUtil() {
        // NO_OP
    }

    /**
     * 功能描述: 生成分区语句,分区语句不支持动态分区,只能在设计阶段预估数据量之后选择好分区个数,然后进行分区
     *
     * @param tableName  主表名称
     * @param colName    分区依赖的字段名
     * @param partitions 分区个数
     * @return 返回信息:分区的执行语句
     * @author wangcanfeng
     * @date 2019/12/3-14:17
     * @since 1.1.100
     */
    public static String partitionSql(String tableName, String colName, Integer partitions) {
        StringBuilder ps = new StringBuilder();
        ps.append("do language plpgsql \n");
        ps.append("$$\n");
        ps.append("declare\n");
        ps.append("  parts int := ").append(partitions).append(";\n");
        ps.append("begin\n");
        ps.append("  for i in 0..parts-1 loop \n");
        ps.append("    execute format('create ").append(tableName).append("%s (like ").append(tableName)
                .append(" including all) inherits (").append(tableName).append(")', i);\n");
        ps.append("    execute format('alter table ").append(tableName)
                .append("%s add constraint ck check(abs(mod(hashtext(")
                .append(colName).append("),%s))=%s)', i, parts, i);\n");
        ps.append("  end loop;\n");
        ps.append("end; \n");
        ps.append("$$;");
        return ps.toString();
    }

    /**
     * 功能描述: 触发器函数,引导数据,将写入到hash值一致的表中
     * 注意: 表名称,列名称,分区数都要和分表语句中的相一致
     *
     * @param tableName  表名称
     * @param colName    分区依赖的字段名
     * @param partitions 分区个数
     * @return 返回信息:分区的执行语句
     * @author wangcanfeng
     * @date 2019/12/3-14:23
     * @since 1.1.100
     */
    public static String insertFunction(String tableName, String colName, Integer partitions) {
        StringBuilder helper = new StringBuilder();
        helper.append("create or replace function ins_").append(tableName).append("() returns trigger as \n");
        helper.append("$$\n");
        helper.append("declare begin\n");
        helper.append("  case abs(mod(hashtext(NEW.").append(colName).append("),").append(partitions).append("))\n");
        for (int i = 0; i < partitions; i++) {
            helper.append("    when ").append(i).append(" then\n");
            helper.append("      insert into ").append(tableName).append(i).append(" values (NEW.*);\n");
        }
        helper.append("    else\n");
        helper.append("      return NEW;\n");
        helper.append("    end case;\n");
        helper.append("    return null;\n");
        helper.append("end;\n");
        helper.append("$$\n");
        helper.append("language plpgsql strict;\n");
        return helper.toString();
    }

    /**
     * 功能描述: 预防分表依赖的字段为空,为空的情况插入到主表中
     *
     * @param tableName 表名称
     * @param colName   分区依赖的字段名
     * @return 返回信息:
     * @author wangcanfeng
     * @date 2019/12/3-14:28
     * @since 1.1.100
     */
    public static String protectNull(String tableName, String colName) {
        String pn="create trigger %s_ins_tg before insert on %s for each row when (NEW.%s is not null) execute procedure ins_%s();";
        return String.format(pn, tableName,tableName,colName,tableName);
    }

    /**
     * 功能描述: 查询的时候需要添加上这个约束条件,在where条件里面用and连接
     * 注意:不加上这个约束将无法加速查询,查询将经历这些表
     *
     * @param colName    分区依赖的字段名,如果插入的参数不是分区依赖的字段,可能会出现异常或无效
     * @param colValue   分区依赖的字段值
     * @param partitions 分区数目
     * @return 返回信息:加速查询的拼接语句
     * @author wangcanfeng
     * @date 2019/12/3-14:35
     * @since 1.1.100
     */
    public static String selectAppend(String colName, String colValue, Integer partitions) {
        // 判断要查询的值在哪个分区
        String judgePartition = "abs(mod(hashtext(%s::text), %d))=abs(mod(hashtext('%s'::text), %d))";
        return String.format(judgePartition, colName, partitions, colValue, partitions);
    }

缺点

(1)查询的时候没有像按时间分表一样自由,查询的时候需要在where的条件中增加"abs(mod(hashtext(字段名::text), %d))=abs(mod(hashtext('字段值'::text), %d))",同时因为是hash值,像时间一样需要范围查询的时候会比较麻烦
(2)在建表的时候需要先预估好数据量的大小,然后执行分表,不能想按时间分表一样动态分表

优势

按hash分表可以将一张表中的数据根据hash值分到各个子表中,可以根据hash值到指定的子表中进行查询,相当于只需要查1/n的数据量即可。

相关文章

  • 数据库hash分表

    前言 平常我们一直用的就是按时间进行分表,按时间分表可以减轻以时间维度的查询。但是如果查询的条件不是时间,那么当查...

  • 分库分表之设备分享的思考

    背景 项目中使用的是MySQL数据库,我们对设备表做了分库分表,分表逻辑:对租户ID进行hash,使用的技术框架S...

  • 哈希索引

    概述 数据库中经常用到的索引包括hash索引和B树索引。 定义 哈希索引(hash index)是基于hash表实...

  • 数据库hash分表后的扩容方案

    postgres的hash分表不停机扩容方案原来我们hash分表之后,数据扩容采用的是rehash,这样迁移全部的...

  • 面试准备——HashMap原理

    Hash表 Hash表的结构就是顺序表+链表的结构Hash表(jdk1.7)中内部是HashMapEntry

  • redis的数据库和键过期的实现原理

    redis 数据库实现原理 redis是内存数据库,非关系型数据库,存储的是键值对 redis底层是用hash表的...

  • java系统架构师必须掌握的技术要点

    1:CAP原则 2:HASH一致性 3:业务幂等性,消息幂等性 4:数据库分库分表 5:全链路监控(参见dappe...

  • HashMap 源码理解

    基础 Node定义 table hash表,Node数组。 size: hash表中Node节点总数,与hash...

  • Redis 字典

    Redis 字典使用Hash 表作为底层的实现,Hash 表这个结构不难理解,但是在实际应用 Hash 表时,当数...

  • 机试常用算法和题型-哈希专题

    哈希专题 hash表的用法 hash表高阶用法,二维数组存放不同组的hash值 hash结合字母表处理字符串的使用方法

网友评论

    本文标题:数据库hash分表

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