MYSQL學習筆記——數據庫範式及MYSQL優化總體思路

1、數據庫範式                                                                                html

      爲了創建冗餘較小、結構合理的數據庫,設計數據庫時必須遵循必定的規則。在關係型數據庫中這種規則就稱爲範式。範式是符合某一種設計要求的總結。要想設計一個結構合理的關係型數據庫,必須知足必定的範式。mysql

1.一、第一範式(1NF:每一列不可包含多個值)
      所謂第一範式(1NF)是指數據庫表的每一列都是不可分割的基本數據項,同一列中不能有多個值,即實體中的某個屬性不能有多個值或者不能有重複的屬性。若是出現重複的屬性,就可能須要定義一個新的實體,新的實體由重複的屬性構成,新實體與原實體之間爲一對多關係。在第一範式(1NF)中表的每一行只包含一個實例的信息。
      在任何一個關係數據庫中,第一範式(1NF)是對關係模式的基本要求,不知足第一範式(1NF)的數據庫就不是關係數據庫。sql

1.二、第二範式(2NF:非主屬性部分依賴於主關鍵字)
      第二範式(2NF)是在第一範式(1NF)的基礎上創建起來的,即知足第二範式(2NF)必須先知足第一範式(1NF)。第二範式(2NF)要求數據庫表中的每一個實例或行必須能夠被惟一地區分。爲實現區分一般須要爲表加上一個列,以存儲各個實例的惟一標識。
      第二範式(2NF)要求實體的屬性徹底依賴於主關鍵字。所謂徹底依賴是指不能存在僅依賴主關鍵字一部分的屬性,若是存在,那麼這個屬性和主關鍵字的這一部分應該分離出來造成一個新的實體,新實體與原實體之間是一對多的關係。爲實現區分一般須要爲表加上一個列,以存儲各個實例的惟一標識。簡而言之,第二範式就是非主屬性部分依賴於主關鍵字。數據庫

1.三、第三範式(3NF:屬性不依賴於其它非主屬性)
      知足第三範式(3NF)必須先知足第二範式(2NF)。簡而言之,第三範式(3NF)要求一個數據庫表中不包含已在其它表中已包含的非主關鍵字信息。例如,存在一個部門信息表,其中每一個部門有部門編號、部門名稱、部門簡介等信息。那麼在員工信息表中列出部門編號後就不能再將部門名稱、部門簡介等與部門有關的信息再加入員工信息表中。若是不存在部門信息表,則根據第三範式(3NF)也應該構建它,不然就會有大量的數據冗餘。簡而言之,第三範式就是屬性不依賴於其它非主屬性。緩存

1.四、反三範式(反3NF:爲了性能,增長冗餘)性能

      3NF提出目的是爲了下降冗餘,減小沒必要要的存儲,這對於存儲設備昂貴的過去是頗有必要的,可是隨着存儲設備的降價以及人們對性能的不斷提升,又有人提出反三範式。優化

      所謂反三範式就是爲了性能,增長冗餘。以部門信息表爲例,每一個部門有部門編號、部門名稱、部門簡介等信息,按照3NF的要求,爲了不冗餘,咱們在員工表中就不該該加入部門名稱、部門簡介等部門有關信息,帶來的代價是每次都要查詢兩次數據庫。反三範式容許咱們冗餘重要信息到員工表中,例如部門名稱,這樣咱們每次取員工信息時就能直接取出部門名稱,不須要查詢兩次數據庫,提高了程序性能。this

2、MYSQL優化總體思路                                                                     spa

      MYSQL優化首先應該定位問題,可能致使MYSQL低性能的緣由有:業務邏輯過多的查詢、表結構不合理、sql語句優化以及硬件優化,從優化效果來看,這四個優化點的優化效果依次下降:理清業務邏輯可以幫助咱們避免沒必要要的查詢,合理設計表結構也能幫助咱們少查詢數據庫。對於sql語句優化,咱們能夠先使用慢查詢日誌(慢查詢日誌的使用:http://www.cnblogs.com/timlearn/p/4052523.html)定位慢查詢,而後針對該查詢進行優化,最多見且最有效的優化範式就是增長合理的索引,這個在上篇博客http://www.cnblogs.com/timlearn/p/4055512.html已經講解,本篇博客將講解其餘一些優化手段或者注意點。設計

2.一、謹慎使用TEXT/BLOB類型

      當列類型是TEXT或者BLOB時,咱們應該特別注意,由於當選擇的字段有 text/blob 類型的時候,沒法建立內存表,只能建立硬盤臨時表,而硬盤臨時表的性能比內存表的性能差,因此若是非要使用TEXT/BLOB類型,應該單獨建表,不要把TEXT/BLOB類型與核心屬性混合在一張表中。對此,作一個實驗以下:

//建立數據表
create table t1 (
num int,
intro text(1000)
);

//插入數據
insert into t1 values (3,'this is USA') , (4,'China');

//查詢臨時表建立狀況
//注意,這裏Created_tmp_disk_tables=4,Created_tmp_tables=10
mysql> show status like '%tmp%';
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| Created_tmp_disk_tables | 4     |
| Created_tmp_files       | 9     |
| Created_tmp_tables      | 10    |
+-------------------------+-------+

//使用group by查詢數據
mysql> select * from t1 group by num;
+------+-------------+
| num  | intro       |
+------+-------------+
|    3 | this is USA |
|    4 | China       |
+------+-------------+
2 rows in set (0.05 sec)

//再次查詢臨時表建立狀況
//如今,Created_tmp_disk_tables=5,Created_tmp_tables=11
mysql> show status like '%tmp%';
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| Created_tmp_disk_tables | 5     |
| Created_tmp_files       | 9     |
| Created_tmp_tables      | 11    |
+-------------------------+-------+

2.二、慎用子查詢

      幾乎全部子查詢均可以改寫爲鏈接查詢,有時候,鏈接查詢的效率要比子查詢高,因此把子查詢改寫成鏈接查詢是一個不錯的注意。若是一條使用子查詢的select語句執行時間過長,那麼就應該嘗試把它改寫爲鏈接查詢,看他是否是執行的更好。  

      下例中,咱們使用子查詢耗時5s,清空緩存後,改用鏈接查詢,只須要0.02s:

//子查詢耗時5s
mysql> select * from emp where ename in (select ename from ename);
+--------+--------+----------+-----+------------+---------+--------+--------+
| empno  | ename  | job      | mgr | hiredate   | sal     | comm   | deptno |
+--------+--------+----------+-----+------------+---------+--------+--------+
|      2 | AsoqNR | SALESMAN |   1 | 2014-10-29 | 2000.00 | 400.00 |    466 |
|      3 | cAuvTj | SALESMAN |   1 | 2014-10-29 | 2000.00 | 400.00 |    155 |
|      6 | oOeekL | SALESMAN |   1 | 2014-10-29 | 2000.00 | 400.00 |     13 |
|      9 | MFPixN | SALESMAN |   1 | 2014-10-29 | 2000.00 | 400.00 |    225 |
| 103219 | MfpiXn | SALESMAN |   1 | 2014-10-29 | 2000.00 | 400.00 |    251 |
| 318098 | mFpIxn | SALESMAN |   1 | 2014-10-29 | 2000.00 | 400.00 |    480 |
| 333225 | ASOqnr | SALESMAN |   1 | 2014-10-29 | 2000.00 | 400.00 |    466 |
| 443919 | AsoqNR | SALESMAN |   1 | 2014-10-29 | 2000.00 | 400.00 |    446 |
| 458077 | OoeEKL | SALESMAN |   1 | 2014-10-29 | 2000.00 | 400.00 |    266 |
| 473649 | AsoqNR | SALESMAN |   1 | 2014-10-29 | 2000.00 | 400.00 |    448 |
| 769138 | CAUVTJ | SALESMAN |   1 | 2014-10-29 | 2000.00 | 400.00 |    252 |
| 826307 | MFPixN | SALESMAN |   1 | 2014-10-29 | 2000.00 | 400.00 |    242 |
+--------+--------+----------+-----+------------+---------+--------+--------+
12 rows in set (5.04 sec)

//清空緩存
reset query cache;

//鏈接查詢耗時0.02s
mysql> select emp.* from emp inner join ename on emp.ename=ename.ename;
+--------+--------+----------+-----+------------+---------+--------+--------+
| empno  | ename  | job      | mgr | hiredate   | sal     | comm   | deptno |
+--------+--------+----------+-----+------------+---------+--------+--------+
|      6 | oOeekL | SALESMAN |   1 | 2014-10-29 | 2000.00 | 400.00 |     13 |
| 458077 | OoeEKL | SALESMAN |   1 | 2014-10-29 | 2000.00 | 400.00 |    266 |
|      9 | MFPixN | SALESMAN |   1 | 2014-10-29 | 2000.00 | 400.00 |    225 |
| 103219 | MfpiXn | SALESMAN |   1 | 2014-10-29 | 2000.00 | 400.00 |    251 |
| 318098 | mFpIxn | SALESMAN |   1 | 2014-10-29 | 2000.00 | 400.00 |    480 |
| 826307 | MFPixN | SALESMAN |   1 | 2014-10-29 | 2000.00 | 400.00 |    242 |
|      3 | cAuvTj | SALESMAN |   1 | 2014-10-29 | 2000.00 | 400.00 |    155 |
| 769138 | CAUVTJ | SALESMAN |   1 | 2014-10-29 | 2000.00 | 400.00 |    252 |
|      2 | AsoqNR | SALESMAN |   1 | 2014-10-29 | 2000.00 | 400.00 |    466 |
| 333225 | ASOqnr | SALESMAN |   1 | 2014-10-29 | 2000.00 | 400.00 |    466 |
| 443919 | AsoqNR | SALESMAN |   1 | 2014-10-29 | 2000.00 | 400.00 |    446 |
| 473649 | AsoqNR | SALESMAN |   1 | 2014-10-29 | 2000.00 | 400.00 |    448 |
+--------+--------+----------+-----+------------+---------+--------+--------+
12 rows in set (0.02 sec)

 

2.三、在DISTINCT列上增長索引

      若是在distinct列上不增長索引,那麼mysql在查詢時將建立臨時表;若是咱們在該列上增長索引,能夠避免使用臨時表:

create table message(
	user_id int,
	group_id int
);

//插入10條數據
insert into message values(24,67),(15,76),(134,986),(6,98),(46,988),(13,2),(12,89),(17,34),(63,19),(92,74);

//沒加索引,extra中有using temporary
mysql> explain select distinct user_id from message where group_id=2\G;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: message
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 12
        Extra: Using where; Using temporary
1 row in set (0.01 sec)

//增長索引
create index KEY_GID_UID on message (group_id, user_id);

//加了索引以後,終於不用建立臨時表了
mysql> explain select distinct user_id from message where group_id=2\G;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: message
         type: ref
possible_keys: KEY_GID_UID
          key: KEY_GID_UID
      key_len: 5
          ref: const
         rows: 1
        Extra: Using where; Using index
1 row in set (0.00 sec)

2.四、在group by後面增長order by null

      在使用group by分組查詢時,默認分組後,還會排序,可能會下降速度。若是不須要排序,那麼能夠在group by後面增長order by null,這樣能夠避免分組後排序:

//注意,沒加order by null時,extra中有Using filesort
mysql> explain select * from emp group by deptno\G;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: emp
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 4296846
        Extra: Using temporary; Using filesort

//加了order by null後,extra中就沒有Using filesort了
mysql> explain select * from emp group by deptno order by null\G;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: emp
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 4354494
        Extra: Using temporary
1 row in set (0.00 sec)
相關文章
相關標籤/搜索