【ArchSummit架构师峰会】探讨数据与人工智能相互驱动的关系>>> 了解详情
写点什么

怎样实现良好的数据库设计?

  • 2020-10-10
  • 本文字数:3069 字

    阅读完需:约 10 分钟

怎样实现良好的数据库设计?

本文最初发布于 relinx.io 博客,经原作者授权由 InfoQ 中文站翻译并分享。

为什么要关注数据库设计?

无论是应用程序,还是数据库如何变化,数据始终是最重要的部分。通常,数据是系统存在的首要目的。这就是为什么,我们不应该只把数据库系统看作是保存数据的黑盒子,而要将其看成验证和防止数据腐化的工具。


要做到这一点,就要有健壮和深思熟虑的数据库设计。当然,业务逻辑是在应用层编码,它确保数据在到达数据库之前的格式是正确的。


但是,谁能保证网络故障或缺陷不会放行不可靠的“客人”?此外,应用层并不是通往数据库的唯一的“门”。我们可以使用导入脚本、维护脚本,DBA 和开发人员也会与之交互。我们可以在底层采取预防措施确保在数据存储前总是进行检查。


拥有健壮、可靠的数据也有助于开发和测试。将一个列设置为 Not Null 可以省掉许多假设该列为空的测试场景,还能简化代码,让开发人员不用(几乎)每次访问它之前都检查值。


在强调了良好的数据库设计的重要性后,让我们看看可以使用哪些工具来实现它。

规范化


这无疑是良好设计的首要原则。这里,我们不打算深入研究规范化规则,只是想强调它的重要性。


关于这个话题,这里有份不错的资料,你可以进一步阅读。

数据类型

另一件要注意的事情是定义适当的属性类型。这不仅可以提高数据库的性能,还能在存储数据前验证数据。所以,我们应该在“integer”、“numeric”字段中保存数值数据;在“timestamp”、“timestamptz”字段中保存时间戳;在“bit”、“char(1)”或“boolean”字段中保存布尔值等等。


日期值得特别注意。如果 Date 属性假设只有日期部分(OrderDate,ReleaseDate),请使用没有时间部分的 Date 类型。如果你只需要保留时间(StartTime,EndTime),就使用合适的时间类型。


如果不需要指定精度,则将其指定为零(“time(0)”)。对带有时间部分的日期,有一个问题是,你必须总是截断时间部分,只显示日期,并且当你要在与数据库所在时区不同的地方显示时,要确保格式化后不会显示成昨天或明天。当跳转到夏令时的时候,带有时间部分的日期时间加减也可能出现问题。

约束

约束是本文讨论的重点。它们将无效数据排除在外,并确保数据的健壮性。让我们一个一个来看。

非空约束

如果业务规则要求该属性应该始终存在,那么要毫不犹豫地将其设置为 Not Null。适合设置为 Not Null 的字段有 Id、Name、AddedDate、IsActive、State、CategoryId(如果所有项都应该有一个类别)、ItemCount、Price 以及许多其他字段。通常,这些属性在业务逻辑中扮演重要角色。其他可选的信息字段可能还是可以设置为 Null。


但是要注意,不要对可以为空的属性使用 Not Null 约束。例如,一个长时间运行的任务总有一个 StartTimestamp(Not Null),但是只有在任务完成时才更新 EndTimestamp(Null)。


另一个典型的例子是,Employee 表的 ManagerId,并不是所有员工都有经理。不要试图让 ManagerId 不为空,并为没有经理的员工插入“0”或“-1”。当我们添加外键约束时,这将导致其他问题。

唯一约束

同样,根据业务规则,一些属性(或属性的组合)应该是惟一的,比如 Id、PinNumber、BookId 和 AuthorId、OrderNo 等。应该通过添加惟一约束来保证这些属性的惟一。


还有一点要注意:可以使用唯一索引来实现同样的效果,但是添加约束是更好的方法。因为当添加惟一约束时,会自动创建非惟一索引。


因此,如果出于某种原因,你必须临时禁用/启用约束,将会非常容易。在使用唯一索引的情况下,你必须删除/重新创建索引,从性能和时间方面来说,这是一个昂贵的操作。

主键

Not Null 和唯一约束一起构成主键。当我们想到主键时,会很快想到 Id 或 ObjectId 之类的列。但是主键也可以是复合的,比如 BookId 和 AuthorId。


这里有个难题是,是使用单独的 Id 列作为主键,还是将两者的组合作为主键?通常,使用单独的 Id 列是一种更好的方法,因为它可以使连接更加清晰,还能方便地将另一列添加到惟一组合中。但是,即使有了一个单独的主键(Id),我们还是要为 BookId 和 AuthorId 列添加唯一约束。

Check 约束

Check 约束允许我们定义数据的有效值/范围。适合 Check 约束的属性有百分比(0 到 100 之间)、状态(0、1、2)、价格、金额、总数(大于或等于 0)、PinNumber(固定长度)等。


同样,不要尝试将业务逻辑编码到 Check 约束中。我记得有一次,在 AccountBalance 列中添加了一个“大于或等于零”的 Check 约束,从而避免了意外透支。

默认约束

默认约束也很重要。它们允许我们向现有表中添加新的 Not Null 列,并使“旧”API 与新结构兼容,直到所有各方都完成升级(尽管在完全升级后,默认约束应该删除)。


这里要记住一点。不要在默认约束中编写业务逻辑。例如,函数“now()”可能很适合(尽管不总是)作为日志表中的时间戳字段的默认值,但不适合 Orders 表的 OrderDate 字段。你可能会倾向于在插入语句中省略 OrderDate,而依赖于默认约束,但这意味着将业务逻辑扩展到数据库层。


此外,在某些情况下,业务可能只在订单批准后才给 OrderDate 赋值,因为默认约束深埋在数据库中,所以,当我们对应用层的代码进行更改时,它不会那么明显。

外键约束

外键约束是关系数据库设计之王。外键与主键一起确保表之间的数据一致性。规范化规则告诉我们何时将数据提取到表中并使用外键引用它。这里我们将关注细节差别,比如 OnDelete 和 OnUpdate 规则。



DBeaver 中的外键约束编辑器


让我们从简单的部分开始:OnUpdate。外键引用主键,它们很少(如果有的话)被修改。因此,OnUpdate 规则不是很常用,但将其设置为 Cascade 还是有意义的,因为我们有时可能必须更新某些行的主键(通常在迁移后)。这样,数据库将允许我们进行更新,并将新的 id 传播到子表中。


OnDelete 规则有点复杂。根据数据库的不同,我们有 NoAction、Restrict、SetNull、SetDefault 和 Cascade 选项。那么,选择哪一个呢?


通常,对于键引用查找或不引用实体的实体,我们选择 NoAction。例如,Products -> Categories、Books -> Authors 等。在大多数情况下,Restrict 与 NoAction 是相同的,但是对于某些数据库,它们有细微的区别


另一方面,当子记录不能在没有父记录的情况下存在时,选择 Cascade。在 Book 和 Author 示例中,当删除一本书时,我们也应该从 BookAuthor 表中删除记录。其他例子有 OrderDetails -> Orders、PostComments -> Posts 等。这里,有些人可能会不同意,数据库不应该自动删除子行,它们应该由应用层删除。根据业务逻辑,是这样的。但有时“不重要的”子项删除可以委托给数据库。


SetNull 很少使用。例如,Employee.ManagerId 和 Employee.Id 之间的外键可以是 SetNull。当一名经理被撤职,他的下属就没经理了。显然,只有当列可为空时才能选择该项规则。


在这些规则中,SetDefault 最罕见。当父记录被删除时,它将列设置为其默认值。因为外键引用主键,我们很难想象一个有外键的字段将默认值硬编码。但无论如何,这个选项是存在的,我们还是有可能需要它。

索引

索引是良好数据库设计的重要组成部分,但有点偏离我们的讨论,因为它们几乎不能保护我们的数据(惟一索引除外)。需要注意的一点是:一些 RDBMS 系统(例如 Oracle)会在创建外键时自动创建索引,而无需我们操心。其他数据库(例如 MS SQL Server)不会这样做,我们必须自己添加索引。

小结

一个深思熟虑的设计可以为我们节省大量的编码、测试和故障排除时间。在设计良好的数据库上编写查询和报表令人愉快。将数据发布并迁移到新系统也会非常容易。


编码快乐!


原文链接:


https://relinx.io/2020/09/14/old-good-database-design/


2020-10-10 16:416466
用户头像

发布了 687 篇内容, 共 397.2 次阅读, 收获喜欢 1498 次。

关注

评论 1 条评论

发布
用户头像
外键约束一般都强制不适用。。。
2020-10-15 17:58
回复
没有更多了
发现更多内容

干货分享|可证明安全的隐私计算

隐语SecretFlow

大数据 AI 安全 隐私保护 隐私计算

区块链上地址与银行账户有什么区别?

BSN研习社

华为云数智新消费创新峰会2023,我们在这里!

云智慧AIOps社区

人工智能 运维 大模型 GPT

如何维护大型 Next.js 应用程序

汽车之家客户端前端团队

next

站在源码的角度看多人语音厅房间系统

山东布谷网络科技

直播源码

波卡链质押挖矿模式系统技术开发合约源代码示例

V\TG【ch3nguang】

区块链技术应用开发 质押挖矿 奖励分红

OpenHarmony 分布式硬件关键技术

OpenHarmony开发者

OpenHarmony

开箱即用!教你如何正确使用华为云CodeArts PerfTest!

华为云PaaS服务小智

云计算 软件开发 性能测试 华为云

万字详解云计算中的云网络技术

华为云开发者联盟

云计算 后端 华为云 华为云开发者联盟 企业号 8 月 PK 榜

智慧仓储 | 你所忽略的系统安全性问题,可能很 “致命”……

网安云

系统安全 智慧仓储 中小企业

Kurator,你的分布式云原生解决方案

华为云开发者联盟

云原生 后端 华为云 华为云开发者联盟 企业号 8 月 PK 榜

超越界限:大模型应用领域扩展,探索文本分类、文本匹配、信息抽取和性格测试等多领域应用

汀丶人工智能

信息抽取 自然语言模型 大语言模型 ChatGLM-6B 文本匹配

如何优雅的给柯里化函数添加类型标注

汽车之家客户端前端团队

函数柯里化

SUI 将通过 SUI Foundation 资助 Footprint Analytics 解析其公链数据,为生态系统提供支持

Footprint Analytics

区块链 公链

隐私计算技术|私有信息检索(PIR)及其应用场景

隐语SecretFlow

大数据 AI 隐私计算 开源社区 私有信息检索

大型集团企业数据治理方案,以“应用驱动”的数据治理策略 | 行业方案

袋鼠云数栈

大数据 数字化转型

揭秘YouTube 的环境模式发光效果

汽车之家客户端前端团队

CSS youtube

GPU荒漠,算力短缺背后的时间竞赛与解决方案|TE解读

TE智库

人工智能 AI gpu 算力

人工智能与软件开发的未来

这我可不懂

人工智能 软件开发

支付宝小程序云效能:四大基于小程序生态的解决方案

TRaaS

企业网络安全守护神-行云管家堡垒机!

行云管家

运维 网络安全 数字化 堡垒机

亚信科技AntDB数据库通过GB 18030-2022最高实现级别认证,荣膺首批通过该认证的产品之列

亚信AntDB数据库

数据库 AntDB AntDB数据库 企业号 8 月 PK 榜

让大数据平台数据安全可见-行云管家

行云管家

大数据 数字化 数据安全 大数据平台

合约交易所系统软件开发详情(源码搭建示例)

V\TG【ch3nguang】

交易所开发 交易所搭建

NFT铸造平台模式系统开发详情介绍[源码搭建]

V\TG【ch3nguang】

NFT 数字藏品开发

怎样实现良好的数据库设计?_文化 & 方法_Elnur_InfoQ精选文章