通用数据库对象设计
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.为查询结果指定别名…...
AI圈神秘领袖Ilya一幅画引爆全网,OpenAI三件大事暗示AGI时代将至?
AI圈神秘精神领袖Ilya在Instagram上传一幅画引发疯狂解读,与此同时,OpenAI连续公布数学成果、升级Codex、筹备IPO,释放AGI到来的强烈信号。Ilya画作引猜测Ilya上传的画中,罗丹的「思考者」踩在芯片Die Shot上,右下角签…...
从单体到事件驱动的生死跃迁:DeepSeek架构委员会认证的6阶段迁移路线图(含风险热力图与回滚触发阈值表)
更多请点击: https://codechina.net 第一章:从单体到事件驱动的生死跃迁:DeepSeek架构委员会认证的6阶段迁移路线图(含风险热力图与回滚触发阈值表) 向事件驱动架构(EDA)演进不是功能迭代&…...
掌握Umi-OCR:5分钟上手开源免费离线文字识别工具
掌握Umi-OCR:5分钟上手开源免费离线文字识别工具 【免费下载链接】Umi-OCR OCR software, free and offline. 开源、免费的离线OCR软件。支持截屏/批量导入图片,PDF文档识别,排除水印/页眉页脚,扫描/生成二维码。内置多国语言库。…...
如何快速定制Office界面:终极开源工具使用指南
如何快速定制Office界面:终极开源工具使用指南 【免费下载链接】office-ribbonx-editor An overhauled fork of the original Custom UI Editor for Microsoft Office, built with WPF 项目地址: https://gitcode.com/gh_mirrors/of/office-ribbonx-editor O…...
Godot 2D随机地图三大静默故障:黑屏、穿墙、寻路失败的根源与修复
1. 为什么刚上手Godot做2D随机地图就总卡在“生成出来是黑的”“角色穿墙”“房间连不通”这三件事上?如果你是刚从Unity或GameMaker转来Godot,或者第一次用GDScript写程序逻辑的新手,大概率已经在2D随机地图生成这个环节反复摔过跟头——不是…...
OpenCore Legacy Patcher完整指南:如何让老旧Mac重获新生运行最新macOS
OpenCore Legacy Patcher完整指南:如何让老旧Mac重获新生运行最新macOS 【免费下载链接】OpenCore-Legacy-Patcher Experience macOS just like before 项目地址: https://gitcode.com/GitHub_Trending/op/OpenCore-Legacy-Patcher 想让你的老旧Mac设备重获新…...
3步免费解锁Cursor Pro:告别设备限制,永久享受AI编程助手高级功能
3步免费解锁Cursor Pro:告别设备限制,永久享受AI编程助手高级功能 【免费下载链接】cursor-free-vip [Support 0.45](Multi Language 多语言)自动注册 Cursor Ai ,自动重置机器ID , 免费升级使用Pro 功能: …...
如何快速上手SoundMind:10分钟完成音频逻辑推理模型训练
如何快速上手SoundMind:10分钟完成音频逻辑推理模型训练 【免费下载链接】SoundMind We introduce the Audio Logical Reasoning (ALR) dataset, consisting of 6,446 text-audio annotated samples specifically designed for complex reasoning tasks. Building o…...
Meteor-Files深度解析:DDP与HTTP传输协议的终极对比指南
Meteor-Files深度解析:DDP与HTTP传输协议的终极对比指南 【免费下载链接】Meteor-Files 🚀 Upload files via DDP or HTTP to ☄️ Meteor server FS, AWS, GridFS, DropBox or Google Drive. Fast, secure and robust. 项目地址: https://gitcode.com…...
UE4插件开发实战:手把手教你为自定义资源创建独立的3D预览窗口(基于SEditorViewport)
UE4插件开发实战:打造自定义资源的3D预览视口在虚幻引擎4的编辑器扩展开发中,为自定义资源提供直观的3D预览功能是提升工具链效率的关键环节。想象一下,当技术美术师调整一把自定义武器的参数时,能够实时看到模型变化,…...
