有关数据库表的冗余字段
有关数据库表的冗余字段

之前看一个开发人员的技术研讨视频,提到了一个数据库表设计中的表拆分字段冗余问题,就是一张表做纵向分表,拆分为a和b以做冷热数据分离存储,但是会有一种情况就是相同的字段值在a,b表中重复出现.这是因为查a但是需要这个字段就需要再去查b,多了一次查询,但是把这个字段存储在a中就可以减少很多次再查询,那么就有了这个必要冗余
实习开发中就遇到了这种情况的一个例子,加深了自己对这个问题的理解,做一个记录分享:
实例
有一个分页查询,查询条件里有一个筛选字段是用户姓名,这个分页数据是从表a中取出的
我们假设表a中有用户姓名字段,那么使用mybatis-plus查询时,只需要通过wrapper上通过like拼接上用户姓名就可以了,如下:
queryWrapper.like(StrUtil.isNotBlank(query.getUserName()), OperateLogDO::getUserName, query.getUserName())
可是如果表a只存有userId,没有username会是什么逻辑呢?
那就需要先到user表里先根据username进行一次模糊匹配查询,获取到符合条件的所有userIds,然后在wrapper中利用in拼接userIds条件,那么这多余一次查询在数据量很大或是没有索引的情况下的时间开销还是不容忽视的
//先根据username查出userIds,然后组装分页参数 private Boolean rebuildParamByUsername(OperationLogQueryParam queryParam) {Set<Long> userIds = userManager.allLikeQueryByName(queryParam.getUserName());//构建包含这些用户ID的查询条件 return buildIncludeUserIds(queryParam,userIds);} //... //in(id1,id2...) queryWrapper.in(CollUtil.isNotEmpty(queryParam.getIncludeUserIds()), OperationLogDO::getUserId, queryParam.getIncludeUserIds())
经过这个例子就对之前提到的"字段冗余"有了更深的理解了,其实就是一种空间换时间的思想
更多思考
1.冗余字段数据一致性
既然在两个地方存了相同的数据,那么保证数据一致性就是少不了的话题,这里提出两种场景的方案
强一致性
是指要确保任何时刻查询到的数据都是最新的,也就是二者的数据要保证即使高度一致,不能容忍不一致性的出现,常见于金融交易/库存扣减等对一致性要求极高的业务.
一般通过数据库事务/分布式事务来保证
-
数据库事务
-
单数据库情况下利用事务提交机制来实现
BEGIN TRANSACTION; -- 更新主表(热表) UPDATE order_hot SET user_id = 456 WHERE order_id = 123; -- 同步更新冗余表(冷表) UPDATE order_cold SET user_id = 456 WHERE order_id = 123; COMMIT; -
缺点是跨表事务可能引发锁竞争,影响并发性能
-
-
分布式事务
-
两阶段提交(2PC)
- Prepare阶段:协调者询问所有参与者是否可提交。
- Commit/Abort阶段:根据Prepare结果提交或回滚。
-
MySQL XA事务
-- 参与者1(热表所在库) XA START 'tx1'; UPDATE order_hot SET user_id = 456 WHERE order_id = 123; XA END 'tx1'; XA PREPARE 'tx1';-- 参与者2(冷表所在库) XA START 'tx2'; UPDATE order_cold SET user_id = 456 WHERE order_id = 123; XA END 'tx2'; XA PREPARE 'tx2';-- 协调者提交 XA COMMIT 'tx1'; XA COMMIT 'tx2'; -
缺点:性能较差,XA事务成功率依赖网络和参与者稳定性
-
弱一致性
允许冗余字段出现短暂不一致,适用于日志记录等对实时性要求不高的场景
通过**异步机制(消息队列,定时任务)**实现最终一致
-
MQ消息队列
-
场景:高并发写入,容忍秒级延迟
-
流程
-
实例
生产者(更新热表后发消息)
jdbc.update("UPDATE order_hot SET user_id=? WHERE order_id=?", 456, 123); // 发送消息 mq.send("topic:user_update", "{order_id:123, user_id:456}");消费者(消费消息更新冷表)
mq.subscribe("topic:user_update", message -> {jdbc.update("UPDATE order_cold SET user_id=? WHERE order_id=?", message.user_id, message.order_id); }); -
缺点:依赖消息队列的可靠性(需解决消息丢失、重复消费问题)
-
-
版本号+定时任务
-
场景:数据更新频率较低,允许分钟级延迟。
-
实现
- 热表和冷表均增加版本号字段。
- 更新热表时递增版本号。
- 定时任务扫描版本号不一致的数据并修复。
-- 表结构 ALTER TABLE order_hot ADD COLUMN version INT DEFAULT 0; ALTER TABLE order_cold ADD COLUMN version INT DEFAULT 0;-- 定时任务(伪代码) SELECT h.order_id, h.user_id, h.version FROM order_hot h JOIN order_cold c ON h.order_id = c.order_id WHERE h.version > c.version;-- 修复不一致数据 UPDATE order_cold SET user_id = {热表.user_id}, version = {热表.version} WHERE order_id = {不一致的订单ID}; -
缺点:定时任务存在延迟,可能影响用户体验。
-
2.冗余字段的设计原则
哪些字段适合做冗余?如何评判优化效果?
- 关联查询字段
- 场景:多表关联查询(JOIN)的字段,冗余后可直接查询单表。
- 比如:
- 在评论表中冗余用户头像URL(
avatar_url),避免关联用户表。 - 在文章表中冗余作者姓名(
author_name),避免关联作者表。
- 在评论表中冗余用户头像URL(
- 评估效果
- 冗余后的查询时间 = 单表查询时间
- 冗余前的查询时间 = 单表查询时间 + JOIN时间 + 网络延迟
- 若单表查询时间 < 50%总时间,冗余可能有效。
-
高频查询&低频更新字段
-
场景:该字段被频繁用于查询条件或结果展示且更新频率低,冗余后维护成本可控。
-
比如:
- 订单表中冗余用户ID(
user_id),避免查询订单时关联用户表。 - 商品表中冗余分类名称(
category_name),避免关联分类表获取名称。 - 用户表中的地区编码(
region_code),区域信息几乎不变。 - 商品表中的品牌名称(
brand_name),品牌更名频率极低。
- 订单表中冗余用户ID(
-
判断依据
-
通过SQL慢查询日志或APM工具(如Arthas、New Relic)统计该字段的查询频率。若该字段出现在80%以上的查询中,优先考虑冗余。
-
统计字段的日更新率(如每天更新次数 / 总数据量)。若更新率低于1%,适合冗余;高于5%需谨慎。
-
-
-
实例
电商订单表设计
- 业务需求
- 高频查询订单列表时需要显示用户昵称(
nickname)和商品标题(product_title)。- 原方案需JOIN用户表和商品表,平均查询时间120ms。
- 冗余方案
- 在订单表中冗余
nickname和product_title字段。- 更新逻辑:
- 下单时从用户表和商品表写入冗余字段(强一致性)。
- 用户修改昵称时异步更新历史订单(最终一致性)(如果订单表中的冗余字段
nickname未更新,查询订单列表时会显示旧昵称(“TechGuy2020”),与用户当前信息不一致)。- 效果评估
- 性能提升:
- 单表查询时间降至40ms,QPS从500提升至1500。
- 存储成本:
- 新增两个VARCHAR(100)字段,总存储增加18GB(1亿订单)。
- 一致性风险:
- 用户修改昵称后,历史订单更新延迟1分钟内完成(业务可接受)。
同时要注意遵循“最小化冗余”原则,优先冗余最关键的1-2个字段。比如不要冗余10个字段以消除所有JOIN,导致更新逻辑复杂化。
3.连表查及三种方案对比
| 维度 | 字段冗余 | JOIN联表查 | 先查ID再用IN查询 |
|---|---|---|---|
| 查询速度 | ⭐⭐⭐⭐(单表快速) | ⭐⭐(JOIN开销大) | ⭐(两次查询+IN性能差) |
| 存储成本 | ⭐(冗余字段占用空间) | ⭐⭐⭐⭐(无冗余) | ⭐⭐⭐⭐(无冗余) |
| 数据一致性维护成本 | ⭐(需同步更新冗余字段) | ⭐⭐⭐⭐(无需维护) | ⭐⭐⭐⭐(无需维护) |
| 高并发写入场景 | ⚠️ 差(更新需同步冗余字段) | ⭐⭐⭐⭐(无额外压力) | ⭐⭐⭐⭐(无额外压力) |
| 模糊查询性能 | ⭐⭐⭐(可走索引) | ⚠️ 依赖用户表索引优化 | ⚠️ 依赖用户表模糊查询效率 |
| IN查询限制 | 无 | 无 | ⚠️ IN列表过长导致性能骤降 |
详细分析与设计建议
1. 字段冗余方案
-
适用场景:
- 高频根据
username查询表A(如日志查询、报表展示)。 username更新频率低(如用户每月修改一次昵称)。
- 高频根据
-
优化建议:
- 为冗余字段
username添加索引(如联合索引)。 - 使用最终一致性更新策略(异步消息同步)。
- 为冗余字段
-
示例优化:sql
-- 添加联合索引(假设常按时间范围+username查询) CREATE INDEX idx_username_created ON table_a (username, created_time);
2. JOIN联表查方案
-
适用场景:
- 表A与用户表数据量较小(如百万级以内)。
- 用户表
username字段有高效索引(如前缀索引)。
-
优化建议:
-
避免全模糊查询(
LIKE '%xxx%'),改用右模糊(LIKE 'xxx%')以利用索引。 -
使用覆盖索引减少回表:
-- 用户表索引优化 CREATE INDEX idx_username ON user (username);
-
3. 先查ID再用IN查询方案
-
适用场景:
- 不推荐
-
分页陷阱规避:
// 分批查询(避免IN列表过长) List<Long> userIds = userService.listUserIdsByName(query.getUserName()); List<List<Long>> batches = Lists.partition(userIds, 1000); batches.forEach(batch -> {queryWrapper.in(CollUtil.isNotEmpty(batch), OperationLogDO::getUserId, batch);// 执行分页查询并合并结果 });
如何选择方案?
- 是否高频查询?
- ✅ 是 → 字段冗余。
- ❌ 否 → 进入下一步。
- 用户表模糊查询是否高效?
- ✅ 是(如索引优化后右模糊) → JOIN联表查。
再往后就没有下一步了,从上面的表格我们可以看出,在轻量级数据查询情况下,join是碾压in的,现在来看原本代码的in方案是非常不可取的
笔者只是从遇到的一个简单实例中回想起看过的这个知识点,所以做了一些搜索总结,一查才发现还有数据一致性的问题,然后又拓展到了冗余字段的设计原则和其他方案的对比,不得感叹ai的出现减少了多少学习成本.本人也只是个小白,只是对内容做搜索总结,如果有错误的地方,也恳请指出交流.
此外上文还隐含一个一个点,就是in查询列表过长导致的性能崩坏问题,这个下篇文章再做讨论…
相关文章:
有关数据库表的冗余字段
有关数据库表的冗余字段 之前看一个开发人员的技术研讨视频,提到了一个数据库表设计中的表拆分字段冗余问题,就是一张表做纵向分表,拆分为a和b以做冷热数据分离存储,但是会有一种情况就是相同的字段值在a,b表中重复出现…...
知识图谱补全KGC
目录 基础知识知识图谱补全概念性能指标 一、翻译模型的知识图谱补全1.TransE2.TransH3.RotatE 二、张量分解的知识补全1.RESCAL2.ComplEx 三、神经网络的知识图谱补全1.卷积神经网络CNN(一般用于二维图像处理)ConvE 2.循环神经网络RNN3.图神经网络GNN1&…...
独立开发者的内容营销教程
内容营销对于独立开发者来说,是一种低成本、高效的方式来推广产品、建立品牌影响力和吸引潜在用户。通过分享有价值、相关性强的内容,您可以吸引用户的注意力,增强用户黏性,并最终将他们转化为忠实用户或客户。以下是详细的独立开…...
Mysql——约束与多表查询
一、约束 1.1定义 约束是对表中的数据进行限制的一套规则,用于防止用户向数据库中输入无效数据。它可以保证表中的数据满足特定业务规则和逻辑,从而维护数据的准确性和可靠性。 1.2作用 数据完整性 :约束可以确保数据在插入、更新或删除时符…...
DockerでOracle Database 23ai FreeをセットアップしMAX_STRING_SIZEを拡張する手順
DockerでOracle Database 23c FreeをセットアップしMAX_STRING_SIZEを拡張する手順 はじめに環境準備ディレクトリ作成Dockerコンテナ起動 データベース設定変更コンテナ内でSQL*Plus起動PDB操作と文字列サイズ拡張設定検証 管理者ユーザー作成注意事項まとめ はじめに Oracle…...
Unity 运用正则表达式保留字符串中的中文英文字母和数字
正则表达 正则表达式 – 语法 | 菜鸟教程 Regex 类 (System.Text.RegularExpressions) | Microsoft Learn 保留字符串中的中英数 中英数的正则表达。 patten "[\u4e00-\u9fa5A-Za-z0-9]"; 使用Regex 类匹配正则并保留。 matches Regex.Matches(str, patten)…...
vue el-table-column 单元表格的 省略号 实现
要对 el-table-column 的某一列中的每个单元格值进行处理,使其在文本内容超出指定宽度时显示省略号(…),可以通过以下方法实现: 使用 scoped slots:利用 Element UI 提供的 scoped slots 自定义单元格内容…...
企业微信里可以使用的企业内刊制作工具,FLBOOK
如何让员工及时了解公司动态、行业资讯、学习专业知识,并有效沉淀企业文化?一份高质量的企业内刊是不可或缺的。现在让我来教你该怎么制作企业内刊吧 1.登录与上传 访问FLBOOK官网,注册账号后上传排版好的文档 2.选择模板 FLBOOK提供了丰富的…...
【数据挖掘】Pandas
Pandas 是 Python 进行 数据挖掘 和 数据分析 的核心库之一,提供了强大的 数据清洗、预处理、转换、分析 和 可视化 功能。它通常与 NumPy、Matplotlib、Seaborn、Scikit-Learn 等库结合使用,帮助构建高效的数据挖掘流程。 📌 1. 读取数据 P…...
explore与explode词源故事
英语单词explore来自古法语,源自拉丁语,由前缀ex-(出来)加词根plor-(叫喊)以及末尾的小尾巴-e组成,字面意思就是“喊出来,通过叫喊声赶出来”。它为什么能表示“探索”呢?…...
CAM350_安装
版本:V14.5 一、安装 打开.exe文件 选择不重启,然后再打开这个.exe 再来一次类似的操作 二、配置 复制patch文件夹中的这三个 ,粘贴到掉安装目录中 设置ACT_INC_LICENSE_FILE用户环境变量来设置license管理 打开电脑的环境变量 破解完毕&am…...
51c自动驾驶~合集22
我自己的原文哦~ https://blog.51cto.com/whaosoft/11870502 #自动驾驶数据闭环最前沿论文 近几年,自动驾驶技术的发展日新月异。从ECCV 2020的NeRF问世再到SIGGRAPH 2023的3DGS,三维重建走上了快速发展的道路!再到自动驾驶端到端技术的…...
games101 作业5
题目 光线追踪的核心算法: 1.光线的生成 2.光线与三角的相交 题解 1.光线的生成 如课件中的图所示: image plane 就是 代码中的scene的FrameBuffer。 但是,FrameBuffer 是窗口坐标系中,而光线是世界坐标系中的。所以我们需要将scene中的屏…...
【高并发秒杀系统设计:从Guava到Redis的6级缓存架构演进】
一、瞬时十万QPS场景分析 1.1 典型秒杀场景特征 public class SpikeScenario {// 特征1:瞬时流量突增private static final int QPS 100000; // 正常流量100倍// 特征2:资源竞争激烈private int stock 1000; // 100万人抢1000件商品// 特征3&#…...
2-程序语言基础知识
本节内容不是很多,在考试中一般是2-3分,多看教材,考试中大概都是原话 本节主要考点 1、编译程序和解释程序 2、程序语言的数据成分和控制成分(多看教材) 3、编译程序的过程(多看教材) 4、中缀、前缀与后缀表达式(增加) 教材P42-52,54-58多看一下 1、程序语言概述 机器…...
【AIGC系列】4:Stable Diffusion应用实践和代码分析
AIGC系列博文: 【AIGC系列】1:自编码器(AutoEncoder, AE) 【AIGC系列】2:DALLE 2模型介绍(内含扩散模型介绍) 【AIGC系列】3:Stable Diffusion模型原理介绍 【AIGC系列】4࿱…...
小米火龙CPU和其他几代温度太高的CPU是由谁代工的
小米火龙CPU”并非小米自研芯片,而是指搭载在小米手机上的部分高通骁龙处理器因发热问题被调侃为“火龙”。以下是几款被称为“火龙”的高通CPU及其代工情况: 骁龙810 骁龙810是高通历史上最著名的“火龙”之一,采用台积电20nm工艺代工。由于…...
在 ASP.NET Core 中压缩并减少图像的文件大小
示例代码:https://download.csdn.net/download/hefeng_aspnet/90294127 在当今的数字时代,图像是 Web 应用程序和用户体验不可或缺的一部分。但是,处理大型图像文件可能会导致网页加载缓慢和更高的存储费用。为了解决这个问题,在…...
网络流算法: Dinic算法
图论相关帖子 基本概念图的表示: 邻接矩阵和邻接表图的遍历: 深度优先与广度优先拓扑排序图的最短路径:Dijkstra算法和Bellman-Ford算法最小生成树二分图多源最短路径强连通分量欧拉回路和汉密尔顿回路网络流算法: Edmonds-Karp算法网络流算法: Dinic算法 环境要求 本文所用…...
【Godot4.3】自定义简易菜单栏节点ETDMenuBar
概述 Godot中的菜单创建是一个复杂的灾难性工作,往往无从下手,我也是不止一次尝试简化菜单的创建。 从自己去年的发明“简易树形数据”用于简化Tree控件获得灵感,于是尝试编写了用于表示菜单数据的EasyMenuData类,以及对应的纯文…...
多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度
一、引言:多云环境的技术复杂性本质 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时,基础设施的技术债呈现指数级积累。网络连接、身份认证、成本管理这三大核心挑战相互嵌套:跨云网络构建数据…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...
【机器视觉】单目测距——运动结构恢复
ps:图是随便找的,为了凑个封面 前言 在前面对光流法进行进一步改进,希望将2D光流推广至3D场景流时,发现2D转3D过程中存在尺度歧义问题,需要补全摄像头拍摄图像中缺失的深度信息,否则解空间不收敛…...
大语言模型如何处理长文本?常用文本分割技术详解
为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...
高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...
基于IDIG-GAN的小样本电机轴承故障诊断
目录 🔍 核心问题 一、IDIG-GAN模型原理 1. 整体架构 2. 核心创新点 (1) 梯度归一化(Gradient Normalization) (2) 判别器梯度间隙正则化(Discriminator Gradient Gap Regularization) (3) 自注意力机制(Self-Attention) 3. 完整损失函数 二…...
iview框架主题色的应用
1.下载 less要使用3.0.0以下的版本 npm install less2.7.3 npm install less-loader4.0.52./src/config/theme.js文件 module.exports {yellow: {theme-color: #FDCE04},blue: {theme-color: #547CE7} }在sass中使用theme配置的颜色主题,无需引入,直接可…...
wpf在image控件上快速显示内存图像
wpf在image控件上快速显示内存图像https://www.cnblogs.com/haodafeng/p/10431387.html 如果你在寻找能够快速在image控件刷新大图像(比如分辨率3000*3000的图像)的办法,尤其是想把内存中的裸数据(只有图像的数据,不包…...
学习一下用鸿蒙DevEco Studio HarmonyOS5实现百度地图
在鸿蒙(HarmonyOS5)中集成百度地图,可以通过以下步骤和技术方案实现。结合鸿蒙的分布式能力和百度地图的API,可以构建跨设备的定位、导航和地图展示功能。 1. 鸿蒙环境准备 开发工具:下载安装 De…...
jdbc查询mysql数据库时,出现id顺序错误的情况
我在repository中的查询语句如下所示,即传入一个List<intager>的数据,返回这些id的问题列表。但是由于数据库查询时ID列表的顺序与预期不一致,会导致返回的id是从小到大排列的,但我不希望这样。 Query("SELECT NEW com…...
