通用数据库对象设计
1. 公共属性
这里的数据模型以陈品山的实体-关系模型为基础,增加了两点修改。一是用“组”的概念表达实体间关系,并将组作为一种特殊实体。二是采用继承的思想,将实体的公共属性提取出来,放到统一表中。实体的特有属性保存在单独的表中。根据这两点,我们建立一个实体表,记录全部实体的公共属性。为了跟踪实体的变化,需要为实体分配唯一标识。为了支持分布式应用,索引不能使用数据库自增字段。而唯一标识是OLTP业务检索时最常用的索引,需要支持高性能查询。我们建议采用类似雪花算法的机制,由应用生成128位整数作为实体标识,再分成两个64位整数保存在数据库中。实体的特有属性保存在单独的表中。我们必须记录特有属性表名,让应用可以找到实体特有属性。考虑到数据表可能需要同步,需要记录数据的创建时间和修改时间。这样我们得到了实体表的第一个版本。
列 | 标识 | 数据类型 | 说明 |
---|---|---|---|
实体标识高64位 | id_high | 整数 | |
实体标识低64位 | id_low | 整数 | |
特有属性表 | attribute_table | 字符串 | 实体特有属性表 |
创建时间 | create_time | 日期和时间 | 新增记录时间 |
修改时间 | modify_time | 日期和时间 | 修改记录时间 |
要查询属性对象时,可以分别从实体表和特定属性表中查询数据,拼接到一起。这个操作可以在数据库中完成,也可以在应用中完成。
代码1 在数据库中查询实体属性
CREATE TABLE entity (id_high BIGINT NOT NULL,id_low BIGINT NOT NULL,attribute_table VARCHAR(200) NULL,create_time DATETIME NOT NULL,modify_time DATETIME NOT NULL,PRIMARY KEY (id_high,id_low) );CREATE TABLE user (id_high BIGINT NOT NULL,id_low BIGINT NOT NULL,name VARCHAR(200) NOT NULL,PRIMARY KEY (id_high,id_low) );INSERT INTO entity (id_high,id_low,attribute_table,create_time,modify_time) VALUES (0, 1, 'user', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);INSERT INTO user (id_high,id_low,name) VALUES(0, 1, 'root');-- 查询 DELIMITER // CREATE PROCEDURE query_entity(IN id_high BIGINT, IN id_low BIGINT) BEGINSET @table_name = '';SELECT attribute_table INTO @table_name FROM entity WHERE id_high=0 AND id_low=1;SET @query = CONCAT('SELECT * FROM entity a LEFT JOIN ', @table_name, ' b ON a.id_high=b.id_high AND a.id_low=b.id_low');PREPARE stmt FROM @query;EXECUTE stmt;DEALLOCATE PREPARE stmt; END // DELIMITER ;CALL query_entity(0, 1);
为了节省存储空间,可以把属性表名从实体表中分拆出来,建立一个元数据表。
列 | 标识 | 数据类型 | 说明 |
---|---|---|---|
实体类型标识 | id | 整数 | 唯一标识实体类型 |
实体类型名字 | name | 字符串 | |
特殊属性表 | attribute_table | 字符串 | |
新增时间 | create_time | 日期和时间 | 新增记录时间 |
更新时间 | modify_time | 日期和时间 | 修改记录时间 |
列 | 标识 | 数据类型 | 说明 |
---|---|---|---|
实体标识高64位 | id_high | 整数 | |
实体标识低64位 | id_low | 整数 | |
实体类型 | entity_type_id | 整数 | 实体类型 |
创建时间 | create_time | 日期和时间 | 新增记录时间 |
修改时间 | modify_time | 日期和时间 | 修改记录时间 |
代码2 查询实体属性
CREATE TABLE entity_type (id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT "实体类型数字标识",name VARCHAR(20) NULL COMMENT "实体属性名字",attribute_table VARCHAR(200) NULL COMMENT "实体特有属性表",create_time DATETIME NOT NULL COMMENT "创建时间",modify_time DATETIME NOT NULL COMMENT "修改时间" );CREATE TABLE entity (id_high BIGINT NOT NULL,id_low BIGINT NOT NULL,entity_type_id BIGINT NULL,create_time DATETIME NOT NULL,modify_time DATETIME NOT NULL,PRIMARY KEY (id_high,id_low) );CREATE TABLE user (id_high BIGINT NOT NULL,id_low BIGINT NOT NULL,name VARCHAR(200) NOT NULL,PRIMARY KEY (id_high,id_low) );INSERT INTO entity_type (id,name,attribute_table,create_time,modify_time) VALUES (1, '用户', 'user', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);INSERT INTO entity (id_high,id_low,entity_type_id,create_time,modify_time) VALUES (0, 1, 1, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);INSERT INTO user (id_high,id_low,name) VALUES(0, 1, 'root');-- 查询 DELIMITER // CREATE PROCEDURE query_entity(IN id_high BIGINT, IN id_low BIGINT) BEGINSET @table_name = '';SELECT attribute_table INTO @table_nameFROM entityLEFT JOIN entity_type ON entity.entity_type_id=entity_type.idWHERE id_high=0 AND id_low=1;SET @query = CONCAT('SELECT * FROM entity a LEFT JOIN ', @table_name, ' b ON a.id_high=b.id_high AND a.id_low=b.id_low');PREPARE stmt FROM @query;EXECUTE stmt;DEALLOCATE PREPARE stmt; END // DELIMITER ;CALL query_entity(0, 1);
上面的例子重复查询了两次实体表,性能上不是最优的。如果实体类型表是“只追加”的,即记录不会修改,可以将实体类型表缓存在应用内存中,由应用生成查询语句,提高查询效率。
2. 多元关系
关系模型可以提供良好的数据独立性,但基于集合的运算难以在大部分业务中直接使用。应用在进行实际计算时,往往需要将数据组织成树或网络结构。这两种结构在微观层面存在“一对多”、“多对一”、“多对多”三种节点关系。在树中,一个父节点可以拥有多个子节点。在网络中,每个节点可以拥有多个父节点,也可以拥有多个子节点。考虑用关系模型保存一颗树。通常的方法是记录父节点和子节点的关系:
父子节点关系 = (父节点编号,子节点编号)
为了处理“从根节点查询全部子节点”问题,可以将根节点编号加入关系。
父子节点关系 = (根节点编号,子节点编号,父节点编号)
这样可以快速查询出一颗树的所有节点,在应用程序中重组成树。这种方法也隐含的产生了一个“组”,即直接或间接依赖于同一根节点的全部节点。我们可以扩展这个概念,来表达一下三种实体之间的依赖关系:
- 集合。各实体间没有依赖关系。
- 列表。各实体间存在线性依赖关系。
- 树。各实体间存在树形依赖结构。
各个实体还可能构成网络依赖结构。这种结构难以用关系模型高效表达,因此本文不考虑这种结构。为了支持组之间的依赖关系,我们把组当作一种特殊实体。
兄弟实体之间也可能存在排序。我们增加一个字段来支持这种同一层级内的序结构。
列 | 标识 | 数据类型 | 说明 |
---|---|---|---|
组标识高64位 | id_high | 整数 | |
组标识低64位 | id_low | 整数 | |
父实体标识高64位 | id_high | 整数 | |
父实体标识低64位 | id_low | 整数 | |
子实体标识高64位 | id_high | 整数 | |
子实体标识低64位 | id_low | 整数 | |
子实体序号 | child_order | 整型 | 序号 |
创建时间 | create_time | 日期和时间 | 新增记录时间 |
修改时间 | modify_time | 日期和时间 | 修改记录时间 |
3. 归档和生命周期
随着记录数不断增加,数据库会出现严重的性能问题。因此需要对不常用的记录进行归档。有些对象需要在未来的特定时间生效或失效,为支持这类对象,需要记录对象的生效标志和生效、失效时间。
列 | 标识 | 数据类型 | 说明 |
---|---|---|---|
实体标识高64位 | id_high | 整数 | |
实体标识低64位 | id_low | 整数 | |
对象类型 | object_type | 枚举(实体、组) | 对象类型:实体或组 |
实体类型 | entity_type_id | 整数 | 实体类型 |
启用标记 | enable_flag | 整数 | |
启用时间 | enable_time | 日期和时间 | |
停用时间 | disable_time | 日期和时间 | |
归档标记 | archive_flag | 整数 | |
归档时间 | archive_time | 日期和时间 | |
创建时间 | create_time | 日期和时间 | 新增记录时间 |
修改时间 | modify_time | 日期和时间 | 修改记录时间 |
4. 性能
现在考虑这些表的性能表现。对象表和组关系表是核心,它们的记录体积不超过72字节,主键占16字节。
列 | 标识 | 数据类型 | 字节数 |
---|---|---|---|
实体标识高64位 | id_high | 整数 | 8 |
实体标识低64位 | id_low | 整数 | 8 |
对象类型 | object_type | 枚举(实体、组) | 4 |
实体类型 | entity_type_id | 整数 | 4 |
启用标记 | enable_flag | 整数 | 4 |
启用时间 | enable_time | 日期和时间 | 8 |
停用时间 | disable_time | 日期和时间 | 8 |
归档标记 | archive_flag | 整数 | 4 |
归档时间 | archive_time | 日期和时间 | 8 |
创建时间 | create_time | 日期和时间 | 8 |
修改时间 | modify_time | 日期和时间 | 8 |
总计 | 72 |
列 | 标识 | 数据类型 | 字节数 |
---|---|---|---|
组标识高64位 | id_high | 整数 | 8 |
组标识低64位 | id_low | 整数 | 8 |
父实体标识高64位 | id_high | 整数 | 8 |
父实体标识低64位 | id_low | 整数 | 8 |
子实体标识高64位 | id_high | 整数 | 8 |
子实体标识低64位 | id_low | 整数 | 8 |
子实体序号 | child_order | 整型 | 4 |
创建时间 | create_time | 日期和时间 | 8 |
修改时间 | modify_time | 日期和时间 | 8 |
总计 | 68 |
innodb_page_size | 16KB | 64KB | |
---|---|---|---|
每页记录数 | 179 | 725 | |
每页索引数 | 647 | 2613 | |
高度为3的B-树 | 最大行数 | 574万 | 4亿 |
最大数据体积 | 394MB | 26GB | |
高度为4的B-树 | 最大行数 | 10亿 | 2763亿 |
最大数据体积 | 69GB | 18TB |
5. 系统架构
实体类型表数据量较小,更新频率不高,可以采用读写分离架构。实体表数据量大,更新频繁,可以根据时间、地理位置或其他方式分拆到多个库中。实体特定属性表可以分布到不同的数据库,分散查询压力。分布信息可以保存在实体类型表中。在关联查询时,应用根据分布信息,同时向多个库查询不同实体的属性,在应用内实现表连接。
6. 附录
代码3 计算B-树信息的代码
(defun record-per-innodb-page (innodb-page-size record-size)"计算一个innodb页面可以保存的记录数 innodb-page-size innodb页面大小,单位字节 record-size 记录大小,单位字节"(let ((record-header-size 5)(transaction-id-size 6)(roll-pointer-size 7)(page-header-size 200))(/ (- innodb-page-size page-header-size)(+ record-size record-header-size transaction-id-size roll-pointer-size))))(defun key-per-innodb-page (innodb-page-size key-size)"计算一个innodb页面可以保存的键数 innodb-page-size innodb页面大小,单位字节 key-size 键大小,单位字节"(let ((record-header-size 5)(child-page-number-size 4)(page-header-size 200))(/ (- innodb-page-size page-header-size)(+ key-size record-header-size child-page-number-size))))(defun estimate-btree-info (innodb-page-size record-size key-size tree-height)"估算innodb b-树信息 innodb-page-size innodb页面大小,单位字节 record-size 记录大小,单位字节 key-size 记录大小,单位字节 tree-height b-树高度"(let* ((child-page-number-size 4)(page-header-size 200)(record-header-size 5)(roll-pointer-size 7)(transaction-id-size 6)(record-per-page (record-per-innodb-page innodb-page-size record-size))(key-per-page (key-per-innodb-page innodb-page-size key-size))(non-leaf-pages 0)(leaf-pages 0)(rows 0)(total-record-size 0)(total-key-size 0))(dotimes (h (- tree-height 1))(setf non-leaf-pages (+ non-leaf-pages (expt key-per-page h))))(setf leaf-pages (expt record-per-page (- tree-height 1)))(setf rows (* leaf-pages record-per-page))(setf total-record-size (* rows record-size))(setf total-key-size (* (+ leaf-pages non-leaf-pages) key-size))(list :non-leaf-pages non-leaf-pages:leaf-pages leaf-pages:rows rows:total-record-size total-record-size:total-key-size total-key-size) ))
7. 参考资料
- 回到原点再出发
- https://zhuanlan.zhihu.com/p/717728750
相关文章:
通用数据库对象设计
1. 公共属性 这里的数据模型以陈品山的实体-关系模型为基础,增加了两点修改。一是用“组”的概念表达实体间关系,并将组作为一种特殊实体。二是采用继承的思想,将实体的公共属性提取出来,放到统一表中。实体的特有属性保存在单独…...

Java基础12-特殊文件和日志技术
十二、特殊文件和日志技术 1、特殊文件 properties:用来存储键值对数据。 xml:用来存储有关系的数据。 1.1 properties文件 特点:存储键值对,键不能重复,文件后缀一般是.properties结尾的。 properties:是…...

2.4 STM32启动过程
目录 一,启动Flow 1.1 初始化MSP 1.2 初始化PC 1.3 设置堆栈大小 1.4初始化中断向量表 1.5 调用初始化函数(可选) 1.6 调用__main 二,Reset_Handler函数 一,启动Flow 下面是stm32在内部FLASH启动的启动建议流程图,在stm32复位到执行我们程序的main函数的过程中,…...

rm: cannot remove: Device or resource busy 解决方案
大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…...

2024年的5款AI写作工具,你用过几个?
AI技术正逐渐渗透到我们生活的方方面面,其中就包括写作领域。今天,我要为大家介绍5款实用的AI写作助手,它们将帮助你提高写作效率,激发创作灵感,让你的写作之路更加顺畅。 1. 宙语Cosmos-全能写作助手 网址ÿ…...

泛癌热门靶点TROP2及研究工具试剂
前 言 TROP2属于肿瘤相关抗原之一,在多种肿瘤中表达升高,促进肿瘤细胞生长、增殖和转移。TROP2已经成为近年来NEJM、肿瘤学期刊、药物研发的多重热点。通过PubMed检索到477篇相关文献,自2020年文献数量逐步增加,2022年达81篇&am…...
2848. 与车相交的点
2848. 与车相交的点 题目链接:2848. 与车相交的点 代码如下: class Solution { public:int numberOfPoints(vector<vector<int>>& nums){set<int> s;for (int i 0; i < nums.size(); i){for (int j nums[i][0]; j < nu…...
第1节 入门
文章目录 1. URL 组成部分详解2. 第一个 Django 项目2.1 项目结构2.2 project 和 app 的关系 3. URL 与视图函数的映射4. URL 的两种传参方式4.1 通过字符串查询4.2 在 path 中携带 5. path 函数详解6. 路由模块化7. 路由反转 1. URL 组成部分详解 URL 的基本组成格式 scheme…...

四数之和(medium)08
其实这道题就是求2数之和,和3数之和的衍生吧,核心算法还是双指针; 暴力解法就不再说了:排序暴力set去重; 直接上:排序双指针去重 大致思路如上图,如果要详细算法过程,可以就看看两数…...
TypeScript中 interface接口 type关键字 enum枚举类型
type interface总是傻傻分不清~~~ Type Aliases (type) type 关键字用于为类型定义一个别名。这可以是基本类型、联合类型、元组、数组、函数等。type 定义的类型在编译后的 JavaScript 代码中会被移除,不会留下任何运行时的代码。 //联合类型 type StringOrNumbe…...
vue3.2实现AES加密解密,秘钥通过API获取,并混淆秘钥,后端thinkphp
aes.ts文件 import CryptoJS from "crypto-js"; import axios from "axios";export const encrypt async(data: any) > {let storeKey sessionStorage.getItem(a)let storeIv:any sessionStorage.getItem(i)// 如果秘钥或 IV 不存在,尝试…...

简述微服务高可用之Sentinel、Seate
简述微服务高可用之Sentinel、Seate使用 下文主要讲述使用sentinel,如何降级限流熔断及如何使用seata管理分布式事务 sentinel服务端安装与使用 1、下载 进入https://github.com/alibaba/Sentinel/releases 根据你的需求进行下载对应版本 我这里是JDK17 下载的1.8.8版本&am…...

将爱传递 将“服务好”延伸
从泰康客户,转身成为泰康人,她直言是因为亲身感受了泰康“服务好”的魅力。 入司已8年的泰康养老浙江分公司HWP何英英,是泰康“服务好”的受益者。她从朋友的理赔中见证了泰康服务好的温度与力量,又被泰康养老的职域模式所吸引选择加入泰康。如今,她全身心投入在服务的第一线,…...
基于MinIO配置bucket,用于文件下载和浏览
文章目录 引言I 配置文件浏览安装MinIO配置自启动服务访问权限配置文件浏览访问地址文件下载地址II 知识扩展MinIO内置访问策略只读策略只写策略读写策略diagnosticsconsoleAdmin引言 需求:文件下载用于OTA升级,文件浏览用于产品展示。 实现方案:基于MinIO配置bucket访问权…...
Ubuntu 配置 ssh 免密连接、安装Docker、docker-compose
Ubuntu 配置 ssh 免密连接 本机 #生成密钥 ssh-keygen -t rsa -b 4096 #发送公钥到服务器 ssh-copy-id usernameremote_host #测试 ssh usernameremote_host全部回车,密钥存储路径默认是 ~/.ssh/id_rsa 服务器 确认服务器公钥配置允许远程公钥认证 sudo vim /et…...

C++ -string -常见用法2
博客主页:【夜泉_ly】 本文专栏:【C】 欢迎点赞👍收藏⭐关注❤️ 文章目录 💡前言💡访问及遍历1.速览2.operator[] -重点⭐2.1函数原型2.2用法2.3与at 3.迭代器3.1函数原型3.2用法速览3.3简介 4.范围for4.1用法速览4.2…...

为您的 WordPress 网站打造完美广告布局 A5广告单元格插件
一个为 WordPress 网站量身定制的强大工具,它将彻底改变您展示广告的方式 灵活多变的布局设计 A5 广告单元格插件的核心优势在于其无与伦比的灵活性。无论您是想要创建整齐的网格布局,还是希望打造独特的不规则设计,这款插件都能满足您的需求。 自定义网格数量:从 2…...

前端的AI工具:ChatGPT Canvas与Claude Artifacts对比 -仅仅是OpenAI一个迟来的追赶吗?- 贺星舰五飞试验成功
如果你对OpenAI的ChatGPT Canvas和Anthropic的Claude Artifacts有所耳闻,可能会想知道这两个工具有何不同,以及哪个能让你的工作流程更加顺畅。这两个工具旨在提升生产力,但侧重点各异——编码、写作、创意和实时反馈。 本文将深入探讨ChatG…...
GNU链接器(LD):输出分区类型(NOLOAD、READONLY、DSECT、COPY、INFO、OVERLAY)介绍
0 参考资料 GNU-LD-v2.30-中文手册.pdf GNU linker.pdf1 前言 一个完整的编译工具链应该包含以下4个部分: (1)编译器 (2)汇编器 (3)链接器 (4)lib库 在GNU工具链中&…...

【MySQL】表的查询操作——SELECT
目录 1.select的基本使用 1.1.查询所有列 1.2.查询特定列 1.3.DISTINCT关键字的使用——查询不重复的年级 1.4.带条件的查询 1.5.排序查询结果 1.6.LIMIT的使用——限制返回的行数 1.7.组合使用 WHERE、ORDER BY 和 LIMIT 1.8.查询字段为表达式 1.9.为查询结果指定别名…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...

el-switch文字内置
el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...

安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖
在Vuzix M400 AR智能眼镜的助力下,卢森堡罗伯特舒曼医院(the Robert Schuman Hospitals, HRS)凭借在无菌制剂生产流程中引入增强现实技术(AR)创新项目,荣获了2024年6月7日由卢森堡医院药剂师协会࿰…...

RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill
视觉语言模型(Vision-Language Models, VLMs),为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展,机器人仍难以胜任复杂的长时程任务(如家具装配),主要受限于人…...

群晖NAS如何在虚拟机创建飞牛NAS
套件中心下载安装Virtual Machine Manager 创建虚拟机 配置虚拟机 飞牛官网下载 https://iso.liveupdate.fnnas.com/x86_64/trim/fnos-0.9.2-863.iso 群晖NAS如何在虚拟机创建飞牛NAS - 个人信息分享...
深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏
一、引言 在深度学习中,我们训练出的神经网络往往非常庞大(比如像 ResNet、YOLOv8、Vision Transformer),虽然精度很高,但“太重”了,运行起来很慢,占用内存大,不适合部署到手机、摄…...

【Veristand】Veristand环境安装教程-Linux RT / Windows
首先声明,此教程是针对Simulink编译模型并导入Veristand中编写的,同时需要注意的是老用户编译可能用的是Veristand Model Framework,那个是历史版本,且NI不会再维护,新版本编译支持为VeriStand Model Generation Suppo…...

Linux-进程间的通信
1、IPC: Inter Process Communication(进程间通信): 由于每个进程在操作系统中有独立的地址空间,它们不能像线程那样直接访问彼此的内存,所以必须通过某种方式进行通信。 常见的 IPC 方式包括&#…...
41道Django高频题整理(附答案背诵版)
解释一下 Django 和 Tornado 的关系? Django和Tornado都是Python的web框架,但它们的设计哲学和应用场景有所不同。 Django是一个高级的Python Web框架,鼓励快速开发和干净、实用的设计。它遵循MVC设计,并强调代码复用。Django有…...