前言
最近有不少前端和測試轉(zhuǎn)Go的朋友在??交流群??里聊:如何做表結(jié)構(gòu)設(shè)計(jì)?
大家關(guān)心的問題陽哥必須整理出來,希望對大家有幫助。
4個(gè)方面
設(shè)計(jì)數(shù)據(jù)庫表結(jié)構(gòu)需要考慮到以下4個(gè)方面:
- 數(shù)據(jù)庫范式:通常情況下,我們希望表的數(shù)據(jù)符合某種范式,這可以保證數(shù)據(jù)的完整性和一致性。例如,第一范式要求表的每個(gè)屬性都是原子性的,第二范式要求每個(gè)非主鍵屬性完全依賴于主鍵,第三范式要求每個(gè)非主鍵屬性不依賴于其他非主鍵屬性。
- 實(shí)體關(guān)系模型(ER模型):我們需要先根據(jù)實(shí)際情況畫出實(shí)體關(guān)系模型,然后再將其轉(zhuǎn)化為數(shù)據(jù)庫表結(jié)構(gòu)。實(shí)體關(guān)系模型通常包括實(shí)體、屬性、關(guān)系等要素,我們需要將它們轉(zhuǎn)化為表的形式。
- 數(shù)據(jù)庫性能:我們需要考慮到數(shù)據(jù)庫的性能問題,包括表的大小、索引的使用、查詢語句的優(yōu)化等。
- 數(shù)據(jù)庫安全:我們需要考慮到數(shù)據(jù)庫的安全問題,包括表的權(quán)限、用戶角色的設(shè)置等。
設(shè)計(jì)原則
在設(shè)計(jì)數(shù)據(jù)庫表結(jié)構(gòu)時(shí),可以參考以下幾個(gè)優(yōu)雅的設(shè)計(jì)原則:
- 簡單明了:表結(jié)構(gòu)應(yīng)該簡單明了,避免過度復(fù)雜化。
- 一致性:表結(jié)構(gòu)應(yīng)該保持一致性,例如命名規(guī)范、數(shù)據(jù)類型等。
- 規(guī)范化:盡可能將表規(guī)范化,避免數(shù)據(jù)冗余和不一致性。
- 性能:表結(jié)構(gòu)應(yīng)該考慮到性能問題,例如使用適當(dāng)?shù)乃饕⒈苊馊頀呙璧取?/li>
- 安全:表結(jié)構(gòu)應(yīng)該考慮到安全問題,例如合理設(shè)置權(quán)限、避免SQL注入等。
- 擴(kuò)展性:表結(jié)構(gòu)應(yīng)該具有一定的擴(kuò)展性,例如預(yù)留字段、可擴(kuò)展的關(guān)系等。
最后,需要提醒的是,優(yōu)雅的數(shù)據(jù)庫表結(jié)構(gòu)需要在實(shí)踐中不斷迭代和優(yōu)化,不斷滿足實(shí)際需求和新的挑戰(zhàn)。
下面舉個(gè)示例讓大家更好的理解如何設(shè)計(jì)表結(jié)構(gòu),如何引入內(nèi)存,有哪些優(yōu)化思路:
問題描述
如上圖所示,紅框中的視頻篩選標(biāo)簽,應(yīng)該怎么設(shè)計(jì)數(shù)據(jù)庫表結(jié)構(gòu)?
這是一個(gè)很好的應(yīng)用場景,大家可以先自己想一下。不要著急看我的方案。
需求分析
- 可以根據(jù)紅框的標(biāo)簽篩選視頻
- 其中綜合標(biāo)簽比較特殊,和類型、地區(qū)、年份、演員等不一樣
- 綜合是根據(jù)業(yè)務(wù)邏輯取值,并不需要入庫
- 類型、地區(qū)、年份、演員等需要入庫
- 設(shè)計(jì)表結(jié)構(gòu)時(shí)要考慮到:
- 方便獲取標(biāo)簽信息,方便把標(biāo)簽信息緩存處理
- 方便根據(jù)標(biāo)簽篩選視頻,方便我們寫后續(xù)的業(yè)務(wù)邏輯
設(shè)計(jì)思路
- 綜合標(biāo)簽可以寫到配置文件中(或者寫在前端),這些信息不需要靈活配置,所以不需要保存到數(shù)據(jù)庫中
- 類型、地區(qū)、年份、演員都設(shè)計(jì)單獨(dú)的表
- 視頻表中設(shè)計(jì)標(biāo)簽表的外鍵,方便視頻列表篩選取值
- 標(biāo)簽信息寫入緩存,提高接口響應(yīng)速度
- 類型、地區(qū)、年份、演員表也要支持對數(shù)據(jù)排序,方便后期管理維護(hù)
表結(jié)構(gòu)設(shè)計(jì)
視頻表
字段 |
注釋 |
id |
視頻主鍵id |
type_id |
類型表外鍵id |
area_id |
地區(qū)表外鍵id |
year_id |
年份外鍵id |
actor_id |
演員外鍵id |
其他和視頻直接相關(guān)的字段(比如名稱)我就省略不寫了
類型表
字段 |
注釋 |
id |
類型主鍵id |
name |
類型名稱 |
sort |
排序字段 |
地區(qū)表
字段 |
注釋 |
id |
類型主鍵id |
name |
類型名稱 |
sort |
排序字段 |
年份表
字段 |
注釋 |
id |
類型主鍵id |
name |
類型名稱 |
要么是年份正序排列,要么是年份倒序排列,所以不需要sort字段
演員表
字段 |
注釋 |
id |
類型主鍵id |
name |
類型名稱 |
sort |
排序字段 |
表結(jié)構(gòu)設(shè)計(jì)完了,別忘了緩存
緩存策略
首先這些不會頻繁更新的篩選條件建議使用緩存:
- 比較常用的就是redis緩存
- 再進(jìn)階一點(diǎn),如果你使用Docker,可以把這些配置信息寫入docker容器所在物理機(jī)的內(nèi)存中,而不用請求其他節(jié)點(diǎn)的redis,進(jìn)一步降低網(wǎng)絡(luò)傳輸帶來的耗時(shí)損耗
- 篩選條件這類配置信息,客戶端和服務(wù)端可以約定一個(gè)更新緩存的機(jī)制,客戶端直接緩存配置信息,進(jìn)一步提高性能
列表數(shù)據(jù)自動緩存
目前很多框架都是支持自動緩存處理的,比如goframe和go-zero,官方文檔都做了詳細(xì)的介紹,不作為本文的重點(diǎn)。
goframe
可以使用ORM鏈?zhǔn)讲僮?查詢緩存[1]
官方示例:
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()
)
// 開啟調(diào)試模式,以便于記錄所有執(zhí)行的SQL
db.SetDebug(true)
// 寫入測試數(shù)據(jù)
_, err := g.Model("user").Ctx(ctx).Data(g.Map{
"name": "xxx",
"site": "https://xxx.org",
}).Insert()
// 執(zhí)行2次查詢并將查詢結(jié)果緩存1小時(shí),并可執(zhí)行緩存名稱(可選)
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())
}
// 執(zhí)行更新操作,并清理指定名稱的查詢緩存
_, 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)
}
// 再次執(zhí)行查詢,啟用查詢緩存特性
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緩存機(jī)制[2]
go-zero緩存設(shè)計(jì)之持久層緩存[3]
官方文檔都做了詳細(xì)的介紹,不作為本文的重點(diǎn)。
總結(jié)
這篇文章介紹了設(shè)計(jì)數(shù)據(jù)庫表結(jié)構(gòu)應(yīng)該考慮的4個(gè)方面,還有優(yōu)雅設(shè)計(jì)的6個(gè)原則,舉了一個(gè)例子分享了我的設(shè)計(jì)思路,為了提高性能我們也要從多方面考慮緩存問題。
本文拋磚引玉,歡迎大家留言交流。
相關(guān)資料
[1]ORM鏈?zhǔn)讲僮?查詢緩存: https://goframe.org/pages/viewpage.action?pageId=1114346
[2]DB緩存機(jī)制: https://go-zero.dev/cn/docs/blog/cache/cache
[3]go-zero緩存設(shè)計(jì)之持久層緩存: https://go-zero.dev/cn/docs/blog/cache/redis-cache
本文轉(zhuǎn)載自微信公眾號「 程序員升級打怪之旅」,作者「王中陽Go」