Mysql表設計需要注意的問題(轉)

Mysql表設計需要注意的問題

下面探討的數據庫爲MySQL 存儲引擎爲innodb因爲這是最常見的,使用最多的數據庫和引擎

什麼是頁分裂?

這是因爲聚簇索引採用的是平衡二叉樹算法,而且每個節點都保存了該主鍵所對應行的數據,假設插入數據的主鍵是自增長的,那麼根據二叉樹算法會很快的把該數據添加到某個節點下,而其他的節點不用動;但是如果插入的是不規則的數據,那麼每次插入都會改變二叉樹之前的數據狀態。從而導致了頁分裂。

爲什麼一定要設置一個主鍵

因爲就算你不主動設置一個主鍵,innodb也會幫你生成一個隱藏列,作爲自增主鍵來使用。所以,不管怎麼樣都會有自增主鍵,還不如自己指定一個,主鍵索引可以提高查詢效率。

主鍵使用自增還是UUID

主鍵肯定是用自增。innodb種的主鍵是聚族索引,如果主鍵是自增的,那麼每次插入的新數據都會順序添加到當前索引節點的後續位置,當一頁寫滿時,就會自動開啓第二頁,如果不是自增主鍵,那麼就可能在中間插入,就會引發頁的分裂,產生很多碎片,總之用自增主鍵性能更好。

UUID產生的索引文件更大,當數據量超過一百萬行時,主鍵查詢性能和索引文件大小都比UUID要更有效率和更小。

 

主鍵爲什麼不推薦有業務含義

1)因爲任何含有業務的列都有改變的可能性,主鍵一旦帶上業務含義,那麼主鍵就有可能發生改變。主鍵發生改變,該數據在磁盤上的存儲位置就會發生更改,有可能引發頁分裂,產生空間碎片。

2)帶有業務含義的主鍵不一定是順序自增的,就有可能導致後邊插入的數據主鍵一定比前面的大,如果出現後邊插入的數據主鍵比前面插入的小,就肯能引發頁分裂,產生空間碎片。

 

表示枚舉的字段爲什麼不用enum類型

首先,枚舉在MySQL中一般用tinyint類型,爲什麼不使用enum呢

1)ENUM類型,order by操作效率低,需要額外的操作

2)如果枚舉是數值,有陷阱

例如:表結構如下create table test(sex enum('0','1'))此時執行插入語句insert into test values(1) 查詢的結果爲0

只有這樣插入insert into test values('1')此時結果纔是1

 

貨幣字段用什麼類型

貨幣:如果貨幣單位是分,可以使用int類型,如果堅持用元的話,可以使用Decimal類型,不能使用float和double類型,因爲這兩個類型是以二進制存儲的,所有有一定的誤差。

例如:create table 't'('price' float(10,2) default null) engine=innodb default charset=utf8然後插入一條數據price=120.23,然後你會發現數據變爲120.25,精度失準。

 

時間字段用什麼類型

1)如果使用varchar,優點是顯示直觀,缺點是做時間比較運算的時候,需要使用str_to_date等函數把它轉化爲事件類型,你會發現這是無法命中索引的,數據量一大,這就是一個大坑。

2)如果使用timestamp類型,該類型爲4個字節,表示的時間範圍爲1970-01-01 到2038-01-19 也就是說2038年以後的時間不能使用timestamp存儲。該類型有一個優勢,就是自帶時區,如果修改了時區,該字段值會跟着改變。

3)dtatime類型,該類型爲8個字節,自己維護一個時間戳,表示範圍比timestamp大,因爲需要自己維護,所以不太方便。

 

爲什麼不直接存儲圖片、音頻、視頻等大容量內容

在實際生產中,我們都是在文件服務器上存真實文件,數據庫中保存對應的文件路勁即可,MySQL有存放大文件的類型,text和blob類型,但是基本不使用

1)MySQL內存臨時表不支持text、blob這樣的大數據類型,如果查詢中包含這樣的數據,在排序等操作時,就不能使用內存臨時表,必須使用磁盤臨時表,導致效率低下。

2)binlog內容太多,因爲數據內容過大,會導致binlog內容較多,而MySQL主從同步靠binlog進行同步,binlog太大就會導致主從同步效率問題。

 

字段爲什麼要定義爲NOT NULL

1)索引性能不好

2)查詢會出現一些不可預料的結果

我們都只是茫茫星辰中的一粒沙。

互聯網產品mysql數據庫設計注意事項

mysql數據庫性能不比oracle數據庫,所以設計上,和oracle有一些不同。下面總結一些互聯網產品的數據庫設計。

1.主鍵

主鍵可以使用bigint(20) unsigned也可以使用varchar,使用bigint,可以設置爲自增主鍵auto_increment。使用varchar,要生成主鍵。

2.gmt_create、gmt_modified

在TB所有表中都添加gmt_create、gmt_modified字段,都是datetime類型。gmt_create表示記錄創建時間,gmt_modified表示最近修改時間,如果記錄沒有修改,gmt_create和gmt_modified一致。

那麼,這兩個字段可以做什麼用呢?這兩個字段可以方便統計每天對某個表做了多少次的DML。另一種統計方式可以通過binlog。

查看昨天insert:

select * FROM  test_table  WHERE 
gmt_create < date_format(DATE(now()), '%Y-%m-%e %H-%i-%s') AND 
gmt_create >= date_format(DATE(date_add(now(), INTERVAL - 1 DAY)),'%Y-%m-%e %H-%i-%s');

查看昨天update:

select * FROM test_table WHERE gmt_create < date_format(DATE(now()), '%Y-%m-%e %H-%i-%s')
AND gmt_create >= date_format(DATE(date_add(now(), INTERVAL - 1 DAY)),'%Y-%m-%e %H-%i-%s')
AND date_add(gmt_create, INTERVAL - 1 DAY) != date_add(gmt_modified, INTERVAL - 1 DAY)

3.邏輯刪除 is_deleted

數據庫不做物理刪除,只做邏輯刪除,用is_deleted做邏輯刪除,如果刪除,則爲1,不刪除則爲0.is_deleted字段可以使用tinyint型。

4.禁止使用物理外鍵,使用邏輯外鍵

由於mysql性能不如oracle,外鍵還是一個比較消耗性能的東西,所以我們不要使用物理外鍵,就是我們在建表的時候,禁止使用foreign key。

例如表b記錄了表a的id,我們只需在表b中添加一列:aid就可以了,然後通過程序來控制外鍵關係。

5.命名規範

1.庫名,表名,字段名必須都用小寫字母,並用下劃線_分隔,並且見名知意,使用名詞。

原因如下:

a) MySQL有配置參數lower_case_table_names,不可動態更改,linux系統默認爲0,即庫表名以實際情況存儲,大小寫敏感。如果是1,以小寫存儲,大小寫不敏感。如果是2,以實際情況存儲,但以小寫比較。
b) 如果大小寫混合用,可能存在abc,Abc,ABC等多個表共存,容易導致混亂。
c) 字段名顯示區分大小寫,但實際使用不區分,即不可以建立兩個名字一樣但大小寫不一樣的字段。
d) 爲了統一規範, 庫名、表名、字段名使用小寫字母。

6.使用innoDB存儲引擎

5.5以後的默認引擘,支持事務,行級鎖,更好的恢復性,高併發下性能更好,對多核,大內存,ssd等硬件支持更好。

7.關於數據類型

a.存儲精確浮點數必須使用DECIMAL替代FLOAT和DOUBLE。

例如:對於金錢的存儲,使用decimal,或者以分爲單位,用bigint(20) unsigned。
b.使用UNSIGNED存儲非負數值。同樣的字節數,存儲的數值範圍更大。如tinyint 有符號爲 -128-127,無符號爲0-255
c.建議使用INT UNSIGNED存儲IPV4。
使用INT UNSIGNED而不是char(15)來存儲ipv4地址,通過MySQL函數inet_ntoa和inet_aton來進行轉化。Ipv6地址目前沒有轉化函數,需要使用DECIMAL或者兩個bigINT來存儲。例如:
SELECT INET_ATON('209.207.224.40');
3520061480
SELECT INET_NTOA(3520061480);
209.207.224.40

d.整形定義中不添加(4),比如使用INT,而不是INT(4)

注意數值類型括號後面的數字只是表示寬度而跟存儲範圍沒有關係,比如INT(3)默認顯示3位,空格補齊,超出時正常顯示,python、java客戶端等不具備這個功能。

e.使用短數據類型,比如取值範圍爲0-80時,使用TINYINT UNSIGNED。

f.不建議使用ENUM類型,使用TINYINT來代替。

 ENUM,有三個問題:添加新的值要做DDL,默認值問題(將一個非法值插入ENUM(也就是說,允許的值列之外的字符串),將插入空字符串以作爲特殊錯誤值),索引值問題(插入數字實際是插入索引對應的值)
實例:
drop table if exists t;
create table t(sex enum('0','1'));
insert into t values(1);
insert into t values('3');
select * from t;
+------+
| sex  |
+------+
| 0    |
|      |
+------+
2 rows in set (0.00 sec)
g.儘可能不使用TEXT、BLOB類型。
h.VARCHAR(N),N表示存的個數,比如:VARCHAR(10) 下面語句都可以成功:
update test set testvarchar='一二三四五六七八九十' where id=1
update test set testvarchar='1234567890' where id=1
update test set testvarchar='abcdefghij' where id=1
i.存儲年使用YEAR類型。
j.存儲日期使用DATE類型。
k.存儲時間(精確到秒)建議使用TIMESTAMP類型,因爲TIMESTAMP使用4字節,DATETIME使用8個字節。
l.將過大字段拆分到其他表中。
m.禁止在數據庫中使用VARBINARY、BLOB存儲圖片、文件等

8.適當建立索引

非唯一索引必須按照“idx_字段名稱_字段名稱[_字段名]”進行命名。
唯一索引必須按照“uniq_字段名稱_字段名稱[_字段名]”進行命名。
索引名稱必須使用小寫。
索引中的字段數建議不超過5個。
單張表的索引數量控制在5個以內。
不建議使用%前綴模糊查詢,例如LIKE “%weibo”。
合理創建聯合索引(避免冗餘),(a,b,c) 相當於 (a) 、(a,b) 、(a,b,c)。
唯一鍵由3個以下字段組成,並且字段都是整形時,使用唯一鍵作爲主鍵。
沒有唯一鍵或者唯一鍵不符合5中的條件時,使用自增id作爲主鍵。
唯一鍵不和主鍵重複。
索引字段的順序需要考慮字段值去重之後的個數,個數多的放在前面。
ORDER BY,GROUP BY,DISTINCT的字段需要添加在索引的後面。

原文: https://www.cnblogs.com/yjp372928571/p/11221667.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章