前语
最近有不少前端和测验转Go的朋友在私信我:怎样做好表结构规划?
我们关心的问题阳哥有必要整理出来,期望对我们有帮助。
先说定论
这篇文章介绍了规划数据库表结构应该考虑的4个方面,还有高雅规划的6个准则,举了一个比方共享了我的规划思路,为了进步功能咱们也要从多方面考虑缓存问题。
收成最大的仍是和我们的沟通评论,总结一下:
- 首要,必定要先搞清楚事务需求。比方我的比方中,假如不需求灵敏设置,彻底能够写到装备文件中,并不需求独自规划外键。主表中直接保存各种挑选标签称号(留意保护的问题,要考虑到数据共同性)
- 数据库表结构规划必定考虑数据量和并发量,我的比方中假如数据量小,能够适作为冗余规划,下降事务复杂度。
4个方面
规划数据库表结构需求考虑到以下4个方面:
-
数据库范式:一般情况下,咱们期望表的数据契合某种范式,这能够确保数据的完整性和共同性。例如,第一范式要求表的每个特点都是原子性的,第二范式要求每个非主键特点彻底依赖于主键,第三范式要求每个非主键特点不依赖于其他非主键特点。
-
实体联系模型(ER模型):咱们需求先依据实践情况画出实体联系模型,然后再将其转化为数据库表结构。实体联系模型一般包含实体、特点、联系等要素,咱们需求将它们转化为表的形式。
-
数据库功能:咱们需求考虑到数据库的功能问题,包含表的巨细、索引的运用、查询语句的优化等。
-
数据库安全:咱们需求考虑到数据库的安全问题,包含表的权限、用户人物的设置等。
规划准则
在规划数据库表结构时,能够参考以下几个高雅的规划准则:
-
简单明了:表结构应该简单明了,防止过度复杂化。
-
共同性:表结构应该坚持共同性,例如命名规范、数据类型等。
-
规范化:尽可能将表规范化,防止数据冗余和不共同性。
-
功能:表结构应该考虑到功能问题,例如运用恰当的索引、防止全表扫描等。
-
安全:表结构应该考虑到安全问题,例如合理设置权限、防止SQL注入等。
-
扩展性:表结构应该具有必定的扩展性,例如预留字段、可扩展的联系等。
最后,需求提示的是,高雅的数据库表结构需求在实践中不断迭代和优化,不断满足实践需求和新的应战。
下面举个示例让我们更好的了解怎样规划表结构,怎样引入内存,有哪些优化思路:
问题描绘
如上图所示,红框中的视频挑选标签,应该怎样规划数据库表结构?除了前台挑选,还想支撑在办理后台灵敏装备这些挑选标签。
这是一个很好的运用场景,我们能够先自己想一下。不要着急看我的方案。
需求剖析
- 能够依据红框的标签挑选视频
- 其中归纳标签比较特别,和类型、区域、年份、艺人等不一样
- 归纳是依据事务逻辑取值,并不需求入库
- 类型、区域、年份、艺人等需求入库
- 规划表结构时要考虑到:
- 便利获取标签信息,便利把标签信息缓存处理
- 便利依据标签挑选视频,便利咱们写后续的事务逻辑
规划思路
- 归纳标签能够写到装备文件中(或许写在前端),这些信息不需求灵敏装备,所以不需求保存到数据库中
- 类型、区域、年份、艺人都规划独自的表
- 视频表中规划标签表的外键,便利视频列表挑选取值
- 标签信息写入缓存,进步接口响应速度
- 类型、区域、年份、艺人表也要支撑对数据排序,便利后期办理保护
表结构规划
视频表
字段 | 注释 |
---|---|
id | 视频主键id |
type_id | 类型表外键id |
area_id | 区域表外键id |
year_id | 年份外键id |
actor_id | 艺人外键id |
其他和视频直接相关的字段(比方称号)我就省略不写了
类型表
字段 | 注释 |
---|---|
id | 类型主键id |
name | 类型称号 |
sort | 排序字段 |
区域表
字段 | 注释 |
---|---|
id | 类型主键id |
name | 类型称号 |
sort | 排序字段 |
年份表
字段 | 注释 |
---|---|
id | 类型主键id |
name | 类型称号 |
sort | 排序字段 |
原以为年份字段不需求排序,要么是年份正序摆放,要么是年份倒序摆放,所以不需求sort字段。
仔细看了看需求,还有“10年代”仍是需求灵敏装备的呀~
艺人表
字段 | 注释 |
---|---|
id | 类型主键id |
name | 类型称号 |
sort | 排序字段 |
表结构规划完了,别忘了缓存
缓存策略
首要这些不会频繁更新的挑选条件主张运用缓存:
- 比较常用的就是redis缓存
- 再进阶一点,假如你运用docker,能够把这些装备信息写入docker容器所在物理机的内存中,而不用恳求其他节点的redis,进一步下降网络传输带来的耗时损耗
- 挑选条件这类装备信息,客户端和服务端能够约好一个更新缓存的机制,客户端直接缓存装备信息,进一步进步功能
列表数据主动缓存
目前许多框架都是支撑主动缓存处理的,比方goframe和go-zero
goframe
能够运用ORM链式操作-查询缓存
示例代码:
package main
import (
"time"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gctx"
)
func main() {
var (
db = g.DB()
ctx = gctx.New()
)
// 开启调试模式,以便于记载一切履行的SQL
db.SetDebug(true)
// 写入测验数据
_, err := g.Model("user").Ctx(ctx).Data(g.Map{
"name": "xxx",
"site": "https://xxx.org",
}).Insert()
// 履行2次查询并将查询成果缓存1小时,并可履行缓存称号(可选)
for i := 0; i < 2; i++ {
r, _ := g.Model("user").Ctx(ctx).Cache(gdb.CacheOption{
Duration: time.Hour,
Name: "vip-user",
Force: false,
}).Where("uid", 1).One()
g.Log().Debug(ctx, r.Map())
}
// 履行更新操作,并清理指定称号的查询缓存
_, err = g.Model("user").Ctx(ctx).Cache(gdb.CacheOption{
Duration: -1,
Name: "vip-user",
Force: false,
}).Data(gdb.Map{"name": "smith"}).Where("uid", 1).Update()
if err != nil {
g.Log().Fatal(ctx, err)
}
// 再次履行查询,启用查询缓存特性
r, _ := g.Model("user").Ctx(ctx).Cache(gdb.CacheOption{
Duration: time.Hour,
Name: "vip-user",
Force: false,
}).Where("uid", 1).One()
g.Log().Debug(ctx, r.Map())
}
go-zero
DB缓存机制
go-zero缓存规划之持久层缓存
官方都做了具体的介绍,不作为本文的要点。
评论
我的方案也在我的技术沟通群里引起了我们的评论,也和我们共享一下:
Q1 冗余规划和共同性问题
发问: 一个表里做了这么多外键,假如我要查各自的称号,势必要相关4张表,对于这种存在多外键相关的这种表,要不要做冗余呢(直接在主表里冗余各自的称号字段)?要是确保共同性的话,就势必会影响功能,假如做冗余的话,又无法确保共同性
答复:
你看文章的上下文应该知道,文章想处理的是视频列表挑选问题。
你提到的这个场景是在视频详情信息中,假如要展现这些外键的称号怎样规划更好。
我的主张是这样的:
- 依据需求能够做恰当冗余,比方你的主表信息量不大,装备信息修正后同步修正冗余字段的本钱并不高。
- 或许像我文章中写的不做冗余规划,可是会把外键信息缓存,事务查询从缓存中取值。
- 或许将视频详情的查询成果全体进行缓存
仍是看具体需求,假如这些挑选信息不改变或许不需求手艺办理,乃至不需求规划表,直接写死在代码的装备文件中也能够。进一步下降DB压力,进步功能。
Q2 why规划外键?
发问:为什么要规划外键相关?直接写到视频表中不就行了?这么规划的意义在哪里?
答复:
- 关键问题是想处理办理后台灵敏装备
- 假如没有这个需求,咱们能够直接把挑选条件以装备文件的方式写死在程序中,下降复杂度。
- 站在我的视点:这个功能的挑选条件改变并不会很大,所以很懂你的意思。也主张像我2.中的方案去做,去和产品司理拉扯喽~
总结
这篇文章介绍了规划数据库表结构应该考虑的4个方面,还有高雅规划的6个准则,举了一个比方共享了我的规划思路,为了进步功能咱们也要从多方面考虑缓存问题。
收成最大的仍是和我们的沟通评论,总结一下:
- 首要,必定要先搞清楚事务需求。比方我的比方中,假如不需求灵敏设置,彻底能够写到装备文件中,并不需求独自规划外键。主表中直接保存各种挑选标签称号(留意保护的问题,要考虑到数据共同性)
- 数据库表结构规划必定考虑数据量和并发量,我的比方中假如数据量小,能够适作为冗余规划,下降事务复杂度
本文抛砖引玉,欢迎我们留言沟通。
一同学习
欢迎和我一同评论沟通:能够在私信我
也欢迎重视我的公众号:程序员升职加薪之旅
也欢迎我们重视我的,点赞、留言、转发。你的支撑,是我更文的最大动力!
文章首或尾句需求带关键词“本文正在参与「金石方案」”