有关数据库表的冗余字段
有关数据库表的冗余字段
之前看一个开发人员的技术研讨视频,提到了一个数据库表设计中的表拆分字段冗余问题,就是一张表做纵向分表,拆分为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类,以及对应的纯文…...

地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)
2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...
pam_env.so模块配置解析
在PAM(Pluggable Authentication Modules)配置中, /etc/pam.d/su 文件相关配置含义如下: 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块,负责验证用户身份&am…...

什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...
macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用
文章目录 问题现象问题原因解决办法 问题现象 macOS启动台(Launchpad)多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显,都是Google家的办公全家桶。这些应用并不是通过独立安装的…...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...

HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...

安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)
船舶制造装配管理现状:装配工作依赖人工经验,装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书,但在实际执行中,工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...