1.Order by與Group by優化
1.1Case1
????????employees表中建立了name,position和age索引,并且使用了order by age進行排序操作:
EXPLAIN SELECT * FROM employees WHERE name = 'LiLei' and position = 'dev' order by age
????????最終explain的結果發現使用了idx_name_age_position索引,并且Extra中顯示Using index condition,并不是Using filesort,所以執行該語句時,使用到了索引進行優化,而不是使用外部排序。但是需要注意的是,ORDER BY走索引的時候,不會體現到key_len中,只要Extra中不是Using Filesort,是Using Index就代表ORDER BY使用了索引。
????????分析:利用最左前綴法則,中間字段不能斷,因為查詢使用到了name索引,從key_len = 74也能看出,age索引列用在排序過程中,因為Extra字段里沒有using filesort,展示的時Using index。
1.2Case2
EXPLAIN SELECT * FROM employees WHERE name = 'LiLei' ORDER BY position;
????????這個SQL語句經過EXPLAIN進行分析,發現name確實是走索引了,但是Extra是Using Filesort,所以代表ORDER BY沒有走索引。
????????其主要原因是:Case1中,name確定時,由于age是有序的,所以是可以走索引的,Case2中,name確定時,position是沒有順序的,所以無法走索引,只能Using Filesort走外部排序。
1.3Case3
EXPLAIN SELECT * FROM employees WHERE name = 'LiLei' ORDER BY position,age
????????這個SQL語句經過EXPLAIN進行分析,發現name確實走索引了,但是Extra是Using Filesort,所以代表ORDER BY沒有走索引。
????????究其原因是因為建立聯合索引樹時,position在age后面,但是使用ORDER BY進行排序時,兩者顛倒了。
1.4Case4
EXPLAIN SELECT * FROM employees WHERE name = 'LiLei' AND age = 18 ORDER BY position,age;
????????這個SQL語句經過EXPLAIN進行分析之后,name走了索引,Extra也是Using Index condition,所以代表ORDER BY也走索引了。
????????主要原因是age當前是常量,ORDER BY后面的age其實形同虛設。
1.5Case5
EXPLAIN SELECT * FROM employees WHERE name = 'CC' ORDER BY age asc, position desc;
????????這個SQL語句經過EXPLAIN進行分析之后,name走了索引,但是Extra是Using Filesort,所以就代表了ORDER BY沒有使用索引進行排序。
????????主要的原因是因為position在索引樹中是按照升序排序進行構建的,但是這里使用ORDER BY進行排序時,采用的是降序排序,所以無法使用索引。
????????注意點:該案例在MySQL5.6,5.7的版本中確實是成立的,但是在MySQL8的版本中,MySQL官方引入了降序索引進行優化,desc也可以使用索引了。
1.6Case6
EXPLAIN SELECT * FROM employees WHERE name in ('LiLei', 'CC') ORDER BY age,position;
????????這個SQL語句經過EXPLAIN進行分析之后,name走了索引,但是Extra是Using Filesort,即ORDER BY排序沒有走索引。
????????主要的原因是name進行查詢出的兩部分數據組合起來時,按照age和position就不是有序的了,所以ORDER BY排序并不能走索引。
1.7Case7
EXPLAIN SELECT * FROM employees WHERE name > 'a' ORDER BY name;
????????這個SQL語句經過EXPLAIN進行分析之后,name沒有走索引,且Extra是Using Filesort,即ORDER BY排序沒有走索引,并且查看type的時候,可以發現type中顯示的是ALL,即該SQL語句進行了全表掃描。
????????主要的原因在于,該數據表中name > 'a'的數據量太大,MySQL在底層進行計算時認為使用索引進行排序是比較麻煩的,所以決定使用全表掃描。
????????可以使用覆蓋索引進行優化:
EXPLAIN SELECT name,age,position FROM employees WHERE name > 'a' ORDER BY name;
1.8優化總結
1.MySQL支持兩種方式的排序filesort和index,Using index是指MySQL掃描索引本身完成排序(因為索引構建的索引樹本身就是有序的)。index效率很高,filesort效率低。
2.ORDER BY滿足兩種情況會使用Using index。
1)ORDER BY語句使用索引最左前列。
2)使用WHERE子句與ORDER BY子句條件列組合滿足索引最左前綴法則。
3.盡量在索引列上完成排序,遵循索引建立(索引創建的順序)時的最左前綴法則。
4.如果ORDER BY的條件不在索引列上,就會產生Using filesort。
5.能用覆蓋索引盡量使用覆蓋索引。
6.GROUP BY與ORDER BY很類似,其是指時先排序分組,遵照索引創建順序的最左前綴法則。對于GROUP BY的優化如果不需要排序的可以加上ORDER BY NULL禁止排序。注意,WHERE優先于HAVING,能寫在WHERE中的限定條件就不要去HAVING中進行限定了。
1.9Using filesort文件排序原理詳解
????????Using filesort是MySQL中重點的概念,主要在ORDER BY排序中進行體現。
1.9.1filesort文件排序方式
????????單路排序:是一次性取出滿足條件行的所有字段,然后再sort buffer中進行排序。使用trace工具(MySQL自帶的性能分析工具)可以看到sort-mode信息里顯示<sort_key, additional_fields>或者<sort_key, packed_additional_fields>,單路排序其實就是將聚簇索引根據條件查詢出來的數據,放到sort buffer中進行排序(比較占用內存)
????????雙路排序(又被稱之為回表排序模式):是首先根據相應的條件取出相應的排序字段和可以直接定位行數據的行ID,然后在sort buffer中進行排序,排序完后需要再次取回其它需要的字段。使用trace工具可以看到sort_mode信息里顯示<sort_key, rowid>,雙路排序其實就是將二級索引根據條件查詢出來數據,進行條件篩選后,再統一回表到聚簇索引中進行數據查詢,將真正的數據進行返回。
1.9.2排序方式選擇的依據
????????MySQL通過比較系統變量max_length_for_sort_data(默認1024字節)的大小和需要查詢的字節總大小來判斷使用哪種排序模式。
????????1.如果字段的總長度小于max_length_for_sort_data,使用單路排序模式。
????????2.如果字段的總長度大于max_length_for_sort_data,使用雙路排序模式,
????????不建議自己去修改MySQL底層的參數。
1.9.3內存排序和磁盤排序
????????MySQL在進行Using Filesort的時候,會根據數據量的規模來選擇是否要調度磁盤進行排序。
????????MySQL進行排序時,會先在內存中開辟一個名為sort_buffer的空間,將數據載入到sort_buffer空間中進行排序,但是如果MySQL發現數據量過大,就會在磁盤中開辟臨時文件進行排序,MySQL會進行分析要在磁盤中開辟多少個臨時文件用于排序。
????????注意:如果全部使用sort_buffer內存排序一般情況下效率會高于磁盤文件排序,但不能因為這個就隨便增大sort_buffer(默認1M),MySQL很多參數都是做過優化的,不要輕易調整。
2.索引設計原則
2.1代碼先行,索引后上
????????錯誤的打開方式:建完表之后馬上建立索引。
????????正確的打開方式:主體業務功能開發完畢后,將涉及到該表的相關SQL語句都需要拿出來分析之后再建立索引。
2.2盡量建立聯合索引,少建立單值索引
????????在實際的項目中,一般不會建立多個單值索引,會使用多個字段建立聯合索引,一方面是索引是占用空間的,建立多個單值索引會導致占用大量的存儲空間,而且項目中一般查詢數據時都是使用多個字段一起進行搜索查詢的。
????????那么為什么要建立主鍵單值索引呢?
????????因為需要建立一個主鍵單值索引,保證每條數據具備一個全局唯一性。
2.3聯合索引盡量覆蓋條件
????????比如可以設計一個或者兩三個聯合索引,讓每一個聯合索引都盡量去包含SQL語句里的WHERE、ORDER BY、GROUP BY的字段,還要確保這些聯合索引的字段順序盡量滿足SQL查詢的最左前綴原則。
2.4不要在小基數上建立索引
????????索引基數是指這個字段在表中總共有多少個不同的值,比如一張表總共100萬行記錄,其中有個性別字段,其值不是男就是女,那么改字段的基數就是2。
????????如果對這種小基數字段建立索引的話,還不如全表掃描了,因為你的索引樹里就包含男和女兩種值,根本無法進行快速的二分查找,那用索引就沒有太大意義了。
????????一般建立索引,盡量使用那些基數比較大的字段,那么才能發揮出B+樹快速二分查找的優勢來。
2.5長字符串我們可以采用前綴索引
????????盡量對字段類型較小的列設計索引,比如說掃描tinyint之類的,因為字段類型較小的話,占用磁盤空間也會比較小的,此時你在搜索的時候性能也會好一點。
????????當然,這個所謂的字段類型小一點的列,也不是絕對的,很多時候你就是要針對varchar(255)這種字段建立索引,哪怕多占用一些磁盤空間也是有必要的。
????????對于這種varchar(255)的大字段可能會比較占用磁盤空間,可以稍微優化下,比如針對這個字段的前20個字符建立索引,即是對這個字段按里的每個值的前20個字符放在索引樹中,類似于KEY index(name(20), age, position)
????????此時的WHERE條件里搜索的時候,如果是根據name字段來搜索,那么此時就會先到索引樹里根據name字段的前20個字符去搜索,定位到之后前20個字符的前綴匹配的部分數據之后,再回到聚簇索引提取出來完整的name字段值進行對比。
????????但是假如你要是order by name,那么此時你的name因為在索引樹里僅僅包含了前20個字符,所以這個排序是沒法用上索引的,group by也是同理。
3.實戰場景優化
3.1整體場景
3.2范圍查詢數據索引建立在最后
????????age這種數據是經常要進行范圍查詢的,所以這種數據一般是直接放在最后的,索引也會建立到最后,因為如果將age索引建立到=值前面,在age符合的范圍中,后面的=值數據可能不是順序性的,就無法完全利用建立的聯合索引。
3.3in優化
????????為什么在不篩選sex的時候,要使用sex in ('female', 'male')呢?對于in來說,在數據量大的時候,in是會走索引的,在數據量小的時候,是不會走索引的,但是在生產環境下,數據量一般都是比較大的,所以in一般都會走索引的,出于索引設計原因,當性別不進行篩選時,采用這種方案可以使得聯合索引可以被完全利用。
3.4時間字段范圍查詢與age沖突的解決方案
????????如果時間字段也需要進行范圍查詢的話,這樣范圍查詢就與age發生了沖突,無法將聯合索引所有的字段都利用上。我們可以將時間字段修改為一個標志,來滿足我們的需求。
4.分頁場景優化
4.1分頁SQL問題分析
????????很多時候我們業務系統實現分析功能可能會用如下SQL實現:
SELECT * FROM employees LIMIT 10000,10;
????????表示從表employees中取出10001行開始的10行記錄,看似只查詢了10條記錄,實際這條SQL是先讀取10010條記錄,然后拋棄前10000條記錄,然后督導后面10條想要的數據。因此要查詢一張大表比較靠后的數據,執行效率是非常低的。
4.2根據自增且連續的主鍵排序的分頁查詢
????????如果數據的主鍵確實是連續的,可以使用WHERE條件查詢ID實現:
SELECT * FROM employees WHERE id > 90000 LIMIT 5;
????????但是主鍵如果是非連續自增,就無法使用這種方式,這也是其中的局限性。
????????局限性:1.主鍵自增且連續。2.結果是按照主鍵排序的。
4.3根據非主鍵字段排序的分頁查詢
????????再看一個根據非主鍵字段排序的分頁查詢,SQL如下:
SELECT * FROM employees ORDER BY name LIMIT 90000, 5;
????????但是這個過程中查詢的數據比較多,所以name字段可能放棄使用索引。原因是:掃描整個索引并查找到沒索引的行(可能要遍歷多個索引樹)的成本比掃描全表的成本更高,所以優化器放棄使用索引。
????????知道不走索引的原因,那么怎么優化呢?
????????其實關鍵是讓排序時返回的字段盡量可能少,讓覆蓋索引可以覆蓋到,無需回表查詢,所以可以讓排序和分頁操作先查出主鍵,然后根據主鍵查到對應的記錄,SQL改寫如下:
SELECT * FROM employees e INNER JOIN (SELECT id FROM employees ORDER BY name LIMIT 90000, 5) ed ON e.id = ed.id;
????????這條語句的查詢過程中,內連接里面的查詢是會走索引的,因為排序查詢時僅僅查詢了id,此時整條語句會走覆蓋索引的,所以整條索引的查詢效率是比較高的。
5.Join關聯查詢優化
5.1MySQL的表關聯常見的兩種算法
????????MySQL表進行關聯查詢的時候,主要是采用了以下兩種算法:
????????1.Nested-Loop Join 算法。
????????2.Block Nested-Loop Join 算法。
5.2嵌套循環連接Nested-Loop Join(NLJ) 算法
????????一次一行循環地從第一張表(稱之為驅動表)中讀取行,在這行數據中取到關聯字段,根據關聯字段另一張表(被驅動表)里取出滿足條件的行,然后取出兩張表的結果合集。
????????觸發點:這種情況一般是兩個表進行連接時,使用索引連接時會使用NLJ這種算法。
????????看下面這個SQL語句:
EXPLAIN SELECT * FROM t1 INNER JOIN t2 JOIN t2 ON t1.a = t2.a;
????????從執行計劃中可以看到這些信息:
????????先執行了t2表的查詢,type是ALL說明采取的方案是全表查詢,后又查詢的是t1表,走了索引(因為t1表的a字段建立了索引),這種情況一共會掃描200次(驅動表中的數據量100時),所以性能還是比較高的。
????????1.驅動表是t2,被驅動表是t1,驅動表是連接符號后面的表。先執行的就是驅動表(執行計劃結果的id如果一樣則按從上到下順序執行SQL),優化器一般會優先選擇小表作為驅動表。所以使用inner join時,排在前面的表不易i的那個是驅動表。
????????2.當使用left join時,左表是驅動表,右表是被驅動表,當受用right join時,右表是驅動表,左表是被驅動表,當使用join時,MySQL會選擇數據量比較小的表作為驅動表,大表作為被驅動表。
????????3.使用了NLJ算法。一般Join語句中,如果執行計劃Extra中未出現Using join buffer則表示使用的join算法時NLJ。
????????上面SQL的大致流程如下:
????????1.從表t2中讀取一行數據(如果t2表有查詢過濾條件的,會從過濾結果里取出一行數據)
????????2.從第1步的數據,取出關聯字段a,到表t1中查找。
????????3.取出表t1中滿足條件的行,跟t2中獲取到的結果合并,作為結果返回給客戶端。
????????4.重復上面3步。
????????整個過程會讀取t2表中的所有數據(掃描100行),然后遍歷這每行數據中字段a的值,根據t2表中a的值索引掃描t1表中的對應的的行(掃描100次t1表的索引,1次掃描可以認為最終只掃描t1表一行完整的數據,也就是總共t1表也掃描了100行)。因此整個過程掃描了200行。
5.3基于塊的連接循環連接Block Nested Loop Join(BNL)算法
????????把驅動表的數據讀入到join_buffer中,然后掃描被驅動表,把被驅動表每一行取出來跟join_buffer中的數據作對比。
????????觸發點:這種情況一般出現在兩個表進行連接時,不使用索引連接時會使用BNL這種算法。
????????看下面這個SQL語句:
EXPLAIN SELECT * FROM t1 INNER JOIN t2 ON t1.b = t2.b;
????????觸發這種情況時,Extra中會顯示使用了BNL這種算法。
????????上面SQL的大致流程如下:
????????1.把t2(驅動表)的所有數據放入到join_buiffer中。
????????2.把表t1中的每一行取出來,跟join_buffer中的數據做對比。
????????3.返回滿足join條件的數據。
????????整個過程中對表t1和t2都做了一次全表掃描,因此掃描的總行數為10000(表t1的數據總量)+ 100(表t2的數據總量)= 10100。并且join_buffer里的數據是無序的,因此對表t1中的每一行,都要做100次判斷,所以內存中的判斷次數是100 * 10000 = 100萬次。
????????這個例子中的表t2才100行,如果表t2是一個大表,join_buffer放不下腫莫辦呢?
????????join_buffer的大小是由參數join_buffer_size設定的,默認值是256kb。如果放不下表t2的所有數據,策略很簡單,就是分段放。
????????比如t2表中有1000行記錄,join_buffer一次只能放800行數據,那么執行過程中就是先往join_buffer里放800行數據,然后從t1表中取出數據跟join_buffer中數據進行對比得到部分結果,然后清空join_buffer,再放入t2表剩余200行記錄,再次從t1表中取出數據跟join_buffer中數據對比得到部分結果,然后清空join_buffer,再放入t2表剩余200行記錄,再從t1表中取數據跟join_buffer中數據對比。所以就多掃了一次t1表。
5.4表連接時連接字段沒有索引為什么采用BNL算法而不是NLJ算法
????????如果表連接時連接字段沒有索引,使用NLJ算法的話,磁盤掃描次數就會是100萬次。
????????很顯然,使用BNL算法磁盤掃描的次數少很多,相比于磁盤掃描,BNL的內存計算會快得多。
????????因此MySQL對于被驅動表的關聯字段沒索引的關聯查詢,一般都會使用BNL算法。如果有索引一般選擇NLJ算法,有索引的情況下NLJ算法比BNL算法性能更高。
5.5對于關聯SQL的優化
????????1.關聯字段加索引:讓MySQL做join操作時盡量選擇NLJ算法。
????????2.小表驅動大表:寫多表連接SQL時如果明確知道那張表是小表可以用straight_join寫法固定連接驅動方式,省去MySQL優化器自己判斷的時間。
????????straight_join解釋:straight_join功能同join類似,但能讓坐標的表來驅動右邊的表,能改優化器對于聯表查詢的執行順序。
????????比如下面這條SQL語句:
SELECT * FROM t2 straight_join t1 ON t2.a = t1.a
????????這條SQL語句代表著指定MySQL選擇t2表作為驅動表。
????????1.straight_join只適用于inner join,并不適用于left join,right join(因為left join,right join已經代表指定了表的執行順序)
????????2.盡可能讓優化器去判斷,因為大部分情況下MySQL優化器是比人要聰明的。使用straight_join一定要慎重,因為部分情況下人為指定的執行順序并不一定會比優化引擎要靠譜。
6.in和exists優化
????????原則:小表驅動大表,即小的數據集驅動大的數據集。
6.1in優化
????????in:當B表的數據集小于A表的數據集,in優于exists。
????????看下面的SQL語句:
SELECT * FROM A WHERE id IN (SELECT id FROM b);
????????這個SQL語句的執行流程:先執行IN后面的SQL語句,從b表中查詢中一個id數據集,再根據查詢出的id數據集去A中查詢數據。
????????等價于下面的代碼:
for (SELECT id FROM B) {SELECT * FROM A WHERE A.id = B.id
}
????????其實就是外循環B表,每次循環查詢到的ID數據都需要A表去查詢一次等價數據。
????????此時如果B表是小表,數據量比較小,所以就會使得查詢的數據比較少,優化整體性能。
6.2exists優化
????????exists:當A表的數據集小于B表的數據集時,exists優于in。將主查詢A的數據,放到子查詢B中做條件驗證,根據驗證結果(true或者false)來決定主查詢的數據是否保留。
????????看下面這條SQL語句:
SELECT * FROM A WHERE EXISTS (SELECT 1 FROM B WHERE B.id = A.id)
????????這個SQL語句的執行流程:先執行前面的SQL語句,從表A中查詢出所有的數據,然后執行EXISTS后面的查詢語句,再進行篩選數據。
????????等價于下面的代碼:
for (SELECT * FROM A) {SELECT * FROM B WHERE B.id = A.id
}
????????1.EXISTS(subquery)只返回TRUE或者FALSE,因此子查詢中的SELECT * 也可以SELECT 1替換,官方說明是實際執行時會忽略SELECT清單,因此沒有區別。
????????2.EXISTS子查詢的實際執行過程可能進行了優化,而不是我們理解上的逐條對比。
7.Count(*)優化
7.1執行原理
????????Count關鍵字有下面四種用法:
EXPLAIN SELECT COUNT(1) FROM employees;
EXPLAIN SELECT COUNT(id) FROM employees;
EXPLAIN SELECT COUNT(NAME) FROM employees;
EXPLAIN SELECT COUNT(*) FROM employees;
????????四個SQL最終的執行計計劃如下:
????????type為index,且Extra為Using index,代表均走了索引,key索引字段都走的是idx_name_age_position二級索引(非聚簇索引)
????????四個SQL的執行計劃一樣,說明這四個SQL的執行效率都應該差不多。
????????字段有索引時:count(*) ≈ count(1) > count(字段) > count(主鍵ID)
????????分析:
????????count的整體執行的流程是將對應的數據取出進行累加計算出一個值,計算出后返回。對于COUNT(1)和COUNT(字段)來說,兩者的執行流程是類似的,不過COUNT(1)不需要取出字段統計,只需要統計常量1即可,COUNT(字段)還需要取出字段,理論上COUNT(1)比COUNT(字段)會快一些。
????????COUNT(*)是例外,MySQL并不會將全部字段取出來,而是專門做了優化,不取值,按行累加,效率很高,所以不需要用COUNT(列名)或者COUNT(常量)來替代COUNT(*)。
????????COUNT(id)之所以會在字段有索引時慢于COUNT(字段),因為字段建立的是二級非聚簇索引,id的葉節點是聚簇索引,聚簇索引的數據量相對于二級非聚簇索引是更大的,所以COUNT(id)的性能會較差一些。但是在MySQL5.7時針對這個進行了對應的優化,當表中建立了二級索引時,使用COUNT(id)時,就會走二級索引樹,來優化提升性能。
????????字段沒有索引時:count(*) ≈ count(1) > count(主鍵ID) > count(字段)
????????因為字段沒有索引時,count(字段)是無法走索引的,只能ALL全表掃描,但是count(主鍵ID)仍然可以走索引,所以性能會更高一些。
7.2優化方案
7.2.1查詢MySQL自己維護的總行數
????????對于myisam存儲引擎的表做不帶WHERE條件的COUNT查詢性能是很高的,因為myisam存儲引擎的表的總行數會被mysql存儲在磁盤上,查詢不需要計算。
????????看下面這個SQL查詢語句:
EXPLAIN SELECT COUNT(*) FROM test_myisam;
????????這個SQL語句的執行計劃中Extra是Select table optimized away,type是null,即代表這條SQL語句的執行速度特別快,性能特別高。
????????對于innodb存儲引擎的表,MySQL不會存儲表的總記錄行數(因為有MVVC機制),查詢count需要實時計算。
7.2.2 show table status
????????如果只需要知道表總行數的估計值可以使用如下SQL查詢,性能很高:
SHOW TABLE STATUS LIKE 'employees';
????????MySQL會在表中維護一個STATUS作為總行數的估計值,雖然不是很準確但是在很多場景下也足夠使用了。
7.2.3將總數維護到Redis中
????????插入或者刪除數據行的時候同時維護Redis中的表總行數key的計數值(用incr和decr命令),但是這種方式可能不準,很難保證表操作和Redis操作的事務一致性。
7.2.4增加數據庫計數表
????????插入或者刪除數據行的時候同時維護計數表,讓他們在同一個事務里操作,這種情況下不會出現數據不一致的情況。
8.MySQL數據類型選擇
8.1數據類型
????????在MySQL中選擇正確的數據類型,對于性能至關重要,一般應該遵循下面兩部:
????????(1)確定合適的大類型:數字、字符串、時間、二進制。
????????(2)確定具體的類型:有無符號、取值范圍、變長定長登。
????????MySQL數據類型設置方面,盡量用更小的數據類型,因為它們通常有更好的性能,花費更少的硬件資源。并且盡量把字段定義為NOT NULL,避免使用NULL。
????????盡量參考對應的數據表。
????????優化建議:
????????1.如果整形數據沒有負數,如ID號,建議指定為UNSIGNED無符號類型,容量可以擴大一倍。
????????2.建議使用TINYINT代替ENUM、BITENUM、SET。
????????3.避免使用整數的顯示寬度,也就是說,不要使用INT(10)類似的方法指定自動福安顯示寬度,直接用INT。
????????4.DECIMAL最適合保存準確度要求高,而且用于計算的數據,比如價格。但是在使用DECIMAL類型的時候,注意長度設置。
????????5.建議使用整形類型來運算和存儲實數,方法是,實數乘以相應的倍數后再操作。
????????6.整數通常是最佳的數據類型,因為它速度快,并且能使用AUTO_INCREMENT。
8.2日期和時間
????????小公司建議使用TIMESTAMP,因為TIMESTAMP相對于DATETIME更加節省內存。
????????大公司建議使用DATETIME,因為TIMESTAMP有2038危機。
????????優化建議:
????????1.MySQL呢個存儲的最小時間粒度為秒。
????????2.建議使用DATE數據類型來保存日期。MySQL中默認的日期格式是yyyy-mm-dd。
????????3.用MySQL的內建類型DATE、TIME、DATETIME來存儲時間,而不是使用字符串。
????????4.當數據格式為TIMESTAMP和DATETIME時,可以使用CURRENT_TIMESTAMP作為默認值(MySQL5.6以后),MySQL會自動返回記錄插入的確切時間。
????????5.TIMESTAMP是UTC時間戳,與時區相關。
????????6.DATETIME的存儲格式時一個YYYYMMDD HH:MM:SS的整數,與時區無關,存儲了什么,讀出來就是什么。
????????7.除非有特殊需求,一般的公司建議使用TIMESTAMP,相比與DATETIME更加節約空間,但是像阿里這種大公司一般會使用DATETIME,因為無需考慮TIMESTAMP的2038危機。
????????8.有時人們把Unix的時間戳保存為整數值,但是這樣通常沒有任何好處,而且這種格式處理起來也不是很方便。
8.3字符串
????????只需要簡單了解以下存儲字符的格式即可,其他無需了解了:
????????優化建議:
????????1.字符串的長度相差較大用VARCHAR;字符串短,且所有值都接近一個長度用CHAR。
????????2.CHAR和VARCHAR適用于包括人名、郵政編碼、電話號碼和不超過255個字符長度的任意字母數字組合。那些要用來計算的數字不要用VARCHAR類型保存,因為可能會導致一些與計算相關的問題。換句話說,可能會影響到計算的準確性和完整性。
????????3.盡量少用BLOB和TEXT,如果實在要用可以考慮將BLOB和TEXT單獨存儲到一張表中,用ID關聯。主要是因為TEXT和BLOB字段中存儲的數據是比較大的,并且TEXT和BLOB的數據可能是不經常需要去查詢的,如果不需要去查詢,還在表的聚簇索引中跟隨掃表,就會出現性能下滑的現象,所以將其單獨存到一張表中有利于提高性能。
????????4.BLOB系列存儲二進制字符串,與字符集無關。TEXT系列存儲非二進制字符串,與字符集相關。
????????5.BLOB和TEXT都不能有默認值。