性能優化系列六:數據庫設計


一、為優化而設計

1. 數據庫設計

數據庫設計,一個軟件項目成功的基石。數據庫設計也是門學問。
在項目早期由開發者進行數據庫設計(后期調優需要DBA)。一個精通OOP和ORM的開發者,設計的數據庫往往更為合理,更能適應需求的變化。因為數據庫的規范化,與OO的部分思想雷同(如內聚)。而DBA,設計的數據庫的優勢是能將DBMS的能力發揮到極致,能夠使用SQL和DBMS實現很多程序實現的邏輯,與開發者相比,DBA優化過的數據庫更為高效和穩定。

2. 數據庫設計與程序設計的差異

 有如下的一個系統:

面向對象設計思路:

封裝
多態
IOC
AOP…

數據庫的設計思路:

關注存儲
關注效率
二維表關系
關注完整性

3. 數據庫設計早期優化

不要把它僅僅當成一個存儲的功能
1、關系明確:各個表之間的關系一定要先例清楚
2、節省空間:原則合適的類型,不要浪費存儲空間
3、提高效率:比如現在的操作系統都是64位的,我們選擇主鍵的類型為beginint,因為beginint也是64位的,和cpu寄存器匹配,這樣就能一次計算拿到值,效率更高

二、設計原則

1. 數據庫種類

說明:我們這里所說的數據庫都是指關系型數據庫

2. 數據庫特點

文件系統和數據庫系統之間的區別:
(1)文件系統用文件將數據長期保存在外存上,數據庫系統用數據庫統一存儲數據;
(2)文件系統中的程序和數據有一定的聯系,數據庫系統中的程序和數據分離;
(3)文件系統用操作系統中的存取方法對數據進行管理,數據庫系統用DBMS統一管理和控制數據;
(4)文件系統實現以文件為單位的數據共享,數據庫系統實現以記錄和字段為單位的數據共享。

文件系統和數據庫系統之間的聯系:
(1)均為數據組織的管理技術;
(2)均由數據管理軟件管理數據,程序與數據之間用存取方法進行轉換;
(3)數據庫系統是在文件系統的基礎上發展而來的。

3. 優化設計第一步

 精通數據類型

 

4. 優化設計第二步

 了解范式1NF,2NF,3NF。。。

4.1 第一范式

1NF:列不可分。每一列都是不可分割的基本數據項

  反例:某列:姓名=(小李,小張)

4.2 第二范式

2NF:1NF的基礎上面,非主屬性完全依賴於主關鍵字

         ID,學號,姓名,帳號,年級

    姓名、年齡這些非主屬性依賴於主關鍵字學號、賬號

4.3 第三范式
3NF:屬性不依賴於其它非主屬性 , 消除傳遞依賴

     課程表:學員ID,學號,年級

     這里的學號就和4.2中的學號存在傳遞依賴,因為4.2中的學號變更了,這里的學號也要跟着變更
4.4 BCNF:符合3NF,每個表中只有一個候選鍵
4.5 4NF:沒有多值依賴

說明:在設計表的時候滿足第一二三范式

5. 優化設計第三步

設計之前要有一些想法

1、選擇小的數據類型——節省空間
2、單獨設計主鍵,並考慮分布式擴展
3、外鍵設計
4、索引設計——提高性能
5、關聯關系表設計,多對一,多對多
6、讀寫頻繁的信息,與不頻繁的信息分開
7、配置表,日志表,定時任務表等
8、匯總表設計——解決大數據性能問題時根據業務場景匯總數據

6. 優化設計第四步

 要有一些套路

1、通用型設計
例:人員,部門,角色

這里指的通用型設計是指適用性,比如最開始做的是一個小公司的OA系統的數據庫設計,后面切換到一個國際型的大公司以后,這一套設計是否還能用
2、特別設計
附件,日志,配置,監控等
3、存儲設計
類型划分便於分區
4、一些附加字段
創建日期,修改日期,排序
5、流水表
類似於日志,但由業務處理結果組成,帳戶變動或業務處理的中間值

7. Codd的RDBMS12法則

Edgar Frank Codd(埃德加·弗蘭克·科德)被譽為“關系數據庫之父”,並因為在數據庫管理系統的理論和實踐方面的傑出貢獻於1981年獲圖靈獎。在1985年,Codd博士發布了12條規則,這些規則簡明的定義出一個關系型數據庫的理念,它們被作為所有關系數據庫系統的設計指導性方針。

1.信息法則 關系數據庫中的所有信息都用唯一的一種方式表示——表中的值。

2.保證訪問法則 依靠表名、主鍵值和列名的組合,保證能訪問每個數據項。

3.空值的系統化處理 支持空值(NULL),以系統化的方式處理空值,空值不依賴於數據類型。

4.基於關系模型的動態聯機目錄 數據庫的描述應該是自描述的,在邏輯級別上和普通數據采用同樣的表示方式,即數據庫必須含有描述該數據庫結構的系統表或者數據庫描述信息應該包含在用戶可以訪問的表中。

5.統一的數據子語言法則 一個關系數據庫系統可以支持幾種語言和多種終端使用方式,但必須至少有一種語言,它的語句能夠一某種定義良好的語法表示為字符串,並能全面地支持以下所有規則:數據定義、視圖定義、數據操作、約束、授權以及事務。(這種語言就是SQL)

6.視圖更新法則 所有理論上可以更新的視圖也可以由系統更新。

7.高級的插入、更新和刪除操作 把一個基礎關系或派生關系作為單個操作對象處理的能力不僅適應於數據的檢索,還適用於數據的插入、修改個刪除,即在插入、修改和刪除操作中數據行被視作集合。

8.數據的物理獨立性 不管數據庫的數據在存儲表示或訪問方式上怎么變化,應用程序和終端活動都保持着邏輯上的不變性。

9.數據的邏輯獨立性 當對表做了理論上不會損害信息的改變時,應用程序和終端活動都會保持邏輯上的不變性。

10.數據完整性的獨立性 專用於某個關系型數據庫的完整性約束必須可以用關系數據庫子語言定義,而且可以存儲在數據目錄中,而非程序中。

11.分布獨立性 不管數據在物理是否分布式存儲,或者任何時候改變分布策略,RDBMS的數據操縱子語言必須能使應用程序和終端活動保持邏輯上的不變性。

12.非破壞性法則 如果一個關系數據庫系統支持某種低級(一次處理單個記錄)語言,那么這個低級語言不能違反或繞過更高級語言(一次處理多個記錄)規定的完整性法則或約束,即用戶不能以任何方式違反數據庫的約束

落實這些原則:

(一)降低對數據庫功能的依賴

(二)定義實體關系的原則
    牽涉到的實體 識別出關系所涉及的所有實體。
    所有權 考慮一個實體“擁有”另一個實體的情況。
    基數 考量一個實體的實例和另一個實體實例關聯的數量。
(三)列意味着唯一的值
    如果表示坐標(0,0),應該使用兩列表示,而不是將“0,0”放在1個列中。
(四)列的順序,可讀性問題
(五)定義主鍵和外鍵
    數據表必須定義主鍵和外鍵(如果有外鍵)。
(六)選擇鍵
(七)是否允許NULL
    任何值和NULL拼接后都為NULL。
    所有與NULL進行的數學操作都返回NULL。
    引入NULL后,邏輯不易處理。

(八)規范化——范式

    1NF
    包含分隔符類字符的字符串數據。
    名字尾端有數字的屬性。
    沒有定義鍵或鍵定義不好的表。
    2NF
    多個屬性有同樣的前綴。
    重復的數據組。
    匯總的數據,所引用的數據在一個完全不同的實體中。
    BCNF- “每個鍵必須唯一標識實體,每個非鍵熟悉必須描述實體。
    4NF
    三元關系(實體:實體:實體)。
    潛伏的多值屬性。(如多個手機號。)
    臨時數據或歷史值。(需要將歷史數據的主體提出,否則將存在大量冗余。)
(九)選擇數據類型
(十)優化並行
    設計DB時就應該考慮到對並行進行優化,比如,timestamp類型。

8. 命名規則

表名規則
1、要用前綴,但不要用無意義的前綴,一般是根據業務模塊設置前綴
2、下划線分隔
3、全小寫
列名規則
1、一般不用前綴
2、下划線分隔
3、全小寫

三、設計案例

1. 鍵設計

物理主鍵,好建索引,消除傳遞依賴
主鍵類型,普通系統是int或bigint,效率問題,目前的操作系統基本都是64位的,建議使用bigint
Uuid,容量問題,防碰撞
取消所有的聯合主鍵(課程表中:年級+課程名)

2. 索引設計

2.1. B-Tree 索引

B-Tree 索引是 MySQL 數據庫中使用最為頻繁的索引類型,除了 Archive 存儲引擎之外的其他所有的存儲引擎都支持B-Tree 索引。不僅僅在MySQL中是如此,實際上在其他的很多數據庫管理系統中B-Tree索引也同樣是作為最主要的索引類型,這主要是因為B-Tree索引的存儲結構在數據庫的數據檢索中有非常優異的表現

2.2. Hash 索引

Hash 索引結構的特殊性,其檢索效率非常高,索引的檢索可以一次定位,不像B-Tree索引需要從根節點到枝節點,最后才能訪問到頁節點這樣多次的IO訪問,所以Hash索引的查詢效率要遠高於B-Tree索引。

 示例:

Hash值:1111對應的有 記錄1,記錄2,記錄3 
Hash值:2222對應的記錄有 記錄4,記錄5,記錄6

說明:

根據字段計算hash值,把記錄放在一個格子里,索引的時候只需要找到hash值就能獲取到里面的記錄了

2.3. 索引的類型

普通索引:最基本的索引,沒有任何限制

唯一索引:與"普通索引"類似,不同的就是:索引列的值必須唯一,但允許有空值。

主鍵索引:它是一種特殊的唯一索引,不允許有空值。

全文索引:僅可用於 MyISAM 表,針對較大的數據,生成全文索引很耗時好空間。

組合索引:為了更多的提高mysql效率可建立組合索引,遵循”最左前綴“原則。

覆蓋索引(Covering Indexes):查詢的列正好是索引列就叫做覆蓋索引,如select id from table_a

聚簇索引(Clustered Indexes)

聚簇索引保證關鍵字的值相近的元組存儲的物理位置也相同(所以字符串類型不宜建立聚簇索引,特別是隨機字符串,會使得系統進行大量的移動操作),且一個表只能有一個聚簇索引。因為由存儲引擎實現引,所以,並不是所有的引擎都支持聚簇索引。目前,只有solidDB和InnoDB支持。

非聚簇索引

二級索引葉子節點保存的不是指行的物理位置的指針,而是行的主鍵值。這意味着通過二級索引查找行。
InnoDB對主鍵建立聚簇索引。如果你不指定主鍵,InnoDB會用一個具有唯一且非空值的索引來代替。如果不存在這樣的索引,InnoDB會定義一個隱藏的主鍵,然后對其建立聚簇索引。一般來說,DBMS都會以聚簇索引的形式來存儲實際的數據,它是其它二級索引的基礎。

2.4 什么時候可以建索引

1)列無重復值,可以建索引:唯一索引和普通索引

2)聚集索引和非聚集索引都可以是唯一的。因此,只要列中的數據是唯一的,就可以在同一個表上創建一個唯一的聚集索引和多個唯一的非聚集索引。

3)建了索引性能得到提高

4)區分度高的列可以建索引,比如表示男和女的列區分度就不高,就不能建索引

說明:

唯一索引一定要小心,它帶有唯一約束。

查詢區分度:SELECT COUNT(DISTINCT 列_xx)/COUNT(*) FROM 表

2.5 什么時候不可以建索引

1.頻繁更新的字段不適合建立索引

2.where條件中用不到的字段不適合建立索引

3.表數據可以確定比較少的不需要建索引

4.數據重復且發布比較均勻的的字段不適合建索引(唯一性太差的字段不適合建立索引),例如性別,真假值

5. 參與列計算的列不適合建索引,如select * from where amount+1>10

6. 查詢返回的記錄數不適合建立索引

7. 查詢的排序表記錄小於40%不適合建立索引

8. 查詢非排序表的記錄小於 7%不適合建立索引

9. 表的碎片較多(頻繁增加、刪除)不適合建立索引

說明:

基礎表維護時,系統要同時維護索引,不合理的索引將嚴重影響系統資源,主要表現在CPU和I/O上;
插入、更新、刪除數據產生大量db file sequential read鎖等待;

2.6 建索引的目的

加快查詢速度,當然了,使用索引后查詢有跡可循。
減少I/O操作,通過索引的路徑來檢索數據,不是在磁盤中隨機檢索。
消除磁盤排序,索引是排序的,走完索引就排序完成

2.7 存儲引擎及文件格式比較

只需要記住Innodb和Myisam的鎖和事物的區別就可以了:

Innodb支持表鎖和行鎖,Myisam只支持表鎖

Innodb支持事物,Myisam不支持事物

 2.8 各存儲引擎的區別

 

說明:

Innod的存儲最大限制64TB

3. 表附加字段設計

常用:
創建時間:create_at
修改時間:update_at
排序:sn
有時用:
說明:desc,remark
備選字段:opts_1,opts_2….

4. 字典表設計

字典表與系統配置表的區別
字典表示例
常用數據的常量化,消費類型,支付類型,物品類型(含層級)
系統配置表示例
各模塊功能參數的常量化,key-value

字典表:

CREATE TABLE dict_content
(
    dcc_id bigint NOT NULL, 
    dct_id bigint, 
    dcc_no character varying(20), 
    dcc_name character varying(30), 
    extend_no character varying(50), 
    remarks character varying(400), 
    sort bigint, 
    parent_id bigint,
    is_leaf smallint, 
    path character varying(400), 
    CONSTRAINT dict_content_pkey PRIMARY KEY (dcc_id)
)

系統表:

CREATE TABLE sys_config_data
(
    id bigint NOT NULL,
    create_date timestamp without time zone NOT NULL,
    modify_date timestamp without time zone NOT NULL,
    catalog character varying(255),
    content character varying(255),
    description character varying(255),
    sn character varying(255),
    CONSTRAINT sys_config_data_pkey PRIMARY KEY (id)
)

5. 附件表設計

存儲位置
類型
用途
使用次數
下載次數

6. 層級結構表設計

父子層級,parent_id
表內划分層級
表外划分層級
最快檢索設計

CREATE TABLE public.ihp_boq_tpl 
( 
    boq_id bigint NOT NULL, -- 清單id 
    boq_no character varying(32), -- 清單編號 
    ext_no character varying(32), -- 擴展編號 
    boq_name character varying(64), -- 清單名稱 
    unit_id bigint, -- 計量單位 
    boq_kind smallint, -- 清單類型 
    boq_mode smallint, -- 清單模式 
    boq_rate numeric(18,2), -- 計量率 
    formula character varying(32), -- 計算公式 
    use_formula smallint, -- 是否啟用公式 
    parent_id bigint, -- 父節點 
    path character varying(128), -- 路徑 
    level smallint, -- 層次 
    end_node smallint, -- 最終節點 
    remarks character varying(256), -- 備注 
    status smallint, -- 狀態 
    feature character varying(128), 
    frequency character varying(32), 
    quota_unit_id bigint, 
    quota_tbl_no character varying(8), 
    project_id bigint, 
    CONSTRAINT pk_ihp_boq_tpl PRIMARY KEY (boq_id) 
)

 

 

7. 流程表設計

流程主表
任務子表
業務表關聯

CREATE TABLE process_run
(
    runid bigint NOT NULL, 
    subject character varying(256) NOT NULL, 
    creator character varying(128), 
    userid bigint NOT NULL, 
    defid bigint NOT NULL, 
    piid character varying(64), 
    createtime timestamp without time zone NOT NULL, 
    runstatus bigint NOT NULL, 
    busdesc character varying(1024), 
    entityname character varying(128), 
    entityid bigint, 
    formdefid bigint, 
    CONSTRAINT pk_process_run PRIMARY KEY (runid)
)
CREATE TABLE process_form
(
    formid bigint NOT NULL, 
    runid bigint NOT NULL, 
    activityname character varying(256) NOT NULL, 
    createtime timestamp without time zone NOT NULL, 
    endtime timestamp without time zone, 
    durtimes bigint, 
    creatorid bigint, 
    creatorname character varying(256), 
    taskid character varying(64), 
    status bigint DEFAULT 0, 
    preformid bigint, 
    comments character varying(2000), 
    entity_id bigint, 
    entity_name character varying(128), 
    CONSTRAINT pk_process_form PRIMARY KEY (formid)
)

四、冗余設計

反范式

適當冗余
1、借鑒覆蓋索引的思路,在表內直接放常用的字段
2、提取相關數據,制作匯總表
3、第3NF的違反
4、程序級別的冗余或緩存
5、不要寫觸發器,不要寫存儲過程

分表分庫

•性能:能輕松面對海量數據和高並發的請求處理,好的分布式數據庫能做到90%以上的線性增長能力;
•靈活性、彈性:現代的系統的業務和使用場景變化很快,用戶的增長也有很多不確定因素。彈性擴容就非常重要。分布式數據庫本身有Cloud-Ready的特性,能很容以通過添加設備擴容滿足需求,而不需要影響開發;
•多中心、多活:這點在大型應用中很常見,分布式數據庫就更容易實現這個功能,當然這里涉及到分布式數據庫的同步和一致性的能力,這也是判斷分布式數據庫好壞的一個重要指標。
•讀寫分離:主從節點都能發揮作用;例如巨杉SequoiaDB數據庫,能在一組三副本的復制組上實現OLTP,NoSQL應用,OLAP多種應用場景同時使用。
•低成本:x86服務器,SATA存儲(部分可以用SSD),加上較好的網絡帶寬就可以了。

 

1.水平分區:基本對1個或多個鍵的水平哈希,增強數據的並行處理能力來提高性能;
2.垂直分區:和過去的Partition很像,對數據進行有含義的拆分;
3.混合分區:水平分區和垂直分區共同使用。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM