禅与计算机 禅与计算机
首页
  • Java基础

    • 聊一聊java一些核心知识点
    • 聊聊java面向对象核心知识点
    • 聊聊Java中的异常
    • 聊聊Java中的常用类String
    • 万字长文带你细聊Java注解本质
    • 来聊聊Java的反射机制
    • 深入解析Java泛型的魅力与机制
    • Java集合框架深度解析与面试指南
    • Java常用集合类HashMap深度解析
    • LinkedHashMap源码到面试题的全解析
    • 深入解析CopyOnWriteArrayList的工作机制
    • Java基础IO总结
    • Java三大IO模型小结
    • Java BIO NIO AIO详解
    • Java进阶NIO之IO多路复用详解
    • Java8流式编程入门
    • 一文速通lambda与函数式编程
    • Java8函数式方法引用最佳实践
  • Java并发编程

    • Java并发编程基础小结
    • 深入理解Java中的final关键字
    • 浅谈Java并发安全发布技术
    • 浅谈Java并发编程中断的哲学
    • Java线程池知识点小结
    • 浅谈Java线程池中拒绝策略与流控的艺术
    • synchronized关键字使用指南
    • 深入源码解析synchronized关键字
    • 详解JUC包下的锁
    • 详解并发编程中的CAS原子类
    • LongAdder源码分析
    • AQS源码解析
    • 深入剖析Java并发编程中的死锁问题
    • Java并发容器总结
    • 详解Java并发编程volatile关键字
    • 并发编程ThreadLocal必知必会
    • CompletableFuture基础实践小结
    • CompletableFuture异步多任务最佳实践
    • 硬核详解FutureTask设计与实现
    • 线程池大小设置的底层逻辑与场景化方案
    • 来聊一个有趣的限流器RateLimiter
  • JVM相关

    • 从零开始掌握 JVM
    • JVM核心知识点小结
    • JVM指令集概览:基础与应用
    • JVM类加载器深度解析
    • JVM方法区深度解析
    • Java内存模型JMM详解
    • Java对象大小的精确计算方法
    • 逃逸分析在Java中的应用与优化
    • 从零开始理解JVM的JIT编译机制
    • G1垃圾回收器:原理详解与调优指南
    • JVM故障排查实战指南
    • JVM内存问题排错最佳实践
    • JVM内存溢出排查指南
    • 简明的Arthas使用教程
    • 简明的Arthas配置及基础运维教程
    • 基于Arthas Idea的JVM故障排查与指令生成
    • 基于arthas量化监控诊断java应用方法论与实践
    • 深入剖析arthas技术原理
  • 深入理解Spring框架

    • Spring 核心知识点全面解析
    • Spring核心功能IOC详解
    • Spring AOP 深度剖析与实践
    • Spring 三级缓存机制深度解析
    • 深入 Spring 源码,剖析设计模式的落地实践
    • 探索 Spring 事务的奥秘
    • 深入解析Spring Bean的生命周期管理
    • 解读 Spring Boot 核心知识点
    • Spring Boot 启动优化实战:1分钟到13秒的排查与优化之路
    • Spring Boot自动装配原理及实践
    • 一文快速上手Sharding-JDBC
    • sharding-jdbc如何实现分页查询
    • 基于DynamicDataSource整合分库分表框架Shardingsphere
  • 计算机组成原理

    • 计算机硬件知识小结
    • CPU核心知识点小结
    • 浅谈CPU流水线的艺术
    • 从Java程序员视角聊聊CPU缓存
    • CPU任务调度和伪共享问题小结
    • CPU MESI缓存一致性协议
    • CPU内存管理机制
    • 内存深度解析
    • 磁盘存储原理
    • 详解计算机启动步骤
    • CPU南北桥架构与发展史
    • CPU中断机制与硬件交互详解
  • 操作系统

    • 如何实现一个高性能服务器
    • Linux文件结构与文件权限
    • Linux常见压缩指令小结
    • Linux核心系统调用详解
    • Linux进程管理
    • Linux线程管理
    • 进程与线程深度解析
    • Linux进程间通信机制
    • 零拷贝技术原理与实践
    • CPU缓存一致性问题深度解析
    • IO任务与CPU调度艺术
  • 计算机网络

    • 网卡通信原理详解
    • 网卡数据包处理指南
    • 基于抓包详解TCP协议
  • 编码最佳实践

    • 浅谈现代软件工程TDD最佳实践
    • 浅谈TDD模式下并发程序设计与实现
    • 面向AI编程新范式Trae后端开发环境搭建与实践
    • 基于提示词工程的Redis签到功能开发实践
    • 基于Vibe Coding的Redis分页查询实现
    • 告别AI无效对话:资深工程师的提示词设计最佳实践
  • 实用技巧与配置

    • Mac常用快捷键与效率插件指南
    • Keynote技术科普短视频制作全攻略
  • 写作

    • 写好技术博客的5大核心原则:从认知科学到AI工具的全流程指南
  • 开发工具

    • IDEA配置详解与高效使用指南
  • Nodejs
  • 博客搭建
  • Redis

    • Redis核心知识小结
    • 解锁Redis发布订阅模式
    • 掌握Redis事务
    • Redis主从复制技术
    • Redis的哨兵模式详解
    • 深度剖析Redisson分布式锁
    • 详解redis单线程设计思路
    • 来聊聊Redis所实现的Reactor模型
    • Redis RDB持久化源码深度解析
    • 来聊聊redis的AOF写入
    • 来聊聊Redis持久化AOF管道通信的设计
    • 来聊聊redis集群数据迁移
    • Redis SDS动态字符串深度解析
    • 高效索引的秘密:redis跳表设计与实现
    • 聊聊redis中的字典设计与实现
  • MySQL

    • MySQL基础知识点小结
    • 解读MySQL 索引基础
    • MySQL 索引进阶指南
    • 解读MySQL Explain关键字
    • 探秘 MySQL 锁:原理与实践
    • 详解MySQL重做日志redolog
    • 详解undoLog在MySQL MVCC中的运用
    • MySQL二进制日志binlog核心知识点
    • MySQL高效插入数据的最佳实践
    • MySQL分页查询优化指南
    • MySQL流式查询的奥秘与应用解析
    • 来聊聊分库分表
    • 来聊聊大厂常用的分布式ID生成方案
  • ElasticSearch

    • 从Lucene到Elasticsearch:进化之路
    • ES 基础使用指南
    • ElasticSearch如何写入一篇文档
    • 深入剖析Elasticsearch文档读取原理
    • 聊聊ElasticSearch性能调优
    • Spring借助Easy-Es操作ES
  • Netty

    • 一文快速了解高性能网络通信框架Netty
    • Netty网络传输简记
    • 来聊聊Netty的ByteBuf
    • 来聊聊Netty消息发送的那些事
    • 解密Netty高性能之谜:NioEventLoop线程池阻塞分析
    • 详解Netty中的责任链Pipeline如何管理ChannelHandler
    • Netty Reactor模型常见知识点小结
    • Netty如何驾驭TCP流式传输?粘包拆包问题全解
    • Netty解码器源码解析
  • 消息队列

    • 一文快速入门消息队列
    • 消息队列RocketMQ入门指南
    • 基于RocketMQ实现分布式事务
    • RocketMQ容器化最佳实践
    • RocketMQ常见问题与深度解析
    • Kafka快速安装与使用指南
  • Nginx

    • Linux下的nginx安装
    • Nginx基础入门总结
    • Nginx核心指令小结
    • Nginx进程结构与核心模块初探
    • Nginx应用进阶HTTP核心模块配置
    • Nginx缓存及HTTPS配置小记
    • nginx高可用实践简记
    • Nginx性能优化
  • 微服务基础

    • 微服务基础知识小结
    • 分布式事务核心概念小结
    • OpenFeign核心知识小结
    • 微服务组件Gateway核心使用小结
    • 分布式事务Seata实践
    • 用 Docker Compose 完成 Seata 的整合部署
  • Nacos

    • Nacos服务注册原理全解析
    • Nacos服务订阅流程全解析
    • Nacos服务变更推送流程全解析
    • 深入解析SpringCloud负载均衡器Loadbalancer
    • Nacos源码环境搭建与调试指南
  • Seata

    • 深度剖析Seata源码
  • Docker部署

    • 一文快速掌握docker的理念和基本使用
    • 使用docker编排容器
    • 基于docker-compose部署微服务基本环境
    • 基于docker容器化部署微服务
    • Gateway全局异常处理及请求响应监控
    • Docker图形化界面工具Portainer最佳实践
  • Go基础

    • 一文带你速通Go语言基础语法
    • 一文快速掌握Go语言切片
    • 来聊聊go语言的hashMap
    • 一文速通go语言类型系统
    • 浅谈Go语言中的面向对象
    • go语言是如何实现协程的
    • 聊聊go语言中的GMP模型
    • 极简的go语言channel入门
    • 聊聊go语言基于epoll的网络并发实现
    • 写给Java开发的Go语言协程实践
  • mini-redis实战

    • 来聊聊我用go手写redis这件事
    • mini-redis如何解析处理客户端请求
    • 实现mini-redis字符串操作
    • 硬核复刻redis底层双向链表核心实现
    • 动手复刻redis之go语言下的字典的设计与落地
    • Go 语言下的 Redis 跳表设计与实现
    • Go 语言版 Redis 有序集合指令复刻探索
  • 项目编排

    • Spring脚手架创建简记
    • Spring脚手架集成分页插件
    • Spring脚手架集成校验框架
    • maven父子模块两种搭建方式简记
    • SpringBoot+Vue3前后端快速整合入门
    • 来聊聊Java项目分层规范
  • 场景设计

    • Java实现文件分片上传
    • 基于时间缓存优化浏览器轮询阻塞问题
    • 基于EasyExcel实现高效导出
    • 10亿数据高效插入MySQL最佳方案
    • 从开源框架中学习那些实用的位运算技巧
  • CI/CD

    • 基于NETAPP实现内网穿透
    • 基于Gitee实现Jenkins自动化部署SpringBoot项目
    • Jenkins离线安装部署教程简记
    • 基于Nexus搭建Maven私服基础入门
    • 基于内网的Jenkins整合gitlab综合方案简记
  • 监控方法论

    • SpringBoot集成Prometheus与Grafana监控
    • Java监控度量Micrometer全解析
    • 从 micrometer计量器角度快速上手promQL
    • 硬核安利一个监控告警开源项目Nightingale
  • Spring AI

    • Spring AI Alibaba深度实战:一文掌握智能体开发全流程
    • Spring AI Alibaba实战:JVM监控诊断Arthas Agent的工程化构建与最佳实践
  • 大模型评测

    • M2.7 真能打!我用两个真实场景测了测,结果有点意外
    • Qoder JetBrains插件评测:祖传代码重构与接口优化实战
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

sharkchili

计算机禅修者
首页
  • Java基础

    • 聊一聊java一些核心知识点
    • 聊聊java面向对象核心知识点
    • 聊聊Java中的异常
    • 聊聊Java中的常用类String
    • 万字长文带你细聊Java注解本质
    • 来聊聊Java的反射机制
    • 深入解析Java泛型的魅力与机制
    • Java集合框架深度解析与面试指南
    • Java常用集合类HashMap深度解析
    • LinkedHashMap源码到面试题的全解析
    • 深入解析CopyOnWriteArrayList的工作机制
    • Java基础IO总结
    • Java三大IO模型小结
    • Java BIO NIO AIO详解
    • Java进阶NIO之IO多路复用详解
    • Java8流式编程入门
    • 一文速通lambda与函数式编程
    • Java8函数式方法引用最佳实践
  • Java并发编程

    • Java并发编程基础小结
    • 深入理解Java中的final关键字
    • 浅谈Java并发安全发布技术
    • 浅谈Java并发编程中断的哲学
    • Java线程池知识点小结
    • 浅谈Java线程池中拒绝策略与流控的艺术
    • synchronized关键字使用指南
    • 深入源码解析synchronized关键字
    • 详解JUC包下的锁
    • 详解并发编程中的CAS原子类
    • LongAdder源码分析
    • AQS源码解析
    • 深入剖析Java并发编程中的死锁问题
    • Java并发容器总结
    • 详解Java并发编程volatile关键字
    • 并发编程ThreadLocal必知必会
    • CompletableFuture基础实践小结
    • CompletableFuture异步多任务最佳实践
    • 硬核详解FutureTask设计与实现
    • 线程池大小设置的底层逻辑与场景化方案
    • 来聊一个有趣的限流器RateLimiter
  • JVM相关

    • 从零开始掌握 JVM
    • JVM核心知识点小结
    • JVM指令集概览:基础与应用
    • JVM类加载器深度解析
    • JVM方法区深度解析
    • Java内存模型JMM详解
    • Java对象大小的精确计算方法
    • 逃逸分析在Java中的应用与优化
    • 从零开始理解JVM的JIT编译机制
    • G1垃圾回收器:原理详解与调优指南
    • JVM故障排查实战指南
    • JVM内存问题排错最佳实践
    • JVM内存溢出排查指南
    • 简明的Arthas使用教程
    • 简明的Arthas配置及基础运维教程
    • 基于Arthas Idea的JVM故障排查与指令生成
    • 基于arthas量化监控诊断java应用方法论与实践
    • 深入剖析arthas技术原理
  • 深入理解Spring框架

    • Spring 核心知识点全面解析
    • Spring核心功能IOC详解
    • Spring AOP 深度剖析与实践
    • Spring 三级缓存机制深度解析
    • 深入 Spring 源码,剖析设计模式的落地实践
    • 探索 Spring 事务的奥秘
    • 深入解析Spring Bean的生命周期管理
    • 解读 Spring Boot 核心知识点
    • Spring Boot 启动优化实战:1分钟到13秒的排查与优化之路
    • Spring Boot自动装配原理及实践
    • 一文快速上手Sharding-JDBC
    • sharding-jdbc如何实现分页查询
    • 基于DynamicDataSource整合分库分表框架Shardingsphere
  • 计算机组成原理

    • 计算机硬件知识小结
    • CPU核心知识点小结
    • 浅谈CPU流水线的艺术
    • 从Java程序员视角聊聊CPU缓存
    • CPU任务调度和伪共享问题小结
    • CPU MESI缓存一致性协议
    • CPU内存管理机制
    • 内存深度解析
    • 磁盘存储原理
    • 详解计算机启动步骤
    • CPU南北桥架构与发展史
    • CPU中断机制与硬件交互详解
  • 操作系统

    • 如何实现一个高性能服务器
    • Linux文件结构与文件权限
    • Linux常见压缩指令小结
    • Linux核心系统调用详解
    • Linux进程管理
    • Linux线程管理
    • 进程与线程深度解析
    • Linux进程间通信机制
    • 零拷贝技术原理与实践
    • CPU缓存一致性问题深度解析
    • IO任务与CPU调度艺术
  • 计算机网络

    • 网卡通信原理详解
    • 网卡数据包处理指南
    • 基于抓包详解TCP协议
  • 编码最佳实践

    • 浅谈现代软件工程TDD最佳实践
    • 浅谈TDD模式下并发程序设计与实现
    • 面向AI编程新范式Trae后端开发环境搭建与实践
    • 基于提示词工程的Redis签到功能开发实践
    • 基于Vibe Coding的Redis分页查询实现
    • 告别AI无效对话:资深工程师的提示词设计最佳实践
  • 实用技巧与配置

    • Mac常用快捷键与效率插件指南
    • Keynote技术科普短视频制作全攻略
  • 写作

    • 写好技术博客的5大核心原则:从认知科学到AI工具的全流程指南
  • 开发工具

    • IDEA配置详解与高效使用指南
  • Nodejs
  • 博客搭建
  • Redis

    • Redis核心知识小结
    • 解锁Redis发布订阅模式
    • 掌握Redis事务
    • Redis主从复制技术
    • Redis的哨兵模式详解
    • 深度剖析Redisson分布式锁
    • 详解redis单线程设计思路
    • 来聊聊Redis所实现的Reactor模型
    • Redis RDB持久化源码深度解析
    • 来聊聊redis的AOF写入
    • 来聊聊Redis持久化AOF管道通信的设计
    • 来聊聊redis集群数据迁移
    • Redis SDS动态字符串深度解析
    • 高效索引的秘密:redis跳表设计与实现
    • 聊聊redis中的字典设计与实现
  • MySQL

    • MySQL基础知识点小结
    • 解读MySQL 索引基础
    • MySQL 索引进阶指南
    • 解读MySQL Explain关键字
    • 探秘 MySQL 锁:原理与实践
    • 详解MySQL重做日志redolog
    • 详解undoLog在MySQL MVCC中的运用
    • MySQL二进制日志binlog核心知识点
    • MySQL高效插入数据的最佳实践
    • MySQL分页查询优化指南
    • MySQL流式查询的奥秘与应用解析
    • 来聊聊分库分表
    • 来聊聊大厂常用的分布式ID生成方案
  • ElasticSearch

    • 从Lucene到Elasticsearch:进化之路
    • ES 基础使用指南
    • ElasticSearch如何写入一篇文档
    • 深入剖析Elasticsearch文档读取原理
    • 聊聊ElasticSearch性能调优
    • Spring借助Easy-Es操作ES
  • Netty

    • 一文快速了解高性能网络通信框架Netty
    • Netty网络传输简记
    • 来聊聊Netty的ByteBuf
    • 来聊聊Netty消息发送的那些事
    • 解密Netty高性能之谜:NioEventLoop线程池阻塞分析
    • 详解Netty中的责任链Pipeline如何管理ChannelHandler
    • Netty Reactor模型常见知识点小结
    • Netty如何驾驭TCP流式传输?粘包拆包问题全解
    • Netty解码器源码解析
  • 消息队列

    • 一文快速入门消息队列
    • 消息队列RocketMQ入门指南
    • 基于RocketMQ实现分布式事务
    • RocketMQ容器化最佳实践
    • RocketMQ常见问题与深度解析
    • Kafka快速安装与使用指南
  • Nginx

    • Linux下的nginx安装
    • Nginx基础入门总结
    • Nginx核心指令小结
    • Nginx进程结构与核心模块初探
    • Nginx应用进阶HTTP核心模块配置
    • Nginx缓存及HTTPS配置小记
    • nginx高可用实践简记
    • Nginx性能优化
  • 微服务基础

    • 微服务基础知识小结
    • 分布式事务核心概念小结
    • OpenFeign核心知识小结
    • 微服务组件Gateway核心使用小结
    • 分布式事务Seata实践
    • 用 Docker Compose 完成 Seata 的整合部署
  • Nacos

    • Nacos服务注册原理全解析
    • Nacos服务订阅流程全解析
    • Nacos服务变更推送流程全解析
    • 深入解析SpringCloud负载均衡器Loadbalancer
    • Nacos源码环境搭建与调试指南
  • Seata

    • 深度剖析Seata源码
  • Docker部署

    • 一文快速掌握docker的理念和基本使用
    • 使用docker编排容器
    • 基于docker-compose部署微服务基本环境
    • 基于docker容器化部署微服务
    • Gateway全局异常处理及请求响应监控
    • Docker图形化界面工具Portainer最佳实践
  • Go基础

    • 一文带你速通Go语言基础语法
    • 一文快速掌握Go语言切片
    • 来聊聊go语言的hashMap
    • 一文速通go语言类型系统
    • 浅谈Go语言中的面向对象
    • go语言是如何实现协程的
    • 聊聊go语言中的GMP模型
    • 极简的go语言channel入门
    • 聊聊go语言基于epoll的网络并发实现
    • 写给Java开发的Go语言协程实践
  • mini-redis实战

    • 来聊聊我用go手写redis这件事
    • mini-redis如何解析处理客户端请求
    • 实现mini-redis字符串操作
    • 硬核复刻redis底层双向链表核心实现
    • 动手复刻redis之go语言下的字典的设计与落地
    • Go 语言下的 Redis 跳表设计与实现
    • Go 语言版 Redis 有序集合指令复刻探索
  • 项目编排

    • Spring脚手架创建简记
    • Spring脚手架集成分页插件
    • Spring脚手架集成校验框架
    • maven父子模块两种搭建方式简记
    • SpringBoot+Vue3前后端快速整合入门
    • 来聊聊Java项目分层规范
  • 场景设计

    • Java实现文件分片上传
    • 基于时间缓存优化浏览器轮询阻塞问题
    • 基于EasyExcel实现高效导出
    • 10亿数据高效插入MySQL最佳方案
    • 从开源框架中学习那些实用的位运算技巧
  • CI/CD

    • 基于NETAPP实现内网穿透
    • 基于Gitee实现Jenkins自动化部署SpringBoot项目
    • Jenkins离线安装部署教程简记
    • 基于Nexus搭建Maven私服基础入门
    • 基于内网的Jenkins整合gitlab综合方案简记
  • 监控方法论

    • SpringBoot集成Prometheus与Grafana监控
    • Java监控度量Micrometer全解析
    • 从 micrometer计量器角度快速上手promQL
    • 硬核安利一个监控告警开源项目Nightingale
  • Spring AI

    • Spring AI Alibaba深度实战:一文掌握智能体开发全流程
    • Spring AI Alibaba实战:JVM监控诊断Arthas Agent的工程化构建与最佳实践
  • 大模型评测

    • M2.7 真能打!我用两个真实场景测了测,结果有点意外
    • Qoder JetBrains插件评测:祖传代码重构与接口优化实战
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Redis

  • MySQL

    • MySQL基础知识点小结
    • Linux环境下MySQL部署安装
    • 解读MySQL 索引基础
    • MySQL 索引进阶指南:深入探秘关键知识点
    • 解读MySQL Explain关键字:优化查询执行计划的实用指南
    • 深入剖析 MySQL 某条执行过程
    • 探秘 MySQL 锁:原理与实践
    • 聊一个MySQL插入死锁问题
    • 详解MySQL重做日志redolog
    • 详解undoLog在MySQL多版本并发控制MVCC中的运用
    • MySQL二进制日志binlog核心知识点小结
    • MySQL高效插入数据的最佳实践
    • 提升 MySQL 批量更新效率的底层原理与优化策略
    • MySQL分页查询优化指南
    • MySQL LEFT JOIN 性能优化策略
    • MySQL流式查询的奥秘与应用解析
    • 主键自增是否会降低数据库insert性能
    • 内网环境MySQL操作非正常耗时问题排查小结
    • 来聊聊分库分表
      • 写在文章开头
      • 关于一些分库分表性能指标的补充
      • 分库分表基本概念介绍
        • 为什么不使用MySQL分区
        • 分库分表的基本概念
      • 分库分表的两种维度
        • 垂直分库或者水平分库
        • 垂直分表和水平分表
      • 常见的分库分表方案
        • 简介常见的集中分库分表设计方案
        • range法分表
        • hash取模法
        • range+hash法
      • 分库分表涉及的一些涉及问题
        • 分库分表时如何选择分表字段(推荐id)
        • 分库分表全局ID生成方案
        • 数据偏斜问题和解决方案
        • 如何解决分库分表后的join问题
        • 非partition key查询问题(读扩散问题)
        • 分库分表扩容问题
        • 分页查询
        • 分布式事务
        • 在分库分表后的模糊查询
        • 分库分表如何进行库表资源评估
      • 小结
      • 参考
    • 来聊聊大厂常用的分布式ID生成方案
    • 仿MyBatis-Plus实现跨数据源事务
  • ElasticSearch

  • StarRocks

  • 数据库
  • MySQL
sharkchili
2024-01-12
目录

来聊聊分库分表

# 写在文章开头

在业务体量还不是很大的时候,单库单表即可满足业务上的需求,随着业务体量的增大,无论是CPU还是IO都可能出现性能瓶颈,由于大量连接达到单库上,导致单库无法承载这些活跃的连接数,这使得我们从Java进程的角度看来就是数据库连接很少或者没有连接可用,最终出现并发、吞吐全面下降甚至是系统崩溃。

所以笔者整理了这篇分库分表的文章来逐一分析拆解这些问题。

Hi,我是 sharkChili ,是个不断在硬核技术上作死的技术人,是 CSDN的博客专家 ,也是开源项目 Java Guide 的维护者之一,熟悉 Java 也会一点 Go ,偶尔也会在 C源码 边缘徘徊。写过很多有意思的技术博客,也还在研究并输出技术的路上,希望我的文章对你有帮助,非常欢迎你关注我的公众号: 写代码的SharkChili 。

同时也非常欢迎你star我的开源项目mini-redis:https://github.com/shark-ctrl/mini-redis (opens new window)

因为近期收到很多读者的私信,所以也专门创建了一个交流群,感兴趣的读者可以通过上方的公众号获取笔者的联系方式完成好友添加,点击备注 “加群” 即可和笔者和笔者的朋友们进行深入交流。

# 关于一些分库分表性能指标的补充

这里我们补充一下IO瓶颈和CPU瓶颈,关于IO瓶颈,即是数据表中存在大量热点数据,大量的请求都需要到数据库进行查询,因为大量的IO请求进来导致数据库连接数不足导致性能瓶颈这就是所谓的IO瓶颈,针对这种情况我们可以考虑根据热点数据类型采取垂直分表或者分库的方式解决。

就像下面这种情况,因为有大量请求专门查询用户名和地址,所以我们采用垂直分表的方式将热点数据拆出来独立维护,解决原有订单表过度冗余的字段使得热点数据体量直接减小,再通过内存中间件加以缓存缓解数据库压力解决IO性能瓶颈:

假如热点数据在垂直分表后,数据量可以减小,那么我们就采取垂直分表结合缓存中间件的方式解决。如果热点数据无法通过垂直分表或者说通过垂直分表后数据规模仍然很大的话,那么我们就必须通过水平分库解决了。

而CPU瓶颈则是因为表关联join或者各种运算例如group by,order by等导致查询效率低下,这种情况如果无法通过索引或者业务代码层面进行计算的方式解决的话,那么就只能通过业务层面并结合水平分表缩小数据体量提升数据聚合效率:

# 分库分表基本概念介绍

# 为什么不使用MySQL分区

可能也会有读者问到,为什么不采用MySQL分区表呢? 这里我们需要了解一下MySQL分区表的工作原理,它会将分区的数据表在在物理层面进行分区,但在逻辑上还是一张表,这使得用户在查询的时候对分区是没有感知的。所以说使用MySQL分区会带来以下好处:

  1. 在一定的数据量情况下,使用分区键进行查询可以快速定位数据。
  2. 因为分区会在物理层面进行切分,所以对于需要定期删除分区数据的场景下,MySQL分区是非常方便管理的。

而同样的它也存在如下缺点:

  1. 无法创建外键,当然这对于现代开发规范来说这一点没有太大影响。
  2. 并发量上来了依然存在IO瓶颈。
  3. 查询时必须带上分区键,否则会对所有分区进行扫描。
  4. 对分区查询时的优化都是由MySQL优化器自定义,对用户来说是黑盒可控性较差,不如分库分表灵活。

# 分库分表的基本概念

本质上分库分表是3个概念,这里我们都是从水平拓展的维度讨论问题,本质上分库分表中的概念分别对应:

  1. 分库:以8C16G的MySQL实例为例,经过业界压测普遍认为其TPS大约在2500~3000左右,所以当系统的并发量超过这个时候,就很可能出现连接数不足导致服务瘫痪的问题,所以业界就有了水平分库增加更多的数据库连接的同时还能分散系统请求从而提升系统并发度:

  1. 分表:当单表体量超过一定阈值之后,无论数据检索还是修改操作对应的耗时的都会增加,最经典的就是深分页问题,此时我们就可以通过水平分表来缩小单表数据体量以提升数据检索速度。

  1. 分库分表:如果系统是典型高并发、海量数据的场景,也就是上述两种情况的综合体,那么我们就需要通过分库分表这种综合方案来解决问题了。

# 分库分表的两种维度

# 垂直分库或者水平分库

垂直分库是解耦服务间依赖的常见手段,在传统单体架构时,我们的所有的数据表都在一个数据库中。随着业务体量的增加,为了针对业务进行优化,我们可以将不同业务进行圈表拆分到不同库中,这就是垂直分库,通过垂直分库进行针对性优化,从而针对这些业务孵化出一个业务模式,达到服务化。

因为高并发导致单点数据库无法承载这些连接,所以我们将相同结构的数据表放到不同的数据库,然后用户通过分库算法定位到这些数据进行操作,以减轻数据库的io和cpu压力,这就是典型的水平分库。

# 垂直分表和水平分表

关于常见的分表技术有垂直分表和水平分表,其中垂直分表主要是优化查询的一种常见手段,从物理角度来说,它就是将一张表垂直进行拆分以实现确保将热点数据与非热点数据进行隔离,确保每次进行查询时缓存行可以尽可能缓存更多的字段,避免到磁盘进行随机IO导致的IO瓶颈。

水平分表则是为了解决大数据存储和查询问题,从物理角度来说它就是将大数据表横切一刀分为无数张小表,然后所有的操作都需要针对体积更小的小表进行操作,从而减小单体查询检索的IO量,提升检索效率:

# 常见的分库分表方案

# 简介常见的集中分库分表设计方案

为保证分库分表后数据能够被准确的定位并查询到,分表的策略也是很重要的,这里笔者列出几种比较常见的分表方案:

  1. range范围发表法
  2. hash取模法
  3. range+hash法

# range法分表

rang法实现比较简单,就是针对每个表都指定一个id范围,假设我们现在有3张分表,分表1存储1-500w的数据,分表2存储500w-1000w的数据,分表3存储1000w-1500w的数据。因为每个表范围是固定的,那么我们在进行数据查询时就很方便了,例如我们想查询id为1500的订单详情,直接通过id%500w即定位到分表0。

这种方案在数据查询比较均衡的情况下表现良好,遇到热点问题就比较棘手了,例如双十一淘宝订单都集中在分表3和分表4,这就会导致这两张表单位时间内承载大量查询和操作请求,而其他表却无法去负担这些压力,这也就是我们常说的数据偏斜问题。

# hash取模法

于是就有一种均摊数据的分表算法即hash取模法,这种算法要求我们尽可能在功能实现前,评估将来的数据量,例如就是5000w,那么我们就设置10张表,每张表500w。后续进行插入操作时我们只需根据自增id值进行取模运算然后均摊存储到不同表即可。例如:我们现在有一条数据得到id为1000,通过1000%10=0,由此可知这条数据就可以存到tb_0表中.

hash分表算法虽然可以均摊数据存储,避免数据热点问题,但是也存在一定的缺点,即查询问题,假如我们现在只有3张分表,id算法为id%3,一旦数据体量增加,我们的分表需要增加到6张,那么规则就需要改变了,很明显这种改动量存在的风险是非常大的。

# range+hash法

由上可知range法可以很好的进行扩容,而hash法可以完美的均摊存储。所以我们更建议使用range+hash法进行分库分表,通过range法决定当前存储的区域,再结合hash取模法指定这个区域中具体的一张表。

例如: 举个例子,笔者现在根据业务需求对数据表进行拆分得到6张分表:

  1. 每张表存储1000w条数据。
  2. 1个库作为一个range范围,id自增。
  3. 1个range包含两张张分表,总和2000w数据。

对应规则得到的表名和含义如下:

-- tb_0前缀 存储0-2000w的数据
tb_0_0
tb_0_1

-- tb_1前缀 存储2000-4000w的数据
tb_1_0
tb_1_1

-- tb_2前缀 存储4000w-6000w的数据
tb_2_0
tb_2_1
1
2
3
4
5
6
7
8
9
10
11

根据我们上文所说,通过range决定区域,假设我们现在数据id为600w,根据上表前缀可知我们要存储的数据表为tb_0开头的表,因为tb_0开头的表有两张,由此我们再用hash法进行取模,即600w%2=0,由此可知数据最终要存到tb_0_0表。 我们再回过头说说扩容问题,因为我们通过range法决定存储的分表area,所以假设需要增加分表,我们也只需定义一个新的range范围和这个范围的分表算法即可。

就比如,我们现在就需要增加两张分表,那么我们可以直接指定这两张分表区域为3,的id范围是6000w到8000w,因为这个区域还是两张分表,所以算法也是hash%2,简单配置一下即可实现扩容,无需对代码进行改造,可以说这套方案相较于前两者会更出色一些。

# 分库分表涉及的一些涉及问题

# 分库分表时如何选择分表字段(推荐id)

整体来说分库分表的选用的字段可以有很多种的方案例如:

  1. 按照用户id
  2. 按照时间
  3. 按照地区

只不过在选择的时候一定要结合业务场景进行设计同时也要考虑到下面这两个问题:

  1. 如何保证数据尽可能均匀分布到库表,同时保证保证检索效率?
  2. 确定字段后,如何在数据检索前明确知晓数据存在的库表?

假设我们的查询都是按照时间维度进行查询,那么我们就使用数据表中带有时间性质的字段作为分表字段,例如我们现在的订单表order分表算法是按月进行分表,在1月创建的数据存放至tb_1,在2月创建的数据存放至tb_2,那么我们雪花id算法作为分布式id生成工具,其原因如下:

  1. 雪花算法自增有序,不会导致大量页分裂而导致检索性能下降问题。
  2. 雪花算法有41bit的空间记录当前时间戳,所以按照我们的分表算法,可以直接通过生成的id定位到日期从而确定库表。

如下所示,我们1.25创建的数据得到的订单id是1882967322877497344,基于高41bit得到时间是1月份所以存入分表1,后续查询时,我们只需要知道对应订单的id即可定位到分表从而利用主键索引检索到数据:

# 分库分表全局ID生成方案

分库分表势必涉及一些关于全局库表id的设计方案,感兴趣的读者可以参考读者这篇文章,该文章内部已对库表ID常见的主流方案做了详尽的说明:https://mp.weixin.qq.com/s/5Gl6cxXL87jj4YBcT05s8g (opens new window)

# 数据偏斜问题和解决方案

数据偏斜即按照现有分库分表算法出现了某份库表数据远远大于其他表数据,进而导致:

  1. 性能瓶颈:因为数据偏斜导致数据分布不均匀,对于分表后的性能表现和分表前并没有很大的提升
  2. 资源利用不均匀
  3. 查询效率低下

数据偏斜问题的根因大部分是分表算法设计不好所导致,例如上面提到的range分表法无法针对业务高峰期的id段进行数据均摊,针对该问题我们建议从以下几个角度考虑并选用合适的方案:

  1. 如果没有特定的范围查询或者分页查询等需要,仅仅针对特定几条数据的检索,我们可以将分表算法改为hash算法结合取模运算均匀分布数据。
  2. 如果需要进行特定日期等范围查询的要求,建议在特殊月份做特殊的分表算法,并针对该月份的分表规则进行特殊处理,例如11月份订单是平时的3倍,我们就可以在该月份多部署几个库源和服务,针对该每个服务都有各自的workerId对应一个库源,如下图一个java-service对应一个tb_11_x的库,通过负载均衡算法将订单请求打到不同的服务上以保证数据均匀的落到不同的库表中。

# 如何解决分库分表后的join问题

水平拓展后可能会导致库表发布到不同的MySQL实例上,这使得原有的单数据源关联查询变为多库源关联:

实际上解决该问题的办法有如下几种:

  1. 应用层进行关联,即应用层面分别查询两张表然后将数据关联。
  2. 通过数据库中间件例如shardingsphere等工具实现,不过shardingsphere的联邦查询还不是很稳定,慎用。
  3. 将需要关联查询的数据直接冗余到分表上。
  4. 通过es等搜索引擎统一结构化存储提供外部检索查询。

# 非partition key查询问题(读扩散问题)

问题说明: 进行分表后,对于非partition key的查询就由为的复杂,因为非partition key和partition key没有任何关联如果没有采取任何措施的话,查询效率就会十分低下。最简单的例子就是上文600w那条数据,他记录着一个用户的个人信息,假如我们希望通过用户名name进行查询,又该如何定位到这条数据呢?很明显在没有任何措施的情况下,只能通过逐表遍历查询解决了。

解决方案:

  1. 映射法:对此我们提出第一种解决方案——映射法,即通过建立一张中间表将partition key和非partition key进行关联,以上面的例子,我们想通过name进行查询时,可直接通过映射表带入对应的name,从而得到对应的id,进而根据id得到对应的表即进行查询了。

映射算法也存在一定的缺陷,其一为了查询要同时维护两套表,并且普通索引更新时对应的映射表也得更新,而且一旦数据量逐渐增大时,可能还需要对映射表进行水平拆分,再一次增加的业务实现的复杂度。

  1. Elasticsearch:上述的映射表起始就是一种倒排索引的思想,而ES天生就是做这种事情的,针对当前问题,我们直接集成ES,通过开源工具canal监听MySQL的binlog拿到日志变更,将数据采集到ES中,通过ES近乎实时查询能力即可完美解决上述问题。

  1. 最终方案:这些做法要么会增加维护的困难和复杂度,亦或者需要增加新的中间件,还需要为了考虑可靠性增加更多的硬件资源。所以,如果业务允许的情况下,针对这种大数据存储,我们更建议直接采用TIDB进行数据存储,它是成熟的分布式数据存储数据库,它通过引入range的概念对数据表进行分片,有点类似于range范围分表,且支持普通索引分片类似倒排索引。且其语法和MySQL几乎一样,市面也有很多工具可以辅助完成数据迁移,如果项目允许的话,很明显这套数据库是最干净利落的解决方案了。

# 分库分表扩容问题

问题简介:

因为各种原有我们需要对旧有数据表进行扩容,对此数据迁移就是一个很麻烦的问题,有没有什么比较安全且易实现的方案呢?这里笔者为大家推荐两种比较常见的解决方案。

解决方案:

  1. 升级从库:先说说升级从库法,这种方式就是通过升级从库为主库的方式实现数据迁移再改造hash的迁移方式。 举个例子,假设我们现在有两个分库,每个库中有一张分表,对应的分库分表算法即id%2得到库索引,然后将数据存入对应分库的分表中,例如我们现在要存储一个id为600w的数据,通过算法得到值为0,那么这条数据就存入分库0的tb表,对应的我们的从库也跟随db0做数据同步。

当现有主库数据已达到一定体量导致查询性能下降,我们可直接将各自的从库升级为主库,这是第一步。

完成升级从库为主库之后,db0对应的从库变为db2,此时这两个数据的数据表是重复的,因为我们将分表算法修改为id%4,所以我们需要基于这个算法清除冗余数据,即主库0删除id%4=2(这些是升级为主库的db2数据),db1删除id%4=3(这个是升级为主库的db3的数据),其余两个从库同理,自此完成算法和数据迁移的升级。

  1. 双写扩容:双写扩容是现如今比较常见的方案,步骤为:
  2. 设计一套全新的算法的分库分表将新的数据插入到新表中。
  3. 通过同步双写将新数据插入新老两库。
  4. 通过异步的方式查询老库的数据全部写到新表中。
  5. 完成迁移工作后以老库为准核对数据,核对结束后配置关闭双写,后续的数据都写入新库。

这种方案相较于前者更加稳妥,也是笔者较为推荐的一种解决方案。

# 分页查询

将单表进行水平维度的分库分表之后所导致的库源不一致,传统的limit查询就无法针对整个分布式维度的分页,此时我们不得不借助一些第三方工具类将库源抽象成一个维度进行实现分表查询,我们以sharding-jdbc为例,它的做法就说基于当前查询的页数n,到所有库源中查询前n页的数据并聚合,将分布式库源检索结果聚合成一个维度,然后进行排序从而得到实际上的第二页的数据并返回。

例如,我们的分库分表希望查到第二页的数据,按照sharding-jdbc的做法,它就会将所有库表的前2页的数据查出来,然后进行归并排序得到一个完整维度的前2页的数据,最后再筛选出第二页数据返回给用户:

但是这种做法也存在一个指明的缺陷,即深分页数据的检索,按照这张方案的做法,假设我们查询100w页的数据10条,那么我们就需要归并n表*100w页*10条的数据进行归并排序,这意味着我们的程序的内存很大概率会被打穿。

针对sharding-jdbc,感兴趣的读者可以参考笔者这篇文章,这里面针对跨库表分页查询有着相对详细的介绍:

本质上,跨库表分页查询就是因为散列的数据缺少一个全局视角,针对该问题业界也有一个在业务和性能上相对折中的方案——二次查询法,下面笔者就以一个单表进行水平分库分表后的查询为例演示一下这套方案。

默认情况下,在单库单表的情况下,我们查询第二页的数据4条对应的SQL为:

select * from tb offset 4 limit 4
1

对应的查询结果如下图所示,即id在5~8这个区间的数据:

在进行水平拆分后,得到两张分表如下所示,接下来笔者就演示一下如何基于二次查询实现相对简单且高效的数据检索:

我们需要对这条SQL进行改造以保证后续步骤能够准确获取全局视角,首先我们假设分表散列均匀,所以均摊一个offset即可两张表对应的SQL为:

select * from tb_1 offset 2 limit 4
select * from tb_2 offset 2 limit 4
1
2

于是我们就得到了下面这张图中绿色区间的数据:

基于上述检索到的数据进行排序,我们得到id的最小值为4,基于这个最小值我们进行第二次查询,对应的查询采用范围查询的方式以排序的最小值作为起点,当前表的最大值作为终点:

select * from tb_1 where id between 4 and  12
select * from tb_2 where id  between 4 and 11
1
2

可以看到分表1数据区间不变,分表2多了一条数据5,此时我们就可以基于这份样本得到数值4在全局视角的offset值:

  1. tb_1中的数值4是offset 2即偏移2得来的数据,这意味着小于4的数值有两个。
  2. tb_2中的二次查询后找到大于等于4的区间是通过offset 1得来的,这意味着小于4的数值只有1个。
  3. 由此可得数值在全局视角是offset 2+1即offset 3的结果。

基于此结果可知,我们还需要偏移一条数据即可完成offset 4的偏移,因为数值4在全局是offset 3的结果,所以跳过数值4就可以实现offset 4,于是我们可知数值5开始之后4个元素就是全局视角的第二页的数值结果,由此查询出5~8,二次查询法完成:

可以看到,二次查询大体步骤为:

  1. 均衡偏移获取候选数据。
  2. 获得最小数据作为二次查询检索范围。
  3. 基于二次查询结果获得最小值在全局的偏移量。
  4. 将二次查询结果排序结合最小值在全局视角的偏移量得出最终得出分表后的分页结果。

这种做法无论是在性能还是实现复杂度都做了较好的折中,算是比较不错的解决方案。

# 分布式事务

因为水平拓展使得库源可能分布在不同的服务器上,所以系统在进行多表操作的时候无法保证数据操作的ACID,此时我们就必须借助一个第三方工具来统一管理分布式数据源,常见的方案又seata或者rocketMQ。

我们就以seata为例了解一下主流的分布式事务解决思路:

  1. seata首先会针对分布式库源要操作的数据进行前置镜像备份。
  2. 协调分布式库源执行本地事务。
  3. 基于分布式库源事务结果判断事务是否提交。
  4. 如果某个库源事务失败,则通知其他库源一并回滚,反之统一提交。

可以看出针对分库分表后的分布式事务本质上就是通过第三方的工具在逻辑上的统一协调来保证分布式事务的ACID:

# 在分库分表后的模糊查询

针对大库表进行分库分表分散压力之后,针对下面这种非前缀匹配还是显得有些力不从心(索引失效):

select * from tb where name like '%ming%'
1

对于此问题最好的办法就是专业事情让专业的工具解决,我们完全可以通过bin.log订阅分库分表数据将其提交到elasticSearch中,通过其强大的自然语言处理的分词器和倒排索引这种天生为数据检索而生的设计理念来解决模糊搜索问题:

# 分库分表如何进行库表资源评估

  1. 确定数据表体量以及TPS
  2. 针对TPS进行分库
  3. 基于分库后得到单库的数据体量进行分表

例如我们现在TPS为6000,每日数据体量在2e,按照上述步骤的推算过程为:

  1. 按照先常见的硬件配置对MySQL实例(8C32G)的压测,单库TPS基本在2000~3000,按照我们业务体量TPS需求为6000,大体需要3个库,当然我们也可以适当冗余一个库预防流量突增,所以最终我们分配4个库。
  2. 平均之后单库数据差不多在7000w以内(2e/3),按照业界标准单条数据1k的情况下单表最好在2000w以内,所以我们单库标准分3张表,还是同样道理冗余1张预防突增的数据,也可以不用,因为我们已经适当冗余了一个数据库。
  3. 基于4库3表指定分库分表方案并完成业务落地:

# 小结

以上便是笔者关于分库分表的全部内容,文章大抵包含分库分表方案以及常见问题的兜底方案,后续笔者还会针对分布式ID生成方案进行整理,感兴趣的朋友可以点击关注。

Hi,我是 sharkChili ,是个不断在硬核技术上作死的技术人,是 CSDN的博客专家 ,也是开源项目 Java Guide 的维护者之一,熟悉 Java 也会一点 Go ,偶尔也会在 C源码 边缘徘徊。写过很多有意思的技术博客,也还在研究并输出技术的路上,希望我的文章对你有帮助,非常欢迎你关注我的公众号: 写代码的SharkChili 。

同时也非常欢迎你star我的开源项目mini-redis:https://github.com/shark-ctrl/mini-redis (opens new window)

因为近期收到很多读者的私信,所以也专门创建了一个交流群,感兴趣的读者可以通过上方的公众号获取笔者的联系方式完成好友添加,点击备注 “加群” 即可和笔者和笔者的朋友们进行深入交流。

# 参考

【MySQL】不建议使用分区表 :https://blog.csdn.net/u022812849/article/details/122266186 (opens new window)

MySQL:互联网公司常用分库分表方案汇总! :https://zhuanlan.zhihu.com/p/137368446 (opens new window)

分库分表会带来读扩散问题?怎么解决? :https://zhuanlan.zhihu.com/p/522839484 (opens new window)

图解分库分表,写的太好了! :https://mp.weixin.qq.com/s/OI5y4HMTuEZR1hoz9aOMxg (opens new window)

分库分表后,如何优雅的实现高效分页查询? :https://blog.csdn.net/weixin_44421461/article/details/144598842 (opens new window)

Mysql性能指标(QPS、TPS):https://blog.csdn.net/qq_16471893/article/details/107542885 (opens new window)

编辑 (opens new window)
上次更新: 2026/03/26, 01:05:31
内网环境MySQL操作非正常耗时问题排查小结
来聊聊大厂常用的分布式ID生成方案

← 内网环境MySQL操作非正常耗时问题排查小结 来聊聊大厂常用的分布式ID生成方案→

最近更新
01
基于EasyExcel实现高效导出
03-25
02
从开源框架中学习那些实用的位运算技巧
03-25
03
浅谈分布式架构设计思想和常见优化手段
03-25
更多文章>
Theme by Vdoing | Copyright © 2025-2026 Evan Xu | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×
×