垂直分表、水平分表详解

news/2024/11/16 22:40:31/

垂直分表

什么是垂直分表

垂直分表就是把一张表按列分为多张表,多张表通过主键进行关联,从而组成完整的数据。

分表之后,每张表的结构都不相同。

垂直分表不需要额外引用其他组件,需要到Repository层面建立好表映射即可。

根据什么分表呢?

根据数据是否是热点数据划分。

热点数据即经常查询、更新频繁的列。

例如一个订单状态信息会频繁进行更新、订单金额在列表会频繁被查询到作为热点数据,而下单地址、手机号码等信息基本不会改变或者改变次数很少作为非热点数据。

垂直分表有哪些好处呢?

  1. 把热点数据分离,更新的性能更加高;
  2. 减少行数据,数据库IO效率高;
  3. 业务所有数据存在一个或者多个表,管理后台聚合查询(走从库)比较方便。

垂直分表实战

例如我们有一张外卖的表,全表结构如下:

CREATE TABLE `meal_order` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',`trade_no` varchar(128) NOT NULL DEFAULT '' COMMENT '交易流水号',`order_id` bigint(20) NOT NULL COMMENT '订单ID',`order_type` tinyint(4) NOT NULL COMMENT '外卖类型:4:外卖;1:买单;3:团购',`order_status` tinyint(4) NOT NULL COMMENT '订单状态:1:未付款;2:已付款;4:部分退款;5:已退款;6:已完成;-1:已取消',`delivery_status` int(11) DEFAULT NULL COMMENT '配送状态:0:默认;0:已推送给配送方;10:已抢单;20:已取餐;40:已送达;100:已取消',`consumption_type` tinyint(4) NOT NULL DEFAULT '1' COMMENT '类型,1=个人,2=商务',`user_sn` bigint(20) NOT NULL COMMENT 'userSn',`employee_sn` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '员工SN',`enterprise_sn` varchar(20) NOT NULL DEFAULT '' COMMENT '企业编号',`order_amount` decimal(12,2) NOT NULL DEFAULT '0.00' COMMENT '订单总价',`order_pay_amount` decimal(12,2) NOT NULL DEFAULT '0.00' COMMENT '订单支付价格',`shipping_fee` decimal(12,2) DEFAULT NULL COMMENT '订单配送费(原始配送费)',`service_fee` decimal(12,2) DEFAULT NULL COMMENT '服务费',`ent_service_fee` decimal(15,2) NOT NULL DEFAULT '0.00' COMMENT '企业服务费',`ent_service_fee_ratio` decimal(15,2) DEFAULT NULL COMMENT '企业服务费比例',`order_time` datetime DEFAULT NULL COMMENT '下单时间',`latest_refund_time` datetime DEFAULT NULL COMMENT '最后退款时间',`pay_expire_at` datetime DEFAULT NULL COMMENT '支付之后有效时间',`pay_time` datetime DEFAULT NULL COMMENT '支付时间',`pay_type` tinyint(4) DEFAULT NULL COMMENT '支付方式 1-微信支付,2-支付宝支付,3-骑士币支付,31-骑士币+微信支付,32-骑士币+支付宝支付',`pay_status` tinyint(4) NOT NULL COMMENT '支付状态',`shop_name` varchar(128) DEFAULT NULL COMMENT '商家名',`shop_phone` varchar(100) DEFAULT NULL COMMENT '商家电话,多个,隔开',`shop_address` varchar(300) DEFAULT NULL COMMENT '商家地址',`estimate_arrive_time` datetime DEFAULT NULL COMMENT '预计送达时间',`actual_arrive_time` datetime DEFAULT NULL COMMENT '实际送达时间',`outer_order_id` varchar(100) NOT NULL COMMENT '外部订单ID',`outer_order_status` int(11) DEFAULT NULL COMMENT '外部订单状态',`recipient_name` varchar(64) DEFAULT NULL COMMENT '收货人姓名',`recipient_phone` varchar(64) DEFAULT NULL COMMENT '收货人手机号',`recipient_address` varchar(128) DEFAULT NULL COMMENT '收货地址',`recipient_address_longitude` varchar(50) DEFAULT NULL COMMENT '收货地址经度  实际值需要除以 10^6 进行换算',`recipient_address_latitude` varchar(50) DEFAULT NULL COMMENT '收货地址维度	实际值需要除以 10^6 进行换算',`notify_url` varchar(500) DEFAULT NULL COMMENT '渠道服务器主动通知外部服务器里指定的页面路径',`return_url` varchar(500) DEFAULT NULL COMMENT '操作成功跳转地址',`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',`version` bigint(20) DEFAULT '0' COMMENT '版本号',PRIMARY KEY (`id`),UNIQUE KEY `uniq_order_id` (`order_id`),KEY `idx_employee_sn` (`employee_sn`),KEY `idx_order_id` (`outer_order_id`),KEY `idx_trade_no` (`trade_no`),KEY `idx_user_sn` (`user_sn`),KEY `idx_create_time_status` (`create_time`,`order_status`)
) ENGINE=InnoDB AUTO_INCREMENT=406 DEFAULT CHARSET=utf8mb4 COMMENT='外卖订单表'

表中的字段很多,如果只是获取其中某些列,数据库也需要把整行加载到内存再截取某些列,这样自然增加了IO成本。

在外卖订单里面,订单ID、订单状态、配送状态、用户Sn、支付状态等经常更新和在订单列表查询到,作为热点数据。

企业信息、服务费、商家信息、收货人信息等更新频率极低,并且只在订单详情查询,作为非热点数据。

外卖订单表垂直拆分如下:

CREATE TABLE `meal_order` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',`order_id` bigint(20) NOT NULL COMMENT '订单ID',`order_type` tinyint(4) NOT NULL COMMENT '外卖类型:4:外卖;1:买单;3:团购',`order_status` tinyint(4) NOT NULL COMMENT '订单状态:1:未付款;2:已付款;4:部分退款;5:已退款;6:已完成;-1:已取消',`delivery_status` int(11) DEFAULT NULL COMMENT '配送状态:0:默认;0:已推送给配送方;10:已抢单;20:已取餐;40:已送达;100:已取消',`user_sn` bigint(20) NOT NULL COMMENT 'userSn',`order_amount` decimal(12,2) NOT NULL DEFAULT '0.00' COMMENT '订单总价',`order_pay_amount` decimal(12,2) NOT NULL DEFAULT '0.00' COMMENT '订单支付价格',`order_time` datetime DEFAULT NULL COMMENT '下单时间',`pay_expire_at` datetime DEFAULT NULL COMMENT '支付之后有效时间',`pay_time` datetime DEFAULT NULL COMMENT '支付时间',`pay_type` tinyint(4) DEFAULT NULL COMMENT '支付方式 1-微信支付,2-支付宝支付,3-骑士币支付,31-骑士币+微信支付,32-骑士币+支付宝支付',`pay_status` tinyint(4) NOT NULL COMMENT '支付状态',`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',`version` bigint(20) DEFAULT '0' COMMENT '版本号',PRIMARY KEY (`id`),UNIQUE KEY `uniq_order_id` (`order_id`),KEY `idx_user_sn` (`user_sn`),KEY `idx_create_time_status` (`create_time`,`order_status`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='外卖订单表';CREATE TABLE `meal_order_attach` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',`trade_no` varchar(128) NOT NULL DEFAULT '' COMMENT '交易流水号',`order_id` bigint(20) NOT NULL COMMENT '订单ID',`consumption_type` tinyint(4) NOT NULL DEFAULT '1' COMMENT '类型,1=个人,2=商务',`employee_sn` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '员工SN',`enterprise_sn` varchar(20) NOT NULL DEFAULT '' COMMENT '企业编号',`shipping_fee` decimal(12,2) DEFAULT NULL COMMENT '订单配送费(原始配送费)',`service_fee` decimal(12,2) DEFAULT NULL COMMENT '服务费',`ent_service_fee` decimal(15,2) NOT NULL DEFAULT '0.00' COMMENT '企业服务费',`ent_service_fee_ratio` decimal(15,2) DEFAULT NULL COMMENT '企业服务费比例',`latest_refund_time` datetime DEFAULT NULL COMMENT '最后退款时间',`shop_name` varchar(128) DEFAULT NULL COMMENT '商家名',`shop_phone` varchar(100) DEFAULT NULL COMMENT '商家电话,多个,隔开',`shop_address` varchar(300) DEFAULT NULL COMMENT '商家地址',`estimate_arrive_time` datetime DEFAULT NULL COMMENT '预计送达时间',`actual_arrive_time` datetime DEFAULT NULL COMMENT '实际送达时间',`outer_order_id` varchar(100) NOT NULL COMMENT '外部订单ID',`outer_order_status` int(11) DEFAULT NULL COMMENT '外部订单状态',`recipient_name` varchar(64) DEFAULT NULL COMMENT '收货人姓名',`recipient_phone` varchar(64) DEFAULT NULL COMMENT '收货人手机号',`recipient_address` varchar(128) DEFAULT NULL COMMENT '收货地址',`recipient_address_longitude` varchar(50) DEFAULT NULL COMMENT '收货地址经度  实际值需要除以 10^6 进行换算',`recipient_address_latitude` varchar(50) DEFAULT NULL COMMENT '收货地址维度	实际值需要除以 10^6 进行换算',`notify_url` varchar(500) DEFAULT NULL COMMENT '渠道服务器主动通知外部服务器里指定的页面路径',`return_url` varchar(500) DEFAULT NULL COMMENT '操作成功跳转地址',`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',`version` bigint(20) DEFAULT '0' COMMENT '版本号',PRIMARY KEY (`id`),UNIQUE KEY `uniq_order_id` (`order_id`),KEY `idx_employee_sn` (`employee_sn`),KEY `idx_order_id` (`outer_order_id`),KEY `idx_trade_no` (`trade_no`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='外卖订单表拓展信息';

meal_order 表保存热点数据,meal_order_attach表保存非热点数据。

meal_order 表可以很好的支持列表查询、订单状态更新等热点数据,并且表列数比原来少了一半多。

水平分表

什么是水平分表

水平分表就是指以行为单位对数据进行拆分,一般意义上的分库分表指的就是水平分表。

分表之后,所有表的结构都是一样的。

水平分表需要额外引入中间主键完成,例如shardingSphere等。

为什么要水平分表

水平分表可以解决表中的数据量大的问题,如果一张表的数据太多,操作起来会很麻烦,所以分表存储数据。

水平分表有哪些好处呢?

  1. 表数据量小,如分表算法合适,每个表大小相似;
  2. 拓展方便,如数据量继续增大,可以在增加表;

但凡事有两面性,水平分表需要选择合适的列进行,管理后台查询是需要union 或者引入ES等组件。

水平分表实战

水平分表算法

一般可以有范围法和hash法来进行水平分表。

加入未来5年数据量增加到1亿,预算每个表1千万数据,那么需要分成10个表。

范围法很好理解,可以让第1-1千万行数据存放在表1,第1千万01-2千万行数据存放在表2,以此类推。
范围法也有缺点,冷热数据不均匀,例如订单查询和操作都是最近的,几个月或者更早一起的订单便很少有操作。

hash法也不难理解,就是选择一个合适的hash函数对指定的列计算哈希码后进行取模,比如说使用取模操作(%),把%3结果为0的数据存放在表1,结果为1的存放在表2,结果为2的存放在表3即可。
hash法也有缺点,后续再就行分表时,需要改造hash函数,或者迁移就数据。


http://www.ppmy.cn/news/240344.html

相关文章

simplify3d 打印参数设置笔记

专业实用且详细的打印教程 3D打印故障排除:所有问题和解决方案 simplify3d 切片软件使用 初始设置 材料:PLA 喷头温度 215 热床温度 60 导入的模型格式 stl 保存的模型名称可以是中文 但是名称中不允许有空格,否则我这个打印机加热好喷头和…

C/C++基础讲解(一百零五)之经典篇(找出三队赛手/打印菱形/前n项和)

C/C++基础讲解(一百零五)之经典篇(找出三队赛手/打印菱形/前n项和) 程序之美 前言 很多时候,特别是刚步入大学的学子们,对于刚刚开展的计算机课程基本上是一团迷雾,想要弄明白其中的奥秘,真的要花费一些功夫,我和大家一样都是这么啃过来的,从不知到知知,懵懂到入门,…

实战【金融评分卡】

金融评分卡 网站:百融、同盾 1.导入 信贷评分卡是一种用于评估个人或企业申请贷款的工具,就像-个评分表-样。我们可以把它看作是银行或金融机构用来判断某人是否有资格获得贷款的一-种方式。 想象一下,你是一个银行家,有很多人向你申请贷款,比如个人贷…

PowerShell install 一键部署mariadb10.11

mariadb MariaDB数据库管理系统是MySQL的一个分支,主要由开源社区在维护,采用GPL授权许可 MariaDB的目的是完全兼容MySQL,包括API和命令行,使之能轻松成为MySQL的代替品。在存储引擎方面,使用XtraDB来代替MySQL的Inno…

VTK学习之vtkProp

vtkProp。渲染场景中数据的可视表达(Visible Depictions)是由vtkProp的子类负责。 也就是说,数据想要进行可视化显示,需要一个转换过程,这个过程就是转换为vtkProp 这样才能进行渲染展示出来。 而vtkProp子类是vtkA…

【GreendDao 】RxQuery根据指定条件查询,完成后处理UI逻辑

GreenDao 和 RxJava 结合使用可以更方便地处理数据查询和 UI 逻辑的交互。RxQuery 使得一次查询结果可以直接转化成 Observable,而通过 RxJava 的操作符,可以方便地完成异步查询和 UI 逻辑的交互。以下是一个根据指定条件查询数据,查询完成后…

Android万能播放器

Android万能播放器 0.eclipse直接导入即可食用(亲,有两个是库工程哦) 1.基于Vitamio的万能播放器(自己百度哈) 2.扫描本地视频,获取每个视频第一帧,并显示 3.手势控制音量、亮度 4.获取到的…

万能格式音视频播放器KMPlayer Plus (Divx)_31.02.100Pro版

Pro版由就要分享网91apps.cn的SOLDIER分享,谈到音视频播放器软件,大家一定会记得昔日的王者——KMplayer!对,你没看错,曾经在 Windows 上有着霸主地位的万能格式音视频播放器。KMPlayer Mobile (移动版)和PC版一样同样…