我們該如何設(shè)計數(shù)據(jù)庫(四)
其實我一直在準備另一篇博文的基礎(chǔ)資料,但是和朋友聊天,他問我最近在做什么,我說在做系統(tǒng)Log模塊,并和他交流了一下,于是這篇博客就應運而生。
所有數(shù)據(jù)都可以用如下形式表述:ID,表名,列名,Value。
比如說現(xiàn)在有這么一條數(shù)據(jù)要插入User表:
ID(Guid,這里為了方便理解用Int) | Username | Password | |
1 | CrazyJinn | 123456 | CrazyJinn@W.C |
這一條記錄可以轉(zhuǎn)換為:
ID | 表名 | 列名 | Value |
1 | User | Username | CrazyJinn |
1 | User | Password | 123456 |
1 | User | CrazyJinn@W.C |
你可以在各種對靈活性要求高的地方看到這種設(shè)計,比如說在《我們該如何設(shè)計數(shù)據(jù)庫(三)》的留言中,就有園友提到了類似的設(shè)計。
當然,這種方式效率不是很高;不過可以把聚集索引加到表名上,然后非聚集索引加在列名上,再水平分割一下,如果你心情好,再做個讀寫分離,相信就非高并發(fā)、千萬數(shù)據(jù)量級的應用來說,理論上還是可以接受的。
好了,現(xiàn)在進入正文。
現(xiàn)在要做一個通用的Log模塊。
既然是通用的,那就意味著靈活性要非常強,因為你不知道Log中要記錄的數(shù)據(jù)結(jié)構(gòu)是如何的。
而且給我的需求有一個非常變態(tài)的地方:要有回退功能。不過這個我們先不去管他。
根據(jù)之前的討論,我們可以很容易設(shè)計出一張Log表。
ID | 表名 | 列名 | Type(Create\Edit\Delete) | Value | ***修改時間 |
如果處理的全是無關(guān)系的問題,這樣做就足夠了。但是要知道,RDBMS,最重要的就是關(guān)系的處理。
比如說要Log這樣兩張表:
上文所設(shè)計出的Log表面對這樣的一對多關(guān)系是無法儲存的,不要往了,我們還有多對多關(guān)系。
當然可以拓展Log表來實現(xiàn)儲存一對多/多對多關(guān)系,雖然我不確定能不能做到,因為我沒有就這方面去深入的思考。如果您想到了好的設(shè)計,歡迎留言和我探討。
讓我們來重新思考一下Log模塊的本質(zhì):
1、大量數(shù)據(jù)。
2、只是大量數(shù)據(jù)(和別的模塊沒有關(guān)聯(lián),純粹的數(shù)據(jù))。
這種場景讓我不由自主的想到了Nosql。在這里,使用MongoDB來實現(xiàn)。關(guān)于MongoDB入門,可以參考下面兩篇文章:
祥叔:《MongoDB開發(fā)學習(1)開天辟地,經(jīng)典入門》
Fish Li:《MongoDB實戰(zhàn)開發(fā) 【零基礎(chǔ)學習,附完整Asp.net示例】》
MongoDB使用Bson來儲存數(shù)據(jù),你可以簡單的把Bson理解為Json。眾所周知,Json是一個非常易于擴充的,松散的的數(shù)據(jù)格式;基于Json易于擴充的特性,我們可以這樣來設(shè)計Log表
LogID | ID | 表名 | Content(所儲存的內(nèi)容,包含了***修改時間,修改類型,以及新的修改ID) |
如果我對User表修改了6次,那么我們Log的數(shù)據(jù)如下圖:
我們主要把注意力集中在上圖用紅框標注的3條數(shù)據(jù)上。
***條數(shù)據(jù),ContactList是一個Array類型,長度為0,這表示沒有對應的Contact。
第二條數(shù)據(jù),ContactList長度變?yōu)?,這表示這次修改為User添加了一個Contact的關(guān)聯(lián),我們將第二條數(shù)據(jù)完全展開來看:
可以看到,包含了一個完整的Contact進來。
#p#
第三條數(shù)據(jù)ContactList為Null,這表示我在某個別的地方修改了User信息。這次修改沒有涉及Contact,所以保存為Null。當我們?nèi)?shù)據(jù)的時候,如果發(fā)現(xiàn)某個List為Null,就要遞歸的向上去查找不為Null的數(shù)據(jù)。例如我這里,就要去找到第二條數(shù)據(jù)的ContactList。
為了方便大家理解,我把Json貼在下面。對照前面的圖片可以很好的閱讀。
- {
- "Content" : [{
- "_id" : new BinData(3, "mtonv7sMCkewsMIjWZ9/qg=="),
- "Username" : "1",
- "Password" : "1",
- "Number" : 1,
- "LastModified" : new Date("27/11/2012 10:28:18"),
- "ContactList" : [{
- "_id" : new BinData(3, "1QwcXGKedUCO27QprZB26Q=="),
- "UserID" : new BinData(3, "YyQDfuoj6EuuDNl91leigA=="),
- "Phone" : "Phone1",
- "Email" : "Email1"
- }, {
- "_id" : new BinData(3, "EeWfiFCknkex4H2jEraR/w=="),
- "UserID" : new BinData(3, "YyQDfuoj6EuuDNl91leigA=="),
- "Phone" : "Phone2",
- "Email" : "Email2"
- }]
- }, {
- "_id" : new BinData(3, "Afk3spV0q0uKM+yNs/SHbw=="),
- "Username" : "1 to 2",
- "Password" : "1 to 2",
- "Number" : 2,
- "LastModified" : new Date("27/11/2012 10:35:03"),
- "ContactList" : []
- }, {
- "_id" : new BinData(3, "H/5o2lizmUWkaxAZUgNHzg=="),
- "Username" : "2 to 3",
- "Password" : "2 to 3",
- "Number" : 3,
- "LastModified" : new Date("27/11/2012 10:40:28"),
- "ContactList" : [{
- "_id" : new BinData(3, "7HDyGU2+A02HbQtUFbOo8A=="),
- "UserID" : new BinData(3, "H/5o2lizmUWkaxAZUgNHzg=="),
- "Phone" : "PhoneNew",
- "Email" : "EmailNew"
- }]
- }, {
- "_id" : new BinData(3, "zf2SiYW81kufGO7ZgY5r3A=="),
- "Username" : "3 to 4",
- "Password" : "3 to 4",
- "Number" : 4,
- "LastModified" : new Date("27/11/2012 10:41:34"),
- "ContactList" : null
- }, {
- "_id" : new BinData(3, "N68jDslbU0uvdHJTSq0vIg=="),
- "Username" : "5",
- "Password" : "6",
- "Number" : 7,
- "LastModified" : new Date("27/11/2012 17:14:12"),
- "ContactList" : null
- }, {
- "_id" : new BinData(3, "Fw6OqMNcc0K+rySfgz3dTg=="),
- "Username" : "9",
- "Password" : "9",
- "Number" : 9,
- "LastModified" : new Date("27/11/2012 17:16:15"),
- "ContactList" : [{
- "_id" : new BinData(3, "zfsQRK***0kGFFcnc5TZ9GA=="),
- "UserID" : new BinData(3, "YyQDfuoj6EuuDNl91leigA=="),
- "Phone" : "PhoneNew",
- "Email" : "EmailNew"
- }]
- }],
- "ModelID" : new BinData(3, "YyQDfuoj6EuuDNl91leigA=="),
- "ModelName" : "User",
- "_id" : ObjectId("50b4254257751f09a02decba")
- }
這樣,一個Log功能的雛形就出來了
就此擱筆
原文鏈接:http://www.cnblogs.com/CrazyJinn/archive/2012/12/04/2794785.html