1、單表設計與優化:
(1)設計規範化表,消除數據冗餘(以使用正確字段類型最明顯):
數據庫範式是確保數據庫結構合理,知足各類查詢須要、避免數據庫操做異常的數據庫設計方式。知足範式要求的表,稱爲規範化表,範式產生於20世紀70年代初,通常表設計知足前三範式就能夠,在這裏簡單介紹一下前三範式。
第一範式(1NF)無重複的列
所謂第一範式(1NF)是指在關係模型中,對域添加的一個規範要求,全部的域都應該是原子性的,即數據庫表的每一列都是不可分割的原子數據項,而不能是集合,數組,記錄等非原子數據項。
第二範式(2NF)屬性
在1NF的基礎上,非碼屬性必須徹底依賴於碼[在1NF基礎上消除非主屬性對主碼的部分函數依賴]
第三範式(3NF)屬性
在1NF基礎上,任何非主屬性不依賴於其它非主屬性[在2NF基礎上消除傳遞依賴。
通俗點講:
第一範式:
屬性(字段)的原子性約束,要求屬性具備原子性,不可再分割;
第二範式:
記錄的唯一性約束,要求記錄有唯一標識,每條記錄須要有一個屬性來作爲實體的惟一標識,即每列都要和主鍵相關。
第三範式:
屬性(字段)冗餘性的約束,即任何字段不能由其餘字段派生出來,在通俗點就是:主鍵沒有直接關係的數據列必須消除(消除的辦法就是再建立一個表來存放他們,固然外鍵除外)。即:確保每列都和主鍵列直接相關,而不是間接相關。
若是數據庫設計達到了徹底的標準化,則把全部的表經過關鍵字鏈接在一塊兒時,不會出現任何數據的複本(repetition)。標準化的優勢是明顯的,它避免了數據冗餘,天然就節省了空間,也對數據的一致性(consistency)提供了根本的保障,杜絕了數據不一致的現象,同時也提升了效率。
尤爲是正確字段類型的選擇:
全部字段類型:
(一)整型數值:
(二)浮點數類型
(三)定點數類型
關於浮點數與定點數有點見解:
浮點數相對於定點數的優勢是在長度必定的狀況下,浮點數可以表示更大的數據範圍;它的缺點是會引發精度問題。
使用時咱們要注意:
1. 浮點數存在偏差問題;
2. 對貨幣等對精度敏感的數據,應該用定點數表示或存儲;
3. 編程中,若是用到浮點數,要特別注意偏差問題,並儘可能避免作浮點數比較;
4. 要注意浮點數中一些特殊值的處理。
(四)位類型
(五)日期時間類型(mysql中用now()寫入當前時間)
(六)字符串類型:
針對經常使用的varchar,咱們來思考幾個問題:
1)varchar的長度?
MySQL的文檔,其中對varchar字段類型這樣描述:varchar(m) 變長字符串。m 表示最大列長度。m的範圍是0到65,535。(VARCHAR的最大實際長度由最長的行的大小和使用的字符集肯定,最大有效長度是65,532字節)。
mysql varchar(50) 無論中文 仍是英文 都是存50個的,可是一個表中全部varchar字段的總長度跟編碼有關,若是是utf-8,那麼大概65535/3,若是是gbk,那麼大概65535/2.
2)存儲限制?編碼長度限制?行長度限制?超出了,會變成怎樣?
針對第一個問題:varchar 字段是將實際內容單獨存儲在聚簇索引以外,實際存儲從第二個字節開始,接着要用1到2個字節表示實際長度(長度超過255時須要2個字節),所以最大長度不能超過65535。
針對第二個問題:字符類型若爲gbk,每一個字符最多佔2個字節。字符類型若爲utf8,每一個字符最多佔3個字節。
針對第三個問題:致使實際應用中varchar長度限制的是一個行定義的長度。 MySQL要求一個行的定義長度不能超過65535。若定義的表長度超過這個值,則提示
ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. You have to change some columns to TEXT or BLOBs。
1
針對第四個問題:若定義的時候超過上述限制,則varchar字段會被強行轉爲text類型,併產生warning。
3)與char的對比:
CHAR(M)定義的列的長度爲固定的,M取值能夠爲0~255之間,當保存CHAR值時,在它們的右邊填充空格以達到指定的長度。當檢 索到CHAR值時,尾部的空格被刪除掉。在存儲或檢索過程當中不進行大小寫轉換。CHAR存儲定長數據很方便,CHAR字段上的索引效率級高,好比定義 char(10),那麼不論你存儲的數據是否達到了10個字節,都要佔去10個字節的空間,不足的自動用空格填充。
CHAR和VARCHAR最大的不一樣就是一個是固定長度,一個是可變長度。因爲是可變長度,所以實際存儲的時候是實際字符串再加上一個記錄 字符串長度的字節(若是超過255則須要兩個字節)。若是分配給CHAR或VARCHAR列的值超過列的最大長度,則對值進行裁剪以使其適合。若是被裁掉 的字符不是空格,則會產生一條警告。若是裁剪非空格字符,則會形成錯誤(而不是警告)並經過使用嚴格SQL模式禁用值的插入。
4)char、varchar與text的建議:
TEXT只能儲存純文本文件。
效率來講基本是char>varchar>text,可是若是使用的是Innodb引擎的話,推薦使用varchar代替char
char和varchar能夠有默認值,text不能指定默認值
如下給出幾個類型選取建議
(一)數字類型:
1)不到不要使用DOUBLE,不只僅只是存儲長度的問題,同時還會存在精確性的問題。
2)固定精度的小數,也不建議使用DECIMAL
建議乘以固定倍數轉換成整數存儲,能夠大大節省存儲空間,且不會帶來任何附加維護成本。
3)對於整數的存儲,在數據量較大的狀況下,建議區分開 TINYINT / INT / BIGINT 的選擇
由於三者所佔用的存儲空間也有很大的差異,能肯定不會使用負數的字段,建議添加unsigned定義。固然,若是數據量較小的數據庫,也能夠不用嚴格區分三個整數類型。
4)對於整型數值,mysql支持在類型名稱後面的小括號內指定顯示寬度
例如int(5)表示當數值寬度小於5位時候在數值前面填滿寬度,通常配合zerofill屬性使用。若是一個列指定爲zerofill,則MySQL自動爲該列添加unsigned屬性。
5)在數據量較大時、建議把實數類型轉爲整數類型。
緣由很簡單:1. 浮點不精確;2.定點計算代價昂貴。例如:要存放財務數據精確到萬分之1、則能夠把全部金額乘以一百萬、而後存在BIGINT下。
(二)字符類型:
1)儘可能不要使用 TEXT 數據類型,其處理方式決定了他的性能要低於char或者是varchar類型的處理。
定長字段,建議使用 CHAR 類型,不定長字段儘可能使用 VARCHAR,且僅僅設定適當的最大長度,而不是很是隨意的給一個很大的最大長度限定,由於不一樣的長度範圍,MySQL也會有不同的存儲處理。
2)char會刪除字符串尾部的空格,varchar不會,varchar向前補1-2字節;char定長。binary相似於char,binary只能保存二進制字符串。
char是固定長度,因此它的處理速度比varchar快得多,但缺點是浪費存儲空間,不能在行尾保存空格。在MySQL中,MyISAM建議使用固定長度代替可變長度列;InnoDB建議使用varchar類型,由於在InnoDB中,內部行存儲格式沒有區分固定長度和可變長度。
3)enum類型忽略大小寫。
4)text與blob區別:
blob保存二進制數據;text保存字符數據,有字符集。text和blob不能有默認值。
應用:text與blob主要區別是text用來保存字符數據(如文章,日記等),blob用來保存二進制數據(如照片等)。blob與text在執行了大量刪除操做時候,有性能問題(產生大量的「空洞「),爲提升性能建議按期optimize table 對這類表進行碎片整理。
關於text與blob咱們有些見解建議:
BLOB和TEXT值也會引發本身的一些問題,特別是執行了大量的刪除或更新操做的時候。刪除這種值會在數據表中留下很大的"空洞",之後填入這些"空洞"的記錄可能長度不一樣,爲了提升性能,建議按期使用 OPTIMIZE TABLE 功能對這類表進行碎片整理.
在沒必要要的時候避免檢索大型的BLOB或TEXT值。
把BLOB或TEXT列分離到單獨的表中。在某些環境中,若是把這些數據列移動到第二張數據表中,可讓你把原數據表中 的數據列轉換爲固定長度的數據行格式,那麼它就是有意義的。這會減小主表中的碎片,使你獲得固定長度數據行的性能優點。它還使你在主數據表上運行 SELECT *查詢的時候不會經過網絡傳輸大量的BLOB或TEXT值。
(三)時間類型:
1)儘可能使用TIMESTAMP類型
由於其存儲空間只須要 DATETIME 類型的一半。對於只須要精確到某一天的數據類型,建議使用DATE類型,由於他的存儲空間只須要3個字節,比TIMESTAMP還少。不建議經過INT類型類存儲一個unix timestamp 的值,由於這太不直觀,會給維護帶來沒必要要的麻煩,同時還不會帶來任何好處。
2)根據實際須要選擇可以知足應用的最小存儲日期類型。
3)timestamp,日期類型中只有它可以和實際時區相對應。
(四)ENUM & SET:
對於狀態字段,能夠嘗試使用 ENUM 來存放,由於能夠極大的下降存儲空間,並且即便須要增長新的類型,只要增長於末尾,修改結構也不須要重建表數據。若是是存放可預先定義的屬性數據呢?能夠嘗試使用SET類型,即便存在多種屬性,一樣能夠遊刃有餘,同時還能夠節省不小的存儲空間。
(五)LOB類型:
強烈反對在數據庫中存放 LOB 類型數據,雖然數據庫提供了這樣的功能,但這不是他所擅長的,咱們更應該讓合適的工具作他擅長的事情,才能將其發揮到極致。
(2)適當的冗餘,增長計算列:(實際開發中必須思考的點)
數據庫設計的實用原則是:在數據冗餘和處理速度之間找到合適的平衡點。
知足範式的表必定是規範化的表,但不必定是最佳的設計。不少狀況下會爲了提升數據庫的運行效率,經常須要下降範式標準:適當增長冗餘,達到以空間換時間的目的。好比
咱們有一個表,產品名稱,單價,庫存量,總價值。這個表是不知足第三範式的,由於「總價值」能夠由「單價」乘以「數量」獲得,說明「金額」是冗餘字段。可是,增長「總價值」這個冗餘字段,能夠提升查詢統計的速度,這就是以空間換時間的做法。合理的冗餘能夠分散數據量大的表的併發壓力,也能夠加快特殊查詢的速度,冗餘字段能夠有效減小數據庫表的鏈接,提升效率。
其中"總價值"就是一個計算列,在數據庫中有兩種類型:數據列和計算列,數據列就是須要咱們手動或者程序給予賦值的列,計算列是源於表中其餘的數據計算得來,好比這裏的"總價值"
在SQL中建立計算列:
create table goods(
id int auto_increment not null,
c1 int,
c2 int,
c3 int as (c1+c2), //這個就是計算列啦
primary key(id)
)
(3)索引的設計:
表優化的重要途徑,好比百萬級別的表沒有索引,註定卡死。
(4)主鍵和外鍵的必要性(實際項目開發的重要取捨)
概述:
主鍵與外鍵的設計,在全局數據庫的設計中,佔有重要地位。 由於:主鍵是實體的抽象,主鍵與外鍵的配對,表示實體之間的鏈接。
主鍵:
根據第二範式,須要有一個字段去標識這條記錄,主鍵無疑是最好的標識,可是不少表也不必定須要主鍵,可是對於數據量大,查詢頻繁的數據庫表,必定要有主鍵,主鍵能夠增長效率、防止重複等優勢。
主鍵的選擇也比較重要,通常選擇總的長度小的鍵,小的鍵的比較速度快,同時小的鍵可使主鍵的B樹結構的層次更少。
主鍵的選擇還要注意組合主鍵的字段次序,對於組合主鍵來講,不一樣的字段次序的主鍵的性能差異可能會很大,通常應該選擇重複率低、單獨或者組合查詢可能性大的字段放在前面。
外鍵:
外鍵做爲數據庫對象,不少人認爲麻煩而不用,實際上,外鍵在大部分狀況下是頗有用的,理由是:外鍵是最高效的一致性維護方法。
數據庫的一致性要求,依次能夠用外鍵、CHECK約束、規則約束、觸發器、客戶端程序,通常認爲,離數據越近的方法效率越高。可是!!!要謹慎使用級聯刪除和級聯更新,由於級聯刪除和級聯更新有些突破了傳統的關於外鍵的定義,功能有點太過強大,使用前必須肯定本身已經把握好其功能範圍,不然,級聯刪除和級聯更新可能讓你的數據莫名其妙的被修改或者丟失。從性能看級聯刪除和級聯更新是比其餘方法更高效的方法。
實際項目中的主外鍵取捨設計:(在性能和可擴展性之間尋求平衡)
邊緣模塊指的是小功能不經常使用需求不多再改的模塊;中心模塊是指關聯的東西太多的模塊、是不少表的主表;物理鍵指的是在表創建主外鍵關聯,邏輯主外鍵指的是利用字段去實現邏輯主外鍵關聯;熱點模塊指的是需求常常要改的模塊
大型系統:
針對性能要求不高,安全要求高的模塊,推薦使用物理主外鍵關聯;針對性能要求高、安全本身控制的模塊,推薦不用物理外鍵;
針對中心模塊和其餘模塊的聯繫,推薦使用物理主外鍵。
針對熱點模塊,必須使用邏輯主外鍵
針對邊緣模塊,推薦使用物理主外鍵
小系統:
隨便你啦,也就是20張表如下的系統。邏輯不復雜都無所謂啦,不過推薦仍是使用外鍵。
注意:
不用外鍵而用程序控制數據一致性和完整性時,應該寫一層來保證,而後個個應用經過這個層來訪問數據庫。
外鍵是有性能問題的,不能過度追求。
(5)存儲過程、視圖、函數的適當使用 :
不少人習慣將複雜操做都放在應用程序層,但若是你要優化數據訪問性能,將SQL代碼移植到數據庫上(使用存儲過程,視圖,函數和觸發器)也是一個很大的改進緣由以下:
1)存儲過程減小了網絡傳輸、處理及存儲的工做量,且通過編譯和優化,執行速度快,易於維護,且表的結構改變時,不影響客戶端的應用程序
2)使用存儲過程,視圖,函數有助於減小應用程序中SQL複製的弊端,由於如今只在一個地方集中處理SQL
3)使用數據庫對象實現全部的TSQL有助於分析TSQL的性能問題,同時有助於你集中管理TSQL代碼,更好的重構TSQL代碼。
(6)傳說中的‘三少原則’:
1)數據庫的表越少越好
2)表的字段越少越好
3)字段中的組合主鍵、組合索引越少越好
這裏的少是相對的,是減小數據冗餘的重要設計理念而已。
實際上,咱們爲了減小單表查詢壓力,會把去分表,從而分發記錄量,避免一個超級表的誕生。
(7)分割你的表,減少表尺寸
若是你發現某個表的記錄太多,例如超過一千萬條,則要對該表進行水平分割。水平分割的作法是,以該表主鍵的某個值爲界線,將該表的記錄水平分割爲兩個表。
若是你若發現某個表的字段太多,例如超過八十個,則垂直分割該表,將原來的一個表分解爲兩個表
(8)字段設計原則:
字段是數據庫最基本的單位,其設計對性能的影響是很大的。須要注意以下:
1)數據類型儘可能用數字型,數字型的比較比字符型的快不少。
2)數據類型儘可能小,這裏的儘可能小是指在知足能夠預見的將來需求的前提下的。
3)儘可能不要容許NULL,除非必要,能夠用NOT NULL+DEFAULT代替。
NULL 類型比較特殊,SQL 難優化。雖然 MySQL NULL類型和 Oracle 的NULL 有差別,會進入索引中,但若是是一個組合索引,那麼這個NULL 類型的字段會極大影響整個索引的效率。此外,NULL 在索引中的處理也是特殊的,也會佔用額外的存放空間。
4)少用TEXT和IMAGE,二進制字段的讀寫是比較慢的,並且,讀取的方法也很少,大部分狀況下最好不用。
5)自增字段要慎用,不利於數據遷移