PostgreSql分库分表

  • PostgreSql分库分表
    • 一、分库分表的判断依据
    • 二、分表
      • 2.1使用触发器,子表的方式分表
        • 2.1.1 创建父表
        • 2.1.2 创建子表
        • 2.1.3 创建子表的约束
        • 2.1.4 创建子表的索引
        • 2.1.4 创建分区函数
        • 2.1.5 创建父表触发器
        • 2.1.6 测试
      • 2.2 使用Mybatis分表
      • 2.3 使用中间件分表

PostgreSql分库分表

一、分库分表的判断依据

  • 如果单表数据量太大:分表–水平分表和垂直分表
  • 如果单数据库QPS达到上限:分库

二、分表

场景:一个公司有多个部门,需要记录人员的操作日志,但是数据量很大

分表:使用部门id分表

2.1使用触发器,子表的方式分表

2.1.1 创建父表

create table db_father (c_id char(32),       --主键c_dept_id varchar(30), --部门idc_yw_id char(32),  --业务idc_value varchar(300)--业务数据
);

2.1.2 创建子表

create table db_father_10000() inherits (db_father);
create table db_father_20000() inherits (db_father);
create table db_father_30000() inherits (db_father);
create table db_father_40000() inherits (db_father);
......

2.1.3 创建子表的约束

alter table db_father_10000
add constraint db_father_10000_check_dept_key
check(c_dept_id = '10000');
alter table db_father_20000
add constraint db_father_20000_check_dept_key
check(c_dept_id = '20000');
alter table db_father_30000
add constraint db_father_30000_check_dept_key
check(c_dept_id = '30000');
alter table db_father_40000
add constraint db_father_40000_check_dept_key
check(c_dept_id = '40000');

2.1.4 创建子表的索引

create index db_father_10000_key
on db_father_10000(c_id, c_dept_id, c_yw_id);
create index db_father_20000_key
on db_father_20000(c_id, c_dept_id, c_yw_id);
create index db_father_30000_key
on db_father_30000(c_id, c_dept_id, c_yw_id);
create index db_father_40000_key
on db_father_40000(c_id, c_dept_id, c_yw_id);

2.1.4 创建分区函数

create or replace function db_father_partition_trigger()returns trigger as $$
beginif new.c_dept_id = '10000'theninsert into db_father_10000 values (new.*);elseif new.c_dept_id = '20000'theninsert into db_father_20000 values (new.*);elseif new.c_dept_id = '30000'theninsert into db_father_30000 values (new.*);elseif new.c_dept_id = '40000'theninsert into db_father_40000 values (new.*);end if;return null;
end;
$$
language plpgsql;

2.1.5 创建父表触发器

create trigger insert_db_father_trigger
before insert on db_father
for each row execute procedure db_father_partition_trigger();

2.1.6 测试

insert into db_father values('8778f843c5cec712be47430c8916d2ad', '10000', '9bb45e1deac9f925ce1358d1ddb7fdd7', '我是db_father10000子表');
insert into db_father values('929d8025664959b7f02774314ce69e6a', '20000', '19855c4edde6c6a1e25a63f0eadbe28f', '我是db_father20000子表');
insert into db_father values('c85a1130d53d87921287f5b8b8c8c6c8', '30000', '1256e6a48fbc0db393adc6cd1960ef44', '我是db_father30000子表');
insert into db_father values('9a8b01388f5fa3816e984f51cf62373f', '40000', '60ae95b6e2fbc62bddc48a3537a06a82', '我是db_father40000子表');
insert into db_father values('51fde34cb00be6cbb17061ada8d1fa52', '50000', '4411e6eaf94cd60443606cb44377ba10', '我是没有子表的数据');


可以看到,前四条数据按触发器中的判断分别进入了四个子表,第五条因为没有对50000的数据处理,所以会执行成功,但是不会插入数据

查询主表可以看到所有的数据,这是逻辑数据,实际数据是存在子表中的

2.2 使用Mybatis分表

  • 实现Interceptor 接口,使用@Intercepts注解 实现一个mybatis拦截器
  • 拦截所有请求,从配置类,注解或者数据库中获取需要分表的表
  • 把需要分表的sql替换为分表后的sql,主要是根据分表策略替换表名
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;import java.lang.reflect.Field;
import java.util.Properties;/*** MybatisSubmeterInterceptor* @Description: 分表拦截器* @Author:* @Date 2022/7/29 9:43* @Version 1.0.0*/
@ConditionalOnClass(value = org.apache.ibatis.plugin.Interceptor.class)
@Component("mybatisSubmeterInterceptor")
@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
@Order(0)
@Slf4j
public class MybatisSubmeterInterceptor  implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {Object[] args = invocation.getArgs();Executor executor = (Executor) invocation.getTarget();// 获取原始的sql语句MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];Object parameter = invocation.getArgs()[1];BoundSql boundSql = mappedStatement.getBoundSql(parameter);String oldsql = boundSql.getSql();// 判断是否需要分表if (isReplaceTableName(oldsql)) {log.error("\n分表前的sql:{}", oldsql);// 查询单独处理if (mappedStatement.getSqlCommandType().compareTo(SqlCommandType.SELECT) == 0) {RowBounds rowBounds = (RowBounds) args[2];ResultHandler resultHandler = (ResultHandler) args[3];CacheKey cacheKey;BoundSql boundSqlnew;if (args.length == 4) {boundSqlnew = mappedStatement.getBoundSql(parameter);cacheKey = executor.createCacheKey(mappedStatement, parameter, rowBounds, boundSqlnew);} else {boundSqlnew = (BoundSql) args[5];cacheKey = (CacheKey) args[4];}String sql = boundSqlnew.getSql();String newsql = sql.replace("db_father", "db_father_" + "2400");String newsql2 = sql.replace("DB_FATHER", "DB_FATHER_" + "2400");newsql = StringUtils.equals(sql,newsql) ? newsql2 : newsql;//通过反射修改sql语句Field field = boundSqlnew.getClass().getDeclaredField("sql");field.setAccessible(true);field.set(boundSqlnew, newsql);log.error("\n分表后的sql:{}", boundSqlnew.getSql());// 直接query可能会应该其他拦截器return executor.query(mappedStatement, parameter, rowBounds, resultHandler, cacheKey, boundSqlnew);} else {// 分表策略,这个地方可以使用注解,配置类,配置数据库等方式String newsql = oldsql.replace("db_father", "db_father_" + "2400");String newsql2 = oldsql.replace("DB_FATHER", "DB_FATHER_" + "2400");newsql = StringUtils.equals(oldsql,newsql) ? newsql2 : newsql;//重新生成一个BoundSql对象BoundSql bs = new BoundSql(mappedStatement.getConfiguration(),newsql,boundSql.getParameterMappings(),parameter);//重新生成一个MappedStatement对象MappedStatement newMappedStatement = copyMappedStatement(mappedStatement, new BoundSqlSqlSource(bs));//赋回给实际执行方法所需的参数中args[0] = newMappedStatement;}log.error("\n分表后的sql:{}", ((MappedStatement)args[0]).getBoundSql(parameter).getSql());}return invocation.proceed();}@Overridepublic Object plugin(Object o) {return Plugin.wrap(o, this);}@Overridepublic void setProperties(Properties properties) {log.info("setProperties " + properties.toString());}/*** MybatisSubmeterInterceptor.java** @Description: 判断是否需要分表* @Author:* @Date 2022/7/29 11:53* @param sql sql* @return boolean* @Version 1.0.0**/private boolean isReplaceTableName(String sql) {// 测试数据,将ddb_father  重新定位到db_father_2400,// 这个地方可以使用注解,配置类,配置数据库等方式判断if (sql.contains("db_father") || sql.contains("DB_FATHER")) {return true;}return false;}/*** MybatisSubmeterInterceptor.java** @Description: 生成一个新的mappedStatement* @Date 2022/7/29 13:49* @param ms* @param newSqlSource* @return org.apache.ibatis.mapping.MappedStatement* @Version 1.0.0**/private MappedStatement copyMappedStatement (MappedStatement ms, SqlSource newSqlSource) {MappedStatement.Builder builder = new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), newSqlSource, ms.getSqlCommandType());builder.resource(ms.getResource());builder.fetchSize(ms.getFetchSize());builder.statementType(ms.getStatementType());builder.keyGenerator(ms.getKeyGenerator());if (ms.getKeyProperties() != null && ms.getKeyProperties().length > 0) {builder.keyProperty(String.join(",",ms.getKeyProperties()));}builder.timeout(ms.getTimeout());builder.parameterMap(ms.getParameterMap());builder.resultMaps(ms.getResultMaps());builder.resultSetType(ms.getResultSetType());builder.cache(ms.getCache());builder.flushCacheRequired(ms.isFlushCacheRequired());builder.useCache(ms.isUseCache());return builder.build();}/**** MappedStatement构造器接受的是SqlSource* 实现SqlSource接口,将BoundSql封装进去*/public static class BoundSqlSqlSource implements SqlSource {private BoundSql boundSql;public BoundSqlSqlSource(BoundSql boundSql) {this.boundSql = boundSql;}@Overridepublic BoundSql getBoundSql(Object parameterObject) {return boundSql;}}
}

2.3 使用中间件分表

  • ShardingSphere Sharding-JDBC

PostgreSql分库分表相关推荐

  1. postgresql分库分表中间件开源实现方案

    PostgreSQL 分库分表的中间件开源实现方案有很多, 你可以根据自己的需要选择适合自己的方案. 下面是几种常见的方案: pgpool-II: 一个 PostgreSQL 透明数据库代理, 具有负 ...

  2. PostgreSQL 分库分表 插件之一 pg_shard

    MySQL的分库分表有非常多的解决方案,PostgreSQL 的分库分表方案也不少. 今天要给大家介绍的是pg_shard插件. 安装很简单, 如果你的GCC版本第一4.6,那么首先要安装一个高版本的 ...

  3. 《童虎学习笔记》15分钟ShardingSphere搭建PostgreSQL分库分表

    本文章配套视频 https://www.ixigua.com/7077056019024904717?id=7082741456641163790 本专栏全部文章 https://blog.csdn. ...

  4. ShardingSphere-Proxy分库分表以及多租户安装使用

    需求:你提供SAAS服务,你有你有2个租户(商户),各自的数据进各自的库,而你不希望你的微服务java里默认配置多个租户数据源,数据连接池太多,而且后面动态增加也不方便,诸如此类很多问题. 方案:Sh ...

  5. Mybatis-Plus 支持分库分表了?-官方神器发布!

    欢迎关注方志朋的博客,回复"666"获面试宝典 今天介绍一个 MyBatis - Plus 官方发布的神器:mybatis-mate 为 mp 企业级模块,支持分库分表,数据审计. ...

  6. 分库分表之 Sharding-JDBC 中间件,看这篇真的够了!

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 本文大纲如下 Sharding-JDBC 的基本用法和基本 ...

  7. 面试官三连问:你这个数据量多大?分库分表怎么做?用的哪个组件?

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 1.  概述 ShardingSphere是一套开源的分布 ...

  8. MySQL 分库分表及其平滑扩容方案

    作者:王克锋 出处:https://kefeng.wang/2018/07/22/mysql-sharding/ 众所周知,数据库很容易成为应用系统的瓶颈.单机数据库的资源和处理能力有限,在高并发的分 ...

  9. 银行背景下分库分表技术选型

    业务持续增长带来的单表数据量过大,必然影响到数据库的读写性能,那到底要不要分库分表呢? 阿里巴巴P3C规范给出一个推荐: [推荐]单表行数超过500万行或者单表容量超过2GB,才推荐进行分库分表. 说 ...

最新文章

  1. 结合Flink,国内自研,大规模实时动态认知图谱平台——AbutionGraph |博文精选
  2. THINKPHP 分页类
  3. 配置JDK时发生'javac'不是内部或外部命令的现象与解决过程
  4. TCP/IP / 网关和路由器的区别
  5. Ajax和JSON-学习笔记02【JQuery方式实现Ajax】
  6. Apache Software Foundation Distribution Directory
  7. python安装numpy库用清华镜像_Mac下基于Anaconda通过清华镜像安装Tensorflow
  8. 在java程序中怎么造成死锁_java – 了解为什么在此实现中发生死锁
  9. python 数据分析模块_Python数据分析之Numpy模块
  10. 构建smaba服务器
  11. 腾讯 android 插件,腾讯 Bugly for Xamarin Android 的插件
  12. 双三次插值算法(bicubic interpolation)与图形学和计算方法的关系
  13. windows7计算机不显示u盘,win7插上u盘不显示盘符怎么办|win7 u盘识别不显示盘符的解决方法...
  14. 锐捷服务器系统安全,更安全 锐捷网络发布RG-ESS易安全系统
  15. Phpcms v9建站详细流程
  16. Excel表格数据丢失,怎样能恢复?
  17. html将页面分成四部分,将HTML页面拆分为定义的宽度和高度部分
  18. 【期末复习】多媒体技术
  19. C#中文件转换为byte[]及Base64String
  20. 2015——致我那终将逝去的青春

热门文章

  1. 手机pdf文件转语音_PDF文件怎么翻译?有了这个网站,100页英文秒转中文
  2. win11改用本地账户后无法登录微软账户的解决方法
  3. 无需越狱,Android通话记录、通讯录、短信同步到iphone6
  4. 【动手学Paddle2.0系列】给女朋友做一次AI美甲
  5. Nginx upstream模块(Nginx集群配置)
  6. 【LateX本地配置】TeXLive和TeXstudio软件安装(Latex下载/安装/测试/升级)以及vscode环境配置详细教程
  7. js三元表达式的结果中不能使用逗号运行多行代码,逗号后的代码不受表达式控制
  8. 使用python获取邮箱邮件
  9. Unity 屏幕特效 之 简单地调整颜色的亮度、饱和度、对比度
  10. 计算机网络-3数据链路层