一、范式
碼是數據系統中的基本概念。所為碼就是能唯一標識實體的屬性。
第一范式(1NF):
MySQL
- 數據庫表中的字段都是單一屬性的,不可再分。這個單一屬性由基本數據類型構成
包括整數型、實數、字符型、邏輯型、日期型。 - 在當前的任何DBMS中,傻瓜也不可能做出不符合第一范式的數據庫。
- 因為這些DBMS不允許你把數據庫表的一列再分為二列或多列。



第二范式(2NF):
2NF在1NF的基礎上,消除了非主屬性對于碼的部分函數依賴。
系名 → 系主任
學號 → 系主任
(學號,課名) → 分數

對于(學號,課名) → 姓名,有 學號 → 姓名,存在非主屬性 姓名 對碼(學號,課名)的部分函數依賴。
對于(學號,課名) → 系名,有 學號 → 系名,存在非主屬性 系名 對碼(學號,課名)的部分函數依賴。
對于(學號,課名) → 系主任,有 學號 → 系主任,存在非主屬性 對碼(學號,課名)的部分函數依賴

第三范式(3NF):
3NF在2NF的基礎上,消除了非主屬性對于碼的傳遞函數依賴。也就是說,如果存在非主屬性對于碼的傳遞函數依賴,則不符合3NF的要求。


由此可見,符合3NF要求的數據庫設計,基本上解決了數據冗余過大,插入異常、修改異常、刪除異常的問題。當然,在實際中,往往為了性能上或者應對擴展的需要,經常做到2NF或者1NF。
二、通常情況下選擇正確存儲數據的最小類型數據
查看表空間的命令
show table status like 't1' \G
以下兩個屬性
** Data_length **
** Data_free **
表示表空間的大小
查看表空間的命令
SELECT CONCAT(ROUND(SUM(data_length)/(1024*1024*1024), 2), ' GB') AS 'Total Data Size'
FROM information_schema.TABLES WHERE table_schema LIKE 'database';
三、手機號應該用什么類型的存儲
int 的最大值為21億, 2**31,所以并不能保存手機號的百億位數據。
mysql> create table big_int_test ( int_01 int(11), b_01 bigint(11), v_01 varchar(11));
Query OK, 0 rows affected (0.03 sec)
mysql> show create table big_int_test;
+--------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+--------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| big_int_test | CREATE TABLE `big_int_test` (
`int_01` int(11) DEFAULT NULL,
`b_01` bigint(11) DEFAULT NULL,
`v_01` varchar(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+--------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> insert into big_int_test (int_01, b_01, v_01) value (13522221111, 13522221111,13522221111);
ERROR 1264 (22003): Out of range value for column 'int_01' at row 1
mysql> insert into big_int_test (b_01, v_01) value (13522221111,13522221111);
Query OK, 1 row affected (0.00 sec)
mysql>
四、盡量避免使用NULL
null 是一個未知的值
null 和任何值都不相等,包括它自己
mysql> select null >0, null < 0 from dual;
+---------+----------+
| null >0 | null < 0 |
+---------+----------+
| NULL | NULL |
+---------+----------+
1 row in set (0.01 sec)
mysql> select null = null from dual;
+-------------+
| null = null |
+-------------+
| NULL |
+-------------+
1 row in set (0.07 sec)
- 將某個值與Null進行比較的正確方法是使用is關鍵字,以及is not 操作符
mysql> select * from areas where id in (3144, 3143, null, 3133);
+------+--------+--------------------------------+--------+
| id | areaid | name | cityid |
+------+--------+--------------------------------+--------+
| 3133 | 654226 | 和布克賽爾蒙古自治縣 | 654200 |
| 3143 | 659003 | 圖木舒克市 | 659000 |
| 3144 | 659004 | 五家渠市 | 659000 |
+------+--------+--------------------------------+--------+
3 rows in set (0.02 sec)
select * from areas where id = 3144 or id = 3143 or id =3133 or id = null;
mysql> select * from areas where id not in (3144, 3143, null, 3133);
Empty set (0.00 sec)
轉換成 select * from areas where id != 3144 and id != 3143 and id !=3133 and id != null;
-
Null 與排序
在排序時,null值被認為是最大的,在降序排序時會讓你非常頭大,因為null值排在了最前面。解決方法是使用 coalesce 進行
– 在輸出時將 null 轉換為 0 :
select name, coalesce(points, 0)
from users
order by 2 desc;
– 輸出時保留 null, 但排序時轉換為 0 :
select name, points
from users
order by coalesce(points, 0) desc;
http://www.codeceo.com/article/sql-null.html
五、DATETIME 和 TIMESTAMP 的區別
DATETIME:
- 占用 8 個字節
- 這個類型能保存大范圍的值,從 1001 年到 9999 年,精度為秒。
- 封存到 'YYYYMMDDHHMMSS' 的整數中,與時區無關。
TIMESTAMP:
- 占用4個字節
- 保存了1970.1.1 午夜到現在的秒數,它和UNIX時間戳相同。
- 只能表示從 1970 年到 2038 年。
- FROM_UNIXTIME() UNIX_TIMESTAMP() 函數把時間戳和日期相互轉換。
- 存儲時對當前的時區進行轉換,檢索時在轉換回當前的時區。
- 如果插入時沒有指定第一個TIMESTAMP 列的值,MySQL則設置這個列的值為當前時間。在插入一行記錄時,MySQL默認也會更新第一個TIMESTAMP的列(除非在UPDATE語句中明確指定的值)。
- TIMESTAMP列的默認值為NOT NULL 這和其他數據類型不一樣。
六、數據庫中價格應該怎么存的,為什么不用浮點數,如何證明浮點數不精確
mysql> CREATE TABLE test (c1 float(10,2),c2 decimal(10,2));
Query OK, 0 rows affected (0.29 sec)
mysql> insert into test values(131072.32,131072.32);
Query OK, 1 row affected (0.07 sec)
mysql> select * from test;
+-----------+-----------+
| c1 | c2 |
+-----------+-----------+
| 131072.31 | 131072.32 |
+-----------+-----------+
1 row in set (0.00 sec)
從上面的例子中我們看到c1列的值由131072.32變成了131072.31,這就是浮點數的不精確性造成的。
在MySQL中float、double(或real)是浮點數,decimal(或numberic)是定點數。
>> (10015.8*100.0).to_i
=> 1001579
>> 10015.8*100.0
=> 1001579.9999999999
>> 1001580.0.to_i
=> 1001580
今后關于浮點數和定點數的應用中,大家要記住以下幾點:
浮點數存在誤差問題。
對貨幣等對精度敏感的數據,應該用定點數表示或存儲;也可以用BIGINT 代替DECIMAL,將需要存儲的貨幣單位根據最小的位數乘以相應的倍數即可。假設要存儲財務數據精確到萬分之一分,則可以把所有金額乘以一百萬。
編程中,如果用到浮點數,要特別注意誤差問題,并盡量避免做浮點數比較。
要注意浮點數中一些特殊值的處理。
為MySQL選擇合適的數據類型
浮點數結構詳解
浮點數的二進制表示
浮點數的二進制表示學習筆記
七、int 后面的數值和 varchar 后面的數值的區別
MySQL 可以為整數類型指定寬度,例如INT(11), 對大多數應用來說是沒有意義的。它不會限制值的合法范圍,只是規定了MySQL 的一些交互工具,用來顯示字符的個數。對于存儲和計算來說,INT(1) 和 INT(20) 是相同的
但是對于varchar 類型來說,其后面顯示的數值,表示其可以插入多少個字符串。
mysql> create table int_test ( int_01 int(1), int_02 int(20));
Query OK, 0 rows affected (0.14 sec)
mysql> insert into int_test (int_01, int_02) value ( 123456, 123456);
Query OK, 1 row affected (0.04 sec)
mysql> select * from int_test;
+--------+--------+
| int_01 | int_02 |
+--------+--------+
| 123456 | 123456 |
+--------+--------+
1 row in set (0.02 sec)
mysql> create table varchar_test ( varchar_01 varchar(5), varchar_02 varchar(10));
Query OK, 0 rows affected (0.04 sec)
mysql> insert into varchar_test (varchar_01, varchar_02) value ("11111", "1111111");
Query OK, 1 row affected (0.01 sec)
mysql> insert into varchar_test (varchar_01, varchar_02) value ("111112", "1111111");
ERROR 1406 (22001): Data too long for column 'varchar_01' at row 1
mysql>
八、varchar 和 char 的區別
VARCHAR:
- VARCHAR類型用于存儲可變長字符串,是最常見的字符串數據類型。
它比定長類型更節省空間,因為它僅使用必要的空間 - VARCHAR需要使用1-2個額外的字節記錄字符串的長度。
如果列的最大長度小于或等于255個字節,則只使用1個字節來表示長度,否則使用2個字節。
所以 VARCHAR(1000) 的列則需要1002個字節。 - 在5.0或更好版本,MySQL在存儲和檢索時會保留末尾空格。
- InnoDB則更靈活,它可以把過長的VARCHAR存儲為BLOB
CHAR:
- CHAR 類型是定長的,MySQL總是根據定義的字符串長度分配足夠的空間。
- CHAR適合存儲很短的字符串,或者所有值都很接近的一個長度。例如
CHAR就非常合適存儲密碼的MD5值,因為這個是定長的。 - 對于經常變更的數據,CHAR也比VARCHAR
更好,因為定長的CHAR類型不容易產生碎片 - 對于非常短的列,CHAR比VARCHAR在存儲空間上也更有效率。例如用
CHAR(1)
來存儲只有Y和N的只,如果采用單字節字符集只需要一個字節,但是
VARCHAR(1) 卻需要兩個字節,因為還有一個用來記錄長度。
CREATE TABLE char_test(
char_col1 CHAR(1),
char_col2 CHAR(10),
varchar_col1 CHAR(1),
varchar_col2 CHAR(10)) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO char_test VALUES ('Y', 'string1', "Y", "string1"), ('Y', ' string1', "Y", " string1"), ('Y', 'string1 ', "Y", "string1 ");
SELECT LENGTH(char_col1), LENGTH(varchar_col1), CONCAT("'", char_col2, "'") FROM char_test;
SELECT LENGTH(char_col1), LENGTH(varchar_col1), CONCAT("'", varchar_col2, "'") FROM char_test;
九、varchar 最大的存儲空間
字符是指計算機中使用的字母、數字、漢字和符號。
字節是計算機信息技術中存儲容量的一種計量單位,也表示一些計算機編程語言中的數據類型和語言字符。
UTF-8 一個漢字=3個字節,英文是一個字節
GBK 一個漢字=2個字節,英文是一個字節
由于MySQL的記錄行長度是有限制的,不是無限長的,這個長度是64K,即65535個字節,對所有的表都是一樣的。
在UTF-8狀態下,漢字最多可以存 21844個字符串, 英文也為 21844個字符串。
在GBK狀態下,漢字最多可以存 32766個字符串,英文也為 32766個字符串。
varchar(n) 其中n表示字符,無論漢字和英文,MySQL都能存入n個字符,但實際存入其中的字節長度有所區別。
- MySQL對于變長類型的字段會有1-2個字節來保存字符長度。
- 當字符數小于等于255時,MySQL只用1個字節來記錄,因為2的8次方減1只能存到255。
當字符數多余255時,就得用2個字節來存長度了。 - 在UTF-8狀態下的varchar,最大只能到 (65535 - 2) / 3 = 21844 余 1。
- 在GBK狀態下的varchar, 最大只能到 (65535 - 2) / 2 = 32766 余 1。
MySQL 數據庫 varchar 到底可以存多少個漢字,多少個英文呢?我們來搞搞清楚
十、數據庫碎片是什么東西,有什么影響
MySQL具有相當多不同種類的存儲引擎來實現列表中的數據存儲功能。每當MySQL從你的
列表中刪除了一行內容,該段空間就會被留空。而在一段時間內的大量刪除操作,
會使這種留空的空間變得比存儲列表內容所使用的空間更大。
當MySQL對數據進行掃描時,它掃描的對象實際是列表的容量需要求的上限
也就是數據被寫入的區域中處于峰值位置的部分。如果進行新的插入操作,
MySQL將嘗試利用這些留空的區域,但仍然無法將其徹底占用。
這就是為什么不建議刪除數據庫的數據。但是你可以使用
一個狀態來描述,描述該狀態是否可用
data_free 表示我們刪除后所產生的留空空間。
查看線上數據庫中Table
Infomation時發現有一個日志表數據大小和索引大小有915M,但實際上行數只有92行。
該表需要頻繁插入并且會定時去刪掉舊的記錄。該表上建立了索引,所以應該是產生了大量的碎片
使用Optimize table
表明,優化后大小變為2.19M,少了很多,同時可以看出該表上的索引建的多余,因為插入操作比查詢操作要多
很多,而且查詢不多,查詢的數量也變小。
十一、什么情況下會使用binary 查詢
BINARY 不是函數,是類型轉換運算符,它用來強制它后面的字符串為一個二進制字符串,可以理解為在字符串比較的時候區分大小寫
因為有的MySQL特別是4.x以前以前的對于字符串檢索不準確,所以在檢索的時候加上
binary
MySQL 中 字符串和字符串的比較是不靠譜的,在二進制層次上比較靠譜
It an efficient have of comparing byte to byte instead of character to character
create table user_test(
id int(9) unsigned NOT NULL auto_increment,
username varchar(30) NOT NULL default '',
primary key (id)
);
INSERT INTO user_test (username) VALUES('美文');
INSERT INTO user_test (username) VALUES('美國項目');
INSERT INTO user_test (username) VALUES('李文');
INSERT INTO user_test (username) VALUES('老唐');
INSERT INTO user_test (username) VALUES('夢漂');
INSERT INTO user_test (username) VALUES('龍武');
INSERT INTO user_test (username) VALUES('夏');
mysql> select * from user_test;
+----+--------------+
| id | username |
+----+--------------+
| 1 | 美文 |
| 2 | 美國項目 |
| 3 | 李文 |
| 4 | 老唐 |
| 5 | 夢漂 |
| 6 | 龍武 |
| 7 | 夏 |
+----+--------------+
7 rows in set (0.00 sec)
mysql> select * from user_test where username = '夏';
+----+----------+
| id | username |
+----+----------+
| 7 | 夏 |
+----+----------+
1 row in set (0.00 sec)
mysql> select * from user_test where username = '夏 ';
+----+----------+
| id | username |
+----+----------+
| 7 | 夏 |
+----+----------+
1 row in set (0.00 sec)
mysql> select * from user_test where username = BINARY '夏';
+----+----------+
| id | username |
+----+----------+
| 7 | 夏 |
+----+----------+
1 row in set (0.00 sec)
mysql> select * from user_test where username = BINARY '夏 ';
Empty set (0.00 sec)
MySQL的binary解決MySQL數據大小寫敏感問題的方法
Why the 'BINARY' in a SELECT statement?
charset-binary-op
十二、VARCHAR(5) 和 VARCHAR(200)存儲上的區別,排序上的區別
- 相比較而言,varchar(5) 所占用的空間相對比較小。
- 在排序時,varchar(5) 所占用的空間相對比 varchar(200) 要少。
十三、1000w的數據,如果在ORDER BY 中使用到varchar(1000)這個列,并且查詢掃描整個表,為了排序就需要超過30GB的臨時表
最壞情況下的長度分配對于排序也是一樣的。
意思就是在排序的時候使用的是分配長度的最大長度。
1 GB = 1000 MB = 1 000 000 KB = 1 000 000 000 B
1 MB = 1 000KB = 1 000 000 B
3000 * 10 000 000 = 30 000 000 000 B = 30GB
十四、排序 使用 FIND_IN_SET 進行按順序排序
mysql> explain
-> select id, title
-> from happy_for_ni_deals t
-> where t.id in (1014694, 1014693, 1014697, 1014691)
-> order by FIND_IN_SET(t.id, '1014694,1014693,1014697,1014691');
+----+-------------+-------+-------+---------------+---------+---------+------+------+-----------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-----------------------------+
| 1 | SIMPLE | t | range | PRIMARY | PRIMARY | 4 | NULL | 4 | Using where; Using filesort |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-----------------------------+
1 row in set (0.01 sec)
4、
mysql> explain
-> select id, title
-> from happy_for_ni_deals t
-> where t.id in (1014694, 1014693, 1014697, 1014691);
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
| 1 | SIMPLE | t | range | PRIMARY | PRIMARY | 4 | NULL | 4 | Using where |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
1 row in set (0.01 sec)
十五、通用的設計實踐,在“查找表”時采用整數主鍵而避免采用基于字符串的值進行關聯
由于根據字符串的關聯,在查找排序的時候,需要占用更多的空間,消耗更多的資源。
theory-of-mysql-index
十六、選擇標識符
標識列選擇數據類型時,應該選擇跟關聯表中的對應列一樣的類型。
混用不同數據類型可能導致性能問題,及時沒有性能影響,在比較操作時飲食類型轉換也可以能導致很難發現的錯誤。
對于完全“隨機”的字符串也需要多加注意,例如MD5(),SHA1()或UUID()產生的字符串,
這些函數生成的新值會任意分布在很大的空間內,這會導致INSERT以及一些SELECT語句變得很慢
使用UUID()并非一無是處,在分布式數據庫中,使用UUID()是一個不錯的選擇。
下面例子中關聯查詢的時候,使用一個字符串和int型主鍵ID進行串聯。會導致索引使用不上。
mysql> EXPLAIN SELECT *
-> FROM happy_for_ni_tb_shops AS t
-> INNER JOIN candidate_happy_for_ni_deals AS c_d
-> WHERE t.id = c_d.cid \G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t
type: ALL
possible_keys: PRIMARY
key: NULL
key_len: NULL
ref: NULL
rows: 1
Extra: NULL
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: c_d
type: ALL
possible_keys: idx_cid
key: NULL
key_len: NULL
ref: NULL
rows: 1
Extra: Range checked for each record (index map: 0x2)
2 rows in set (0.07 sec)
十七、特殊類型數據
MySQL 提供INET_ATON()和INET_NEOA()函數在IP地址上進行轉換
mysql> SELECT INET_ATON('209.207.224.40'), INET_NTOA(3520061480) FROM dual;
+-----------------------------+-----------------------+
| INET_ATON('209.207.224.40') | INET_NTOA(3520061480) |
+-----------------------------+-----------------------+
| 3520061480 | 209.207.224.40 |
+-----------------------------+-----------------------+
1 row in set (0.02 sec)
十八、JOIN 驅動表
inner join驅動順序由優化器自己制定,如果優化器選擇有誤可以使用straight_join 自己指定驅動順序以達到優化的目的。
left join驅動順序是固定的,left join左邊的表為驅動表,右邊為匹配表,right join則剛好相反。
存在group by或者order by 子句的關聯查詢中,如果引用的字段是驅動表的字段那么分組或者排序是可以使用到索引的。但是如果引用的是其他匹配表的字段,那么分組或者排序動作則無法使用索引。
內連接就是檢索出與連接條件完全匹配的數據行;而外連接則保留了所有驅動表的數據,匹配表中無法匹配的數據則以null輸出。
驅動表的結果集,越小越好。
** 有一點需要注意的是:我們前面說要盡量縮小驅動表的結果集,
但是這里卻選擇了一個超大表來做為驅動表,
但其實兩者是在一定程度上是不相悖的。
因為縮小驅動表結果集實際上是為了減少nested loop的次數,
而我們修改了驅動表以后,排序動作直接在索引完成,
優化器直接按照logintime desc的順序進行掃描,
只要返回記錄數等于100時就可以完成查詢操作,
實際的nested loop次數是很少的。而未修改前的SQL之所以需要執行那么久是因為它的排序無法使用索引,
所以必須先等待臨時結果集生成,而且是全量生成,也就是說需要在約3000萬行的表里進行10萬次查詢,
然后把結果聚集到一張臨時表再進行排序選出100行記錄。**
十九、從父表冗余一些數據到子表的理由是排序的需要。
工單87334
happy_for_ni_deals 表和 happy_for_ni_tb_shops 表的關聯查詢,但是最后使用了 happy_for_ni_tb_shops.grade 做desc
EXPLAIN SELECT `happy_for_ni_deals`.*
FROM `happy_for_ni_deals`
INNER JOIN `happy_for_ni_tb_shops`
ON `happy_for_ni_tb_shops`.`id` = `happy_for_ni_deals`.`happy_for_ni_tb_shop_id`
LEFT JOIN candidate_happy_for_ni_deals
on happy_for_ni_deals.candidate_happy_for_ni_deal_id = candidate_happy_for_ni_deals.id
LEFT JOIN happy_for_ni_deal_infos
on happy_for_ni_deal_infos.happy_for_ni_deal_id = happy_for_ni_deals.id
WHERE ( happy_for_ni_deals.bg_tag_id > 0 )
AND (happy_for_ni_deals.complete_status = 1)
ORDER BY happy_for_ni_tb_shops.grade DESC, happy_for_ni_deals.id DESC LIMIT 20 OFFSET 0
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: happy_for_ni_deals
type: range
possible_keys: index_happy_for_ni_deals_on_happy_for_ni_tb_shop_id_and_id,idx_bg_tag_pub_beg,idx_bg_tag_pub_end
key: idx_bg_tag_pub_beg
key_len: 4
ref: NULL
rows: 1
Extra: Using index condition; Using where; Using temporary; Using filesort <- 壞味道,temporary && filesort
將happy_for_ni_tb_shops
表上的 grade
賦值到 happy_for_ni_deals
上,在代碼中將happy_for_ni_tb_shops
表 grade
邏輯也同步到happy_for_ni_deals
上
然后加索引。這樣排序,會快一些。
ALTER TABLE happy_for_ni_deals ADD COLUMN grade int(11) not null;
ALTER TABLE happy_for_ni_deals ADD INDEX `idx_of_grade` (grade, id);
EXPLAIN SELECT `happy_for_ni_deals`.*
FROM `happy_for_ni_deals`
INNER JOIN `happy_for_ni_tb_shops`
ON `happy_for_ni_tb_shops`.`id` = `happy_for_ni_deals`.`happy_for_ni_tb_shop_id`
LEFT JOIN candidate_happy_for_ni_deals
on happy_for_ni_deals.candidate_happy_for_ni_deal_id = candidate_happy_for_ni_deals.id
LEFT JOIN happy_for_ni_deal_infos
on happy_for_ni_deal_infos.happy_for_ni_deal_id = happy_for_ni_deals.id
WHERE ( happy_for_ni_deals.bg_tag_id > 0 )
AND (happy_for_ni_deals.complete_status = 1)
ORDER BY happy_for_ni_deals.grade DESC, happy_for_ni_deals.id DESC LIMIT 20 OFFSET 0
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: happy_for_ni_deals
type: ref
possible_keys: index_happy_for_ni_deals_on_happy_for_ni_tb_shop_id_and_id,idx_bg_tag_pub_beg,idx_bg_tag_pub_end
key: index_happy_for_ni_deals_on_happy_for_ni_tb_shop_id_and_id
key_len: 4
ref: tao800_test9.happy_for_ni_tb_shops.id
rows: 1
Extra: Using where
二十、MySQL 視圖
什么是視圖?視圖是由查詢結果形成的一張虛擬表。
什么時候要用到視圖?如果某個查詢結果出現的非常頻繁,也就是說,要經常拿到這個查詢結果來做子查詢。
-
視圖的創建語法:
create view 視圖名 as select 語句;
使用視圖有什么好處?
** 1、簡化查詢語句。將經常用到的子查詢做成視圖。**
** 2、可以進行權限控制。 視圖里只開放部分數據列。**
** 3、大數據分表時可以用到。**
二十一、更快地讀,更慢的寫。高性能數據庫的設計
為了提升查詢的速度,經常會需要見一些額外的索引,增加冗余列,甚至是創建緩存表和匯總表。這些方法會增加寫查詢的負擔,也需要額外的維護任務,但是在設計高性能數據庫時,這些都是最常見的技巧。雖然寫操作變得更慢了,但更顯著地提高了讀操作的性能
二十二、加快ALTER TBALE 操作的速度
ALTER TABLE操作需要花費數小時時間甚至數天才能完成。
大部分ALTER TABLE操作將導致MySQL服務中斷。
不是所有的ALTER TABLE操作都會引起表重建。
ALTER TABLE sakila.film MODIFY COLUMN rental_duration TINYINT(3) NOT NULL DEFAULT 5;
所有的MODIFY COLUMN 操作都將導致表重建。所以這種操作是很慢的。
ALTER TABLE sakila.film ALTER COLUMN rental_duration TINYINT(3) NOT NULL DEFAULT 5;
這個語句會直接修改.frm 文件而不設計表數據。所以,這個操作是非常快的。
ALTER TABLE 允許使用 ALTER COLUMN、MODIFY COLUMN和CHANGE COLUMN語句修改列,這三種操作都是不一樣的。
二十三、給大表添加索引或字段該如何操作
在構建索引的工作被延遲到數據完全載入以后,這個時候已經可以通過排序來構建索引了。這樣做會快很多,并且使得索引樹的碎片更少、更緊湊。
1、clone 新表
2、先刪除所有的非唯一性索引,然后增加新的列,最后重新創建刪除掉的索引。
3、交換新表和舊表