当前位置: 首页 > news >正文

有关数据库表的冗余字段

有关数据库表的冗余字段

在这里插入图片描述

之前看一个开发人员的技术研讨视频,提到了一个数据库表设计中的表拆分字段冗余问题,就是一张表做纵向分表,拆分为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消息队列

    • 场景:高并发写入,容忍秒级延迟

    • 流程

      更新热表
      发送MQ消息
      消费者更新冷表
    • 实例

      生产者(更新热表后发消息)

      jdbc.update("UPDATE order_hot SET user_id=? WHERE order_id=?"456123);
      // 发送消息
      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);
      });
      
    • 缺点:依赖消息队列的可靠性(需解决消息丢失、重复消费问题)

  • 版本号+定时任务

    • 场景:数据更新频率较低,允许分钟级延迟。

    • 实现

      1. 热表和冷表均增加版本号字段。
      2. 更新热表时递增版本号。
      3. 定时任务扫描版本号不一致的数据并修复。
      -- 表结构
      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.冗余字段的设计原则

哪些字段适合做冗余?如何评判优化效果?
  1. 关联查询字段
  • 场景:多表关联查询(JOIN)的字段,冗余后可直接查询单表。
  • 比如:
    • 在评论表中冗余用户头像URL(avatar_url),避免关联用户表。
    • 在文章表中冗余作者姓名(author_name),避免关联作者表。
  • 评估效果
    • 冗余后的查询时间 = 单表查询时间
    • 冗余前的查询时间 = 单表查询时间 + JOIN时间 + 网络延迟
    • 若单表查询时间 < 50%总时间,冗余可能有效
  1. 高频查询&低频更新字段

    • 场景:该字段被频繁用于查询条件或结果展示且更新频率低,冗余后维护成本可控。

    • 比如:

      • 订单表中冗余用户ID(user_id),避免查询订单时关联用户表。
      • 商品表中冗余分类名称(category_name),避免关联分类表获取名称。
      • 用户表中的地区编码(region_code),区域信息几乎不变。
      • 商品表中的品牌名称(brand_name),品牌更名频率极低。
    • 判断依据

      • 通过SQL慢查询日志或APM工具(如Arthas、New Relic)统计该字段的查询频率。若该字段出现在80%以上的查询中,优先考虑冗余。

      • 统计字段的日更新率(如每天更新次数 / 总数据量)。若更新率低于1%,适合冗余;高于5%需谨慎。

  2. 实例

电商订单表设计
  1. 业务需求
    • 高频查询订单列表时需要显示用户昵称(nickname)和商品标题(product_title)。
    • 原方案需JOIN用户表和商品表,平均查询时间120ms。
  2. 冗余方案
    • 在订单表中冗余nicknameproduct_title字段。
    • 更新逻辑:
      • 下单时从用户表和商品表写入冗余字段(强一致性)。
      • 用户修改昵称时异步更新历史订单(最终一致性)(如果订单表中的冗余字段 nickname 未更新,查询订单列表时会显示旧昵称(“TechGuy2020”),与用户当前信息不一致)。
  3. 效果评估
    • 性能提升:
      • 单表查询时间降至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);// 执行分页查询并合并结果
    });
    

如何选择方案?
  1. 是否高频查询?
    • ✅ 是 → 字段冗余
    • ❌ 否 → 进入下一步。
  2. 用户表模糊查询是否高效?
    • ✅ 是(如索引优化后右模糊) → JOIN联表查

再往后就没有下一步了,从上面的表格我们可以看出,在轻量级数据查询情况下,join是碾压in的,现在来看原本代码的in方案是非常不可取的

笔者只是从遇到的一个简单实例中回想起看过的这个知识点,所以做了一些搜索总结,一查才发现还有数据一致性的问题,然后又拓展到了冗余字段的设计原则和其他方案的对比,不得感叹ai的出现减少了多少学习成本.本人也只是个小白,只是对内容做搜索总结,如果有错误的地方,也恳请指出交流.

此外上文还隐含一个一个点,就是in查询列表过长导致的性能崩坏问题,这个下篇文章再做讨论…

相关文章:

有关数据库表的冗余字段

有关数据库表的冗余字段 之前看一个开发人员的技术研讨视频&#xff0c;提到了一个数据库表设计中的表拆分字段冗余问题&#xff0c;就是一张表做纵向分表&#xff0c;拆分为a和b以做冷热数据分离存储&#xff0c;但是会有一种情况就是相同的字段值在a&#xff0c;b表中重复出现…...

知识图谱补全KGC

目录 基础知识知识图谱补全概念性能指标 一、翻译模型的知识图谱补全1.TransE2.TransH3.RotatE 二、张量分解的知识补全1.RESCAL2.ComplEx 三、神经网络的知识图谱补全1.卷积神经网络CNN&#xff08;一般用于二维图像处理&#xff09;ConvE 2.循环神经网络RNN3.图神经网络GNN1&…...

独立开发者的内容营销教程

内容营销对于独立开发者来说&#xff0c;是一种低成本、高效的方式来推广产品、建立品牌影响力和吸引潜在用户。通过分享有价值、相关性强的内容&#xff0c;您可以吸引用户的注意力&#xff0c;增强用户黏性&#xff0c;并最终将他们转化为忠实用户或客户。以下是详细的独立开…...

Mysql——约束与多表查询

一、约束 1.1定义 约束是对表中的数据进行限制的一套规则&#xff0c;用于防止用户向数据库中输入无效数据。它可以保证表中的数据满足特定业务规则和逻辑&#xff0c;从而维护数据的准确性和可靠性。 1.2作用 数据完整性 &#xff1a;约束可以确保数据在插入、更新或删除时符…...

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 的某一列中的每个单元格值进行处理&#xff0c;使其在文本内容超出指定宽度时显示省略号&#xff08;…&#xff09;&#xff0c;可以通过以下方法实现&#xff1a; 使用 scoped slots&#xff1a;利用 Element UI 提供的 scoped slots 自定义单元格内容…...

企业微信里可以使用的企业内刊制作工具,FLBOOK

如何让员工及时了解公司动态、行业资讯、学习专业知识&#xff0c;并有效沉淀企业文化&#xff1f;一份高质量的企业内刊是不可或缺的。现在让我来教你该怎么制作企业内刊吧 1.登录与上传 访问FLBOOK官网&#xff0c;注册账号后上传排版好的文档 2.选择模板 FLBOOK提供了丰富的…...

【数据挖掘】Pandas

Pandas 是 Python 进行 数据挖掘 和 数据分析 的核心库之一&#xff0c;提供了强大的 数据清洗、预处理、转换、分析 和 可视化 功能。它通常与 NumPy、Matplotlib、Seaborn、Scikit-Learn 等库结合使用&#xff0c;帮助构建高效的数据挖掘流程。 &#x1f4cc; 1. 读取数据 P…...

explore与explode词源故事

英语单词explore来自古法语&#xff0c;源自拉丁语&#xff0c;由前缀ex-&#xff08;出来&#xff09;加词根plor-&#xff08;叫喊&#xff09;以及末尾的小尾巴-e组成&#xff0c;字面意思就是“喊出来&#xff0c;通过叫喊声赶出来”。它为什么能表示“探索”呢&#xff1f…...

CAM350_安装

版本&#xff1a;V14.5 一、安装 打开.exe文件 选择不重启&#xff0c;然后再打开这个.exe 再来一次类似的操作 二、配置 复制patch文件夹中的这三个 &#xff0c;粘贴到掉安装目录中 设置ACT_INC_LICENSE_FILE用户环境变量来设置license管理 打开电脑的环境变量 破解完毕&am…...

51c自动驾驶~合集22

我自己的原文哦~ https://blog.51cto.com/whaosoft/11870502 #自动驾驶数据闭环最前沿论文 近几年&#xff0c;自动驾驶技术的发展日新月异。从ECCV 2020的NeRF问世再到SIGGRAPH 2023的3DGS&#xff0c;三维重建走上了快速发展的道路&#xff01;再到自动驾驶端到端技术的…...

games101 作业5

题目 光线追踪的核心算法: 1.光线的生成 2.光线与三角的相交 题解 1.光线的生成 如课件中的图所示&#xff1a; image plane 就是 代码中的scene的FrameBuffer。 但是&#xff0c;FrameBuffer 是窗口坐标系中&#xff0c;而光线是世界坐标系中的。所以我们需要将scene中的屏…...

【高并发秒杀系统设计:从Guava到Redis的6级缓存架构演进】

一、瞬时十万QPS场景分析 1.1 典型秒杀场景特征 public class SpikeScenario {// 特征1&#xff1a;瞬时流量突增private static final int QPS 100000; // 正常流量100倍// 特征2&#xff1a;资源竞争激烈private int stock 1000; // 100万人抢1000件商品// 特征3&#…...

2-程序语言基础知识

本节内容不是很多,在考试中一般是2-3分,多看教材,考试中大概都是原话 本节主要考点 1、编译程序和解释程序 2、程序语言的数据成分和控制成分(多看教材) 3、编译程序的过程(多看教材) 4、中缀、前缀与后缀表达式(增加) 教材P42-52,54-58多看一下 1、程序语言概述 机器…...

【AIGC系列】4:Stable Diffusion应用实践和代码分析

AIGC系列博文&#xff1a; 【AIGC系列】1&#xff1a;自编码器&#xff08;AutoEncoder, AE&#xff09; 【AIGC系列】2&#xff1a;DALLE 2模型介绍&#xff08;内含扩散模型介绍&#xff09; 【AIGC系列】3&#xff1a;Stable Diffusion模型原理介绍 【AIGC系列】4&#xff1…...

小米火龙CPU和其他几代温度太高的CPU是由谁代工的

小米火龙CPU”并非小米自研芯片&#xff0c;而是指搭载在小米手机上的部分高通骁龙处理器因发热问题被调侃为“火龙”。以下是几款被称为“火龙”的高通CPU及其代工情况&#xff1a; 骁龙810 骁龙810是高通历史上最著名的“火龙”之一&#xff0c;采用台积电20nm工艺代工。由于…...

在 ASP.NET Core 中压缩并减少图像的文件大小

示例代码&#xff1a;https://download.csdn.net/download/hefeng_aspnet/90294127 在当今的数字时代&#xff0c;图像是 Web 应用程序和用户体验不可或缺的一部分。但是&#xff0c;处理大型图像文件可能会导致网页加载缓慢和更高的存储费用。为了解决这个问题&#xff0c;在…...

网络流算法: Dinic算法

图论相关帖子 基本概念图的表示: 邻接矩阵和邻接表图的遍历: 深度优先与广度优先拓扑排序图的最短路径:Dijkstra算法和Bellman-Ford算法最小生成树二分图多源最短路径强连通分量欧拉回路和汉密尔顿回路网络流算法: Edmonds-Karp算法网络流算法: Dinic算法 环境要求 本文所用…...

【Godot4.3】自定义简易菜单栏节点ETDMenuBar

概述 Godot中的菜单创建是一个复杂的灾难性工作&#xff0c;往往无从下手&#xff0c;我也是不止一次尝试简化菜单的创建。 从自己去年的发明“简易树形数据”用于简化Tree控件获得灵感&#xff0c;于是尝试编写了用于表示菜单数据的EasyMenuData类&#xff0c;以及对应的纯文…...

[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?

&#x1f9e0; 智能合约中的数据是如何在区块链中保持一致的&#xff1f; 为什么所有区块链节点都能得出相同结果&#xff1f;合约调用这么复杂&#xff0c;状态真能保持一致吗&#xff1f;本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里&#xf…...

synchronized 学习

学习源&#xff1a; https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖&#xff0c;也要考虑性能问题&#xff08;场景&#xff09; 2.常见面试问题&#xff1a; sync出…...

【JavaEE】-- HTTP

1. HTTP是什么&#xff1f; HTTP&#xff08;全称为"超文本传输协议"&#xff09;是一种应用非常广泛的应用层协议&#xff0c;HTTP是基于TCP协议的一种应用层协议。 应用层协议&#xff1a;是计算机网络协议栈中最高层的协议&#xff0c;它定义了运行在不同主机上…...

电脑插入多块移动硬盘后经常出现卡顿和蓝屏

当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时&#xff0c;可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案&#xff1a; 1. 检查电源供电问题 问题原因&#xff1a;多块移动硬盘同时运行可能导致USB接口供电不足&#x…...

【git】把本地更改提交远程新分支feature_g

创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...

零基础设计模式——行为型模式 - 责任链模式

第四部分&#xff1a;行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习&#xff01;行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想&#xff1a;使多个对象都有机会处…...

全志A40i android7.1 调试信息打印串口由uart0改为uart3

一&#xff0c;概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本&#xff1a;2014.07&#xff1b; Kernel版本&#xff1a;Linux-3.10&#xff1b; 二&#xff0c;Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01)&#xff0c;并让boo…...

七、数据库的完整性

七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配

目录 一、C 内存的基本概念​ 1.1 内存的物理与逻辑结构​ 1.2 C 程序的内存区域划分​ 二、栈内存分配​ 2.1 栈内存的特点​ 2.2 栈内存分配示例​ 三、堆内存分配​ 3.1 new和delete操作符​ 4.2 内存泄漏与悬空指针问题​ 4.3 new和delete的重载​ 四、智能指针…...

掌握 HTTP 请求:理解 cURL GET 语法

cURL 是一个强大的命令行工具&#xff0c;用于发送 HTTP 请求和与 Web 服务器交互。在 Web 开发和测试中&#xff0c;cURL 经常用于发送 GET 请求来获取服务器资源。本文将详细介绍 cURL GET 请求的语法和使用方法。 一、cURL 基本概念 cURL 是 "Client URL" 的缩写…...