数据库专题——分库分表

  • 一. 分库分表介绍
  • 二. 分库分表实践

一. 分库分表介绍

1.1 分库分表解决了什么问题

  • 先说分库:
    • 《高性能MySQL》中提到了两种数据库扩展方式:垂直扩展和水平扩展。前者意味着买更多性能强悍的硬件,但是总会达到扩展的天花板,且成本较高。分库则是后者的一种实现方式。
    • 流量瓶颈:主要是写多的场景(读多可以通过读写分离,缓存等方式解决问题),而单集群写在大流量下是易达到瓶颈的。
    • 容量瓶颈:数据量大的情况下,单集群磁盘不够存储(尤其对于订单类的业务,即使有归档等手段,也难以支撑一定时间范围内的数据存储)。
  • 再说分表
    • 分表主要解决单表过大问题(DBA推荐单表容量在1000万行左右):随着表增大,从经验上看会引起慢查询(即使SQL语句很简单且走了索引),分表有利于提高读写效率

1.2 分库分表的方式

分表通常可分为水平分表和垂直分表

  • 垂直分表:把大表的字段拆分,拆成小表。如把order表拆成order_major(包含热点字段)和order_extra(包含冷门字段)
    • 优点:热点数据分离,热点表走主库,冷门表走从库
  • 水平分表:水平分表就是指以行为单位对数据进行拆分,一般分库分表指的就是水平分表
    • 优点:优化单表行数过大,提高性能

1.3 分库分表的基本原理

选择一个或多个路由键(routeKey,也可称分片),根据该路由键及路由规则,将SQL路由至不同的分库及分表。

  • 如分10库,每库100表,路由键为userId
    • 库路由规则:#userId#%10
    • 表路由规则:(#userId#).intdiv(10)%100
  • 代表含义:uid=100001会路由到1库0表中;uid=100329会路由到9库32表中

1.4 分库分表需要注意的问题

  • (1)自增主键无法标识唯一id,需要依赖分布式ID生成服务
  • (2)路由键的选择(拆分的维度):如订单表,假如又要根据订单id查询,又要根据用户id查询,该怎么解决
    • 思路一:系统拆分(实现成本高)
    • 思路二:建路由表(订单表以订单id为路由键;新建一张用户id与订单id的路由表,以用户id作为路由键)——> 注意分布式事务问题
  • (3)分布式事务问题:跨库就会涉及到分布式事务问题,除非所有操作的表都基于相同的路由键与路由规则
  • (4)路由键的选择:数据倾斜
  • (5)可扩展性:如果当前已经是分库分表,未来再扩容,建议以倍数扩(如2—>4,4—>8)。不然会复杂
  • (6)数据迁移过程中的平滑稳定

1.5 分库分表的实现方案

业界主要两种:服务端代理 和 客户端代理

  • 服务端代理:通过部署代理服务,背后管理多个数据库实例。应用层通过一个普通的数据源(c3p0、druid、dbcp等)与代理服务器建立连接,所有的SQL语句都是发送给这个代理,由这个代理去路由底层数据库,开发人员无需关注底层逻辑
  • 客户端代理:应用层内部管理了多个普通的数据源(c3p0、druid、dbcp等),每个普通数据源各自与不同的库建立连接。应用层通过路由规则路由给各个普通的数据源去执行,并返回结果。数据源代理通常也实现了JDBC规范定义的API,因此能够直接与orm框架整合。这种方案下开发人员需要修改代码

主流的实现方案对比

  • 数据库代理
    • 目前的实现方案有:阿里巴巴开源的cobar,mycat团队在cobar基础上开发的mycat,mysql官方提供的mysql-proxy,奇虎360在mysql-proxy基础开发的atlas。目前除了mycat,其他几个项目基本已经没有维护。
    • 优点:多语言支持。也就是说,不论你用的php、java或是其他语言,都可以支持。原因在于数据库代理本身就实现了mysql的通信协议,你可以就将其看成一个mysql 服务器。mysql官方团队为不同语言提供了不同的客户端驱动,如java语言的mysql-connector-java,python语言的mysql-connector-python等等。因此不同语言的开发者都可以使用mysql官方提供的对应的驱动来与这个代理服务器建通信。
    • 缺点:实现复杂。因为代理服务器需要实现mysql服务端的通信协议,因此实现难度较大。
  • 数据源代理
    • 目前的实现方案有:阿里巴巴开源的tddl,大众点评开源的zebra,当当网开源的sharding-jdbc。需要注意的是tddl的开源版本只有读写分离功能,没有分库分表,且开源版本已经不再维护。大众点评的zebra开源版本代码已经很久更新,基本上处于停滞的状态。当当网的sharding-jdbc目前算是做的比较好的,代码时有更新,文档资料比较全。
    • 优点:更加轻量,可以与任何orm框架整合。这种方案不需要实现mysql的通信协议,因为底层管理的普通数据源,可以直接通过mysql-connector-java驱动与mysql服务器进行通信,因此实现相对简单。
    • 缺点:仅支持某一种语言。例如tddl、zebra、sharding-jdbc都是使用java语言开发,因此对于使用其他语言的用户,就无法使用这些中间件。版本升级困难,因为应用使用数据源代理就是引入一个jar包的依赖,在有多个应用都对某个版本的jar包产生依赖时,一旦这个版本有bug,所有的应用都需要升级。而数据库代理升级则相对容易,因为服务是单独部署的,只要升级这个代理服务器,所有连接到这个代理的应用自然也就相当于都升级了。
  • ORM框架代理
    • 目前有hibernate提供的hibernate-shards,也可以通过mybatis插件的方式编写。相对于前面两种方案,这种方案可以说是只有缺点,没有优点。

二. 分库分表实践

2.1 路由键的选择

  • 确定业务逻辑上的主体,并确认大部分数据库操作都基于这个主体进行(如用户ID)。
  • 路由键在业务上不应被update,应是一个稳定的数据。且不应该为Null
  • 数据倾斜问题,确保散列均匀

2.2 分库分表数决策

  • 分表数量决策:
    • 单表建议:不超过1000万行数据
    • 通常可以预估2到5年的数据增长量,用估算出的总数据量除以总的物理分库数,再除以建议的最大数据量1000万,即可得出每个物理分库上需要创建的物理分表数
      • (未来3到5年内总共的记录行数) / 单张表建议记录行数
  • 分库数量决策:
    • 提前规划好未来2到5年的峰值流量+容量

2.3 路由规则

  • 路由键自身散列均匀:可通过取模的形式
    • 例如:库路由规则:#user_id#%10;表路由规则:(#user_id#).intdiv(10)%100
  • 路由键自身不散列均匀:对路由键作hash
    • 例如:crc32(#user_id#)%8

2.4 其他挑战

  • 自增主键:分布式自增id
  • 分库分表数据迁移过程的双写:
    • 思路一:mq事务消息,对于老表的事务,通过mq异步对新表重新执行,失败则补偿
      • 缺点:异步极端情况下非实时(mq消息延迟),代码改动大
      • 优点:老表 与 新表的最终数据一致性可保证
    • 思路二:监听老表binlog,异步对新表执行。与思路一差不多
    • 思路三:代码内部双写事务(设置两个事务管理器来处理)
      • 优点:可通过切面形式实现,代码嵌入改造量小
      • 缺点:①极端情况下无法保证两个事务的一致性(已提交事务A,准备提交B时宕机);②双写流程中接口性能下降
  • 迁移过程整体思路:建分库分表—>双写(增量)—>刷存量—>数据核验—>切读流量—>切写流量—>下线双写—>收尾
  • 小部分业务场景下没有路由键:
    • 思路:空间换时间,建路由表(订单表以订单id为路由键;新建一张用户id与订单id的路由表,以用户id作为路由键)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/484878.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

软件测试需要学习什么?好就业吗?

目前来说的话,整个it 都不太好!但是既然你问了,我也就告诉你吧! 1功能测试 :前端和后端,前端就是简单的页面,你需要考虑的是:必填项,边界值,组合&#xff0c…

WebRTC最新版报错解决:city.wav:missing and no known rule to make it (二十六)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒体系统工程师系列【原创干货持续更新中……】🚀 人生格言: 人生从来没有捷径,只…

学习 python的第四天,顺便分享两首歌:we don‘ talk anymore,You ‘re Still The One

诸君晚上好,现在是🌃晚上,今天是学习python的第四个学习日,不知不觉学了四天了,还是那句话:不积跬步无以至千里、不积小流无以成江海! 暂时回顾下前面的学习日吧: 第一个学习日----…

Spring Boot打war包部署到Tomcat,访问页面404 !!!

水善利万物而不争,处众人之所恶,故几于道💦 文章目录 Spring Boot打war包部署到Tomcat,访问页面404 !!!解决办法:检查Tomcat版本和Jdk的对应关系,我的Tomcat是6.x&#x…

Web前端3D JS框架和库 整理

在WebGL库和SVG/Canvas元素的支持下,JavaScript变得惊人的强大。几乎可以为网络构建任何东西,包括基于浏览器的游戏和本地应用,许多最新的突破性功能都在3D上运行。 为此,「数维图小编」整理了19个交互式3D Javascript库和框架&am…

Docker基础篇(二)

docker run -d docker run -d 容器名或容器ID docker run -d 后台生成容器,并退出容器(除容器中在运行脚本) docker run -it 交互生成容器 docker run -d centos /bin/sh -c “while true; do echo zen; sleep 2;done” 查看容器中的进程…

基于java,springboot和vue房屋租赁租房销售平台设计

摘要 在现代城市生活中,房屋租赁市场一直是一个活跃且复杂的领域。随着互联网技术的不断发展,基于Spring Boot和Vue的房屋租赁系统应运而生,旨在提供一个高效、方便、可靠的在线服务平台。该系统利用了前后端分离架构的优势,后端…

LLMChain使用 | RouterChain的使用 - 用本地大模型搭建多Agents

单个本地大模型搭建参考博客 单个Chain:面对一个需求,我们需要创建一个llmchain,设置一个prompt模板,这个chain能够接收一个用户input,并输出一个结果;多个Chain:考虑到同时面对多个需求&#x…

vue保留用户在列表的操作记录, beforeRouteLeave离开当前组件缓存数据即可

最近遇到一个需求,用户在列表页的查询输入框输入条件后,点击查询,然后此时切换菜单,再回到之前的页面,希望能停留在上一次输入的结果上,如下例子,用户管理页面,输入yangfan这个关键词搜索后,结果如下图: 当我此时点击权限管理后,再点击用户管理切回来,结果依旧如上…

【Webpack】处理字体图标和音视频资源

处理字体图标资源 1. 下载字体图标文件 打开阿里巴巴矢量图标库open in new window选择想要的图标添加到购物车,统一下载到本地 2. 添加字体图标资源 src/fonts/iconfont.ttf src/fonts/iconfont.woff src/fonts/iconfont.woff2 src/css/iconfont.css 注意字体…

用 Python 自动化处理无聊的事情

“编程最棒的部分就是看到机器做一些有用的事情而获得的胜利。用 Python 将无聊的事情自动化将所有编程视为这些小小的胜利;它让无聊变得有趣。” Hilary Mason,数据科学家兼 Fast Forward Labs 创始人 “我很享受打破东西然后把它们重新组合起来的乐趣…

Studio One 6免费下载安装激活教程

一、Studio One 6安装 1.双击Studio One6安装包(见文章尾部),如下图,可以切换语言,点击【OK】。 2.根据安装导航,点击【下一步】 3.阅读许可证协议后,点击【我接受】。 4.选择安装位置&#xf…