mysql數據庫面試題大全html
原理篇node
1:什麼是數據庫事務
參考答案:
數據庫事務(Database Transaction) ,是指做爲單個邏輯工做單元執行的一系列操做,要麼徹底地執行,要麼徹底地不執行。事務處理能夠確保除非事務性單元內的全部操做都成功完成,不然不會永久更新面向數據的資源。mysql
2:事務的特性及其含義
參考答案:
事務有四個特性,分別是原子性(Atomicity)、一致性(Correspondence)、隔離性(Isolation)、持久性(Durability),簡稱ACID。
原子性:事務必須是原子工做單元;對於數據修改,要麼全都執行,要麼全都不執行。
一致性:事務在完成時,必須使全部的數據都保持一致狀態。
隔離性:由併發事務所做的修改必須與任何其它併發事務所做的修改隔離。
持久性:事務完成以後,它對於系統的影響是永久性的。面試
3:關係型數據庫和非關係型數據庫的區別
參考答案:
關係型數據庫採用了關係模型來組織數據,關係模型是一個二位表格,通常採用行存儲,最大的特色就是事務的一致性;
非關係型數據庫使用鍵值對存儲數據,多用於分佈式的數據存儲,通常不支持ACID特性,嚴格上不是一種數據庫,應該是一種數據結構化存儲方法的集合。正則表達式
4:什麼是視圖
參考答案:
視圖(VIEW)也被稱做虛表,即虛擬的表,是一組數據的邏輯表示,其本質是對應於一條SELECT語句,結果集被賦予一個名字,即視圖名字。 視圖自己並不包含任何數據,它只包含映射到基表的一個查詢語句,當基表數據發生變化,視圖數據也隨之變化。
一、視圖可以簡化用戶的操做;
二、視圖使用戶能以多鍾角度看待同一數據;
三、視圖對重構數據庫提供了必定程度的邏輯獨立性;
四、視圖可以對機密數據提供安全保護;
五、適當的利用視圖能夠更清晰的表達查詢。算法
5:什麼是存儲過程
參考答案:
存儲過程是一組爲了完成特定功能的SQL 語句集,存儲在數據庫中,通過第一次編譯後調用不須要再次編譯,用戶經過指定存儲過程的名字並給出參數(若是該存儲過程帶有參數)來執行它。sql
6:簡述數據庫三範式
參考答案:
第一範式:字段是最小的的單元不可再分;
第二範式:知足第一範式,表中的字段必須徹底依賴於所有主鍵而非部分主鍵;
第三範式:知足第二範式,非主鍵外的全部字段必須互不依賴。數據庫
7:Mysql兩種引擎MyISAM和InnoDB的特色
參考答案:
MyISAM引擎是MySQL 5.1及以前版本的默認引擎,它的特色是:
不支持行鎖,讀取時對須要讀到的全部表加鎖,寫入時則對錶加排它鎖;
不支持事務;
不支持外鍵;
不支持崩潰後的安全恢復;
在表有讀取查詢的同時,支持往表中插入新紀錄;
支持BLOB和TEXT的前500個字符索引,支持全文索引;
支持延遲更新索引,極大提高寫入性能;
對於不會進行修改的表,支持壓縮表,極大減小磁盤空間佔用;
InnoDB在MySQL 5.5後成爲默認索引,它的特色是:
支持行鎖,採用MVCC來支持高併發;
支持事務;
支持外鍵;
支持崩潰後的安全恢復;
不支持全文索引;
整體來說,MyISAM適合SELECT密集型的表,而InnoDB適合INSERT和UPDATE密集型的表。緩存
8:什麼是索引
參考答案:
索引是爲了加速對錶中數據行的檢索而建立的一種分散的存儲結構。索引是針對表而創建的,它是由數據頁面之外的索引頁面組成的,每一個索引頁面中的行都會含有邏輯指針,以便加速檢索物理數據。安全
9:什麼是主鍵和外鍵,主鍵與惟一鍵的區別
參考答案:
主鍵是可以惟一標識表中某一行的屬性或屬性組。一個表只能有一個主鍵。
外鍵是用於創建和增強兩個表數據之間的連接的一列或多列。外鍵約束主要用來維護兩個表之間數據的一致性。
主鍵不能重複,不能爲空,惟一鍵不能重複,能夠爲空。
創建主鍵的目的是讓外鍵來引用。
一個表最多隻有一個主鍵,但能夠有不少惟一鍵。
語法篇
1:SQL語言包括哪幾部分,每部分都有哪些操做關鍵字
參考答案:
SQL語言包括數據定義(DDL)、數據操縱(DML),數據控制(DCL)和數據查詢(DQL)四個部分。
數據定義:Create Table,Alter Table,Drop Table, Craete/Drop Index等
數據操縱:Select ,insert,update,delete,
數據控制:grant,revoke
數據查詢:select
2:什麼樣的對象能夠使用 CREATE 語句建立 ?
參考答案:
如下對象是使用 CREATE 語句建立的:
DATABASE、EVENT、FUNCTION、INDEX、PROCEDURE、TABLE、TRIGGER、USER、VIEW
3:Mysql中NULL是什麼意思,它和空值有什麼區別
參考答案:
NULL這個值表示UNKNOWN(未知),它不表示「」(空字符串)。對NULL這個值的任何比較都會生產一個NULL值。不能把任何值與一個 NULL值進行比較,並在邏輯上但願得到一個答案。須要使用IS NULL或者IS NOT NULL來進行NULL判斷,SQL語句函數中能夠使用ifnull ()函數來進行處理。
空值(」)是不佔用空間的,判斷空字符用 = 」 或者 <> 」 來進行處理。
沒法比較 NULL 和 0;它們是不等價的。
沒法使用比較運算符來測試 NULL 值,好比 =, <, 或者 <>。
NULL值能夠使用 <=> 符號進行比較,該符號與等號做用類似,但對NULL有意義。
進行 count ()統計某列的記錄數的時候,若是採用的 NULL 值,會別系統自動忽略掉,可是空值是統計到其中。
4:IFNULL函數如何使用
參考答案:
IFNULL(expr1,expr2)
若是expr1不是NULL,IFNULL()返回expr1,不然它返回expr2。IFNULL()返回一個數字或字符串值。
5:什麼狀況下設置了索引但沒法使用
參考答案:
一、以「%」開頭的LIKE語句,模糊匹配
二、OR語句先後沒有同時使用索引
三、數據類型出現隱式轉化(如varchar不加單引號的話可能會自動轉換爲int型)
6:如何列出某個數據庫,以及一個數據庫中的全部表
參考答案:
show databases;
show tables;
7:如何獲取表內全部 Field 對象的名稱和類型
參考答案:
在mysql中若是想要查看錶的定義的話,有如下兩種方式:
一、show create table table_name;
二、desc table_name;
8:UNION 和 UNION ALL 有什麼區別
參考答案:
UNION用於合併兩個或多個SELECT語句的結果集,並消去表中任何重複行。UNION 內部的 SELECT 語句必須擁有相同數量的列,列也必須擁有類似的數據類型。同時,每條SELECT語句中的列的順序必須相同。
UNIONALL基本使用和UNION是一致的,可是UNION ALL不會消除表中的重複行。
9:鏈接的種類與區別
參考答案:
SQL 鏈接(JOIN)子句用於將數據庫中兩個或者兩個以上表中的記錄組合起來。鏈接經過共有值將不一樣表中的字段組合在一塊兒。
SQL 中有多種不一樣的鏈接:
內鏈接(INNERJOIN):當兩個表中都存在匹配時,才返回行。
左鏈接(LEFTJOIN):返回左表中的全部行,即便右表中沒有匹配的行。
右鏈接(RIGHTJOIN):返回右表中的全部行,即便左表中沒有匹配的行。
全鏈接(FULLJOIN):只要某一個表存在匹配,就返回行。
笛卡爾鏈接(CARTESIANJOIN):返回兩個或者更多的表中記錄集的笛卡爾積。
10:如何作模糊查詢
參考答案:
語法:SELECT 字段 FROM 表 WHERE 某字段 LIKE 條件
其中條件的匹配模式有:
一、%:表示任意0個或多個字符。可匹配任意類型和長度的字符。
二、_:表示任意單個字符。匹配單個任意字符,它經常使用來限制表達式的字符長度語句。
三、[ ]:表示括號內所列字符中的一個(相似正則表達式)。指定一個字符、字符串或範圍,要求所匹配對象爲它們中的任一個。
四、[^ ]:表示不在括號所列以內的單個字符。其取值和[] 相同,但它要求所匹配對象爲指定字符之外的任一個字符。
11:如何定義 REGEXP,LIKE 和 REGEXP 操做有什麼區別
REGEXP 是模式匹配,其中匹配模式在搜索值的任何位置。
語法:SELECT字段 FROM 表 WHERE 某字段 REGEXP 條件。
LIKE進行簡單的模糊查詢, REGEXP能夠進行各類複雜匹配。
12:in與exists的區別
參考答案:
exists用於檢查子查詢是否至少會返回一行數據,該子查詢實際上並不返回任何數據,而是返回值True或False。IN語句對於子查詢的返回字段只能由一個。
外層查詢表小於子查詢表,則用exists,外層查詢表大於子查詢表,則用in,若是外層和子查詢表差很少,則愛用哪一個用哪一個。
13:varchar與char的區別以及varchar(50)中的50表明的含義
參考答案:
char是一種固定長度的類型,varchar則是一種可變長度的類型,它們的區別是:
char(M)類型的數據列裏,每一個值都佔用M個字節,若是某個長度小於M,MySQL就會在它的右邊用空格字符補足。在varchar(M)類型的數據列裏,每一個值只佔用恰好夠用的字節再加上一個用來記錄其長度的字節。
varchar(50)表示最多佔用50個字符。
14:int(5)中5的含義
參考答案:
int(M) M表示的不是數據的最大長度,只是數據寬度,並不影響存儲多少位長度的數據。
int類型數據的字節大小是固定的4個字節。int(5)和int(11)區別在於,顯示的數據位數一個是5位一個是11位,在開啓zerofill(填充零)狀況下,若int(5)存儲的數字長度是小於5的則會在不足位數的前面補充0,可是若是int(5)中存儲的數字長度大於5位的話,則按照實際存儲的顯示(數據大小在int類型的4個字節範圍內便可)。
15:BLOB 和 TEXT 有什麼區別
參考答案:
TEXT與BLOB的主要差異就是BLOB保存二進制數據,TEXT保存字符數據。
BLOB有4種類型:TINYBLOB、BLOB、MEDIUMBLOB和LONGBLOB。它們只是可容納值的最大長度不一樣。TEXT也有4種類型:TINYTEXT、TEXT、MEDIUMTEXT和LONGTEXT。這些類型同BLOB類型同樣,有相同的最大長度和存儲需求。
BLOB列沒有字符集,而且排序和比較基於列值字節的數值值。TEXT列有一個字符集,而且根據字符集的校對規則對值進行排序和比較。
BLOB和TEXT列不能有默認值。
16:drop,delete與truncate的區別
參考答案:
delete
一、delete是DML,執行delete操做時,每次從表中刪除一行,而且同時將該行的的刪除操做記錄在redo和undo表空間中以便進行回滾(rollback)和重作操做,但要注意表空間要足夠大,須要手動提交(commit)操做才能生效,能夠經過rollback撤消操做。
二、delete可根據條件刪除表中知足條件的數據,若是不指定where子句,那麼刪除表中全部記錄。
三、delete語句不影響表所佔用的extent,高水線(high watermark)保持原位置不變。
truncate
一、truncate是DDL,會隱式提交,因此,不能回滾,不會觸發觸發器。
二、truncate會刪除表中全部記錄,而且將從新設置高水線和全部的索引,缺省狀況下將空間釋放到minextents個extent,除非使用reusestorage,。不會記錄日誌,因此執行速度很快,但不能經過rollback撤消操做(若是一不當心把一個表truncate掉,也是能夠恢復的,只是不能經過rollback來恢復)。
三、對於外鍵(foreignkey)約束引用的表,不能使用truncate table,而應使用不帶where 子句的delete語句。
四、truncate table不能用於參與了索引視圖的表。
drop
一、drop是DDL,會隱式提交,因此,不能回滾,不會觸發觸發器。
二、drop語句刪除表結構及全部數據,並將表所佔用的空間所有釋放。
三、drop語句將刪除表的結構所依賴的約束,觸發器,索引,依賴於該表的存儲過程/函數將保留,可是變爲invalid狀態。
安全與優化篇
1:MySQL 服務器默認端口是什麼
參考答案:
MySQL 服務器的默認端口是 3306。
2:mysql怎樣進行用戶權限管理
參考答案:
能夠經過以下方面進行用戶權限管理:
一、限制用戶訪問哪些庫、哪些表
二、限制用戶對哪些表執行SELECT、CREATE、DELETE、DELETE、ALTER等操做
三、限制用戶登陸的IP或域名
四、限制用戶本身的權限是否能夠受權給別的用戶
3:MySQL有哪些日誌,分別是什麼用處
參考答案:
mysql日誌通常分爲5種
一、錯誤日誌:-log-err (記錄啓動,運行,中止mysql時出現的信息)
二、二進制日誌:-log-bin (記錄全部更改數據的語句,還用於複製,恢復數據庫用)
三、查詢日誌:-log (記錄創建的客戶端鏈接和執行的語句)
四、慢查詢日誌:-log-slow-queries (記錄全部執行超過long_query_time秒的全部查詢)
五、更新日誌: -log-update (二進制日誌已經代替了老的更新日誌,更新日誌在MySQL5.1中再也不使用)
4:sql注入的主要特色與危害
參考答案:
Sql注入變種極多,攻擊簡單,危害極大,主要有:
一、未經受權操做數據庫的數據
二、惡意纂改網頁
三、私自添加系統帳號或者是數據庫使用者帳號
四、網頁掛木馬
5:如何提升insert性能
參考答案:
一、合併多條 insert 爲一條,即: insert into t values(a,b,c), (d,e,f) ,,,
二、修改參數 bulk_insert_buffer_size, 調大批量插入的緩存;
三、設置 innodb_flush_log_at_trx_commit = 0 ,相對於innodb_flush_log_at_trx_commit = 1 能夠十分明顯的提高導入速度;
四、手動使用事務
由於mysql默認是autocommit的,這樣每插入一條數據,都會進行一次commit;因此,爲了減小建立事務的消耗,咱們可用手工使用事務,即STARTTRANSACTION;insert 。。,insert。。commit;即執行多個insert後再一塊兒提交;通常1000條insert 提交一次。
六、數據庫優化的思路
參考答案:
能夠從如下方面進行展開
一、SQL語句優化
二、索引優化
三、數據庫結構優化
範式優化: 好比消除冗餘
反範式優化:好比適當加冗餘等(減小join)
拆分表:垂直拆分和水平拆分
四、服務器硬件優化
七、列舉一些sql語句優化方法(sql語句優化的30種方法)
轉載於:https://www.cnblogs.com/Little-Li/p/8031295.html
1.對查詢進行優化,應儘可能避免全表掃描,首先應考慮在 where 及 order by 涉及的列上創建索引。
2.應儘可能避免在 where 子句中使用!=或<>操做符,不然將引擎放棄使用索引而進行全表掃描。
3.應儘可能避免在 where 子句中對字段進行 null 值判斷,不然將致使引擎放棄使用索引而進行全表掃描,如:
select id from t where num is null
能夠在num上設置默認值0,確保表中num列沒有null值,而後這樣查詢:
select id from t where num=0
4.應儘可能避免在 where 子句中使用 or 來鏈接條件,不然將致使引擎放棄使用索引而進行全表掃描,如:
select id from t where num=10 or num=20
能夠這樣查詢:
select id from t where num=10
union all
select id from t where num=20
5.下面的查詢也將致使全表掃描:
select id from t where name like '%abc%'
若要提升效率,能夠考慮全文檢索。
6.in 和 not in 也要慎用,不然會致使全表掃描,如:
select id from t where num in(1,2,3)
對於連續的數值,能用 between 就不要用 in 了:
select id from t where num between 1 and 3
7.若是在 where 子句中使用參數,也會致使全表掃描。由於SQL只有在運行時纔會解析局部變量,但優化程序不能將訪問計劃的選擇推遲到運行時;它必須在編譯時進行選擇。然而,若是在編譯時創建訪問計劃,變量的值仍是未知的,於是沒法做爲索引選擇的輸入項。以下面語句將進行全表掃描:
select id from t where num=@num
能夠改成強制查詢使用索引:
select id from t with(index(索引名)) where num=@num
8.應儘可能避免在 where 子句中對字段進行表達式操做,這將致使引擎放棄使用索引而進行全表掃描。如:
select id from t where num/2=100
應改成:
select id from t where num=1002
9.應儘可能避免在where子句中對字段進行函數操做,這將致使引擎放棄使用索引而進行全表掃描。如:
select id from t where substring(name,1,3)='abc'--name以abc開頭的id
select id from t where datediff(day,createdate,'2005-11-30')=0--'2005-11-30'生成的id
應改成:
select id from t where name like 'abc%'
select id from t where createdate>='2005-11-30' and createdate<'2005-12-1'
10.不要在 where 子句中的「=」左邊進行函數、算術運算或其餘表達式運算,不然系統將可能沒法正確使用索引。
11.在使用索引字段做爲條件時,若是該索引是複合索引,那麼必須使用到該索引中的第一個字段做爲條件時才能保證系統使用該索引,不然該索引將不會被使用,而且應儘量的讓字段順序與索引順序相一致。
12.不要寫一些沒有意義的查詢,如須要生成一個空表結構:
select col1,col2 into #t from t where 1=0
這類代碼不會返回任何結果集,可是會消耗系統資源的,應改爲這樣:
create table #t(...)
13.不少時候用 exists 代替 in 是一個好的選擇:
select num from a where num in(select num from b)
用下面的語句替換:
select num from a where exists(select 1 from b where num=a.num)
14.並非全部索引對查詢都有效,SQL是根據表中數據來進行查詢優化的,當索引列有大量數據重複時,SQL查詢可能不會去利用索引,如一表中有字段sex,male、female幾乎各一半,那麼即便在sex上建了索引也對查詢效率起不了做用。
15.索引並非越多越好,索引當然能夠提升相應的 select 的效率,但同時也下降了 insert 及 update 的效率,由於 insert 或 update 時有可能會重建索引,因此怎樣建索引須要慎重考慮,視具體狀況而定。一個表的索引數最好不要超過6個,若太多則應考慮一些不常使用到的列上建的索引是否有必要。
16.應儘量的避免更新 clustered 索引數據列,由於 clustered 索引數據列的順序就是表記錄的物理存儲順序,一旦該列值改變將致使整個表記錄的順序的調整,會耗費至關大的資源。若應用系統須要頻繁更新 clustered 索引數據列,那麼須要考慮是否應將該索引建爲 clustered 索引。
17.儘可能使用數字型字段,若只含數值信息的字段儘可能不要設計爲字符型,這會下降查詢和鏈接的性能,並會增長存儲開銷。這是由於引擎在處理查詢和鏈接時會逐個比較字符串中每個字符,而對於數字型而言只須要比較一次就夠了。
18.儘量的使用 varchar/nvarchar 代替 char/nchar ,由於首先變長字段存儲空間小,能夠節省存儲空間,其次對於查詢來講,在一個相對較小的字段內搜索效率顯然要高些。
19.任何地方都不要使用 select from t ,用具體的字段列表代替「*」,不要返回用不到的任何字段。
20.儘可能使用表變量來代替臨時表。若是表變量包含大量數據,請注意索引很是有限(只有主鍵索引)。
21.避免頻繁建立和刪除臨時表,以減小系統表資源的消耗。
22.臨時表並非不可以使用,適當地使用它們能夠使某些例程更有效,例如,當須要重複引用大型表或經常使用表中的某個數據集時。可是,對於一次性事件,最好使用導出表。
23.在新建臨時表時,若是一次性插入數據量很大,那麼能夠使用 select into 代替 create table,避免形成大量 log ,以提升速度;若是數據量不大,爲了緩和系統表的資源,應先create table,而後insert。
24.若是使用到了臨時表,在存儲過程的最後務必將全部的臨時表顯式刪除,先 truncate table ,而後 drop table ,這樣能夠避免系統表的較長時間鎖定。
25.儘可能避免使用遊標,由於遊標的效率較差,若是遊標操做的數據超過1萬行,那麼就應該考慮改寫。
26.使用基於遊標的方法或臨時表方法以前,應先尋找基於集的解決方案來解決問題,基於集的方法一般更有效。
27.與臨時表同樣,遊標並非不可以使用。對小型數據集使用 FAST_FORWARD 遊標一般要優於其餘逐行處理方法,尤爲是在必須引用幾個表才能得到所需的數據時。在結果集中包括「合計」的例程一般要比使用遊標執行的速度快。若是開發時間容許,基於遊標的方法和基於集的方法均可以嘗試一下,看哪種方法的效果更好。
28.在全部的存儲過程和觸發器的開始處設置 SET NOCOUNT ON ,在結束時設置 SET NOCOUNT OFF 。無需在執行存儲過程和觸發器的每一個語句後向客戶端發送 DONE_IN_PROC 消息。
29.儘可能避免向客戶端返回大數據量,若數據量過大,應該考慮相應需求是否合理。
30.儘可能避免大事務操做,提升系統併發能力。
8:如何優化 DISTINCT
sql distinct詳解以及優化
一.distinct簡介
distinct這個關鍵字來過濾掉多餘的重複記錄只保留一條,但每每只用 它來返回不重複記錄的條數,而不是用它來返回不重記錄的全部值。其緣由是distinct只有用二重循環查詢來解決,而這樣對於一個數據量很是大的站來講,無疑是會直接影響到效率的。
下面先來看看例子:
table表
字段1 字段2
id name
1 a
2 b
3 c
4 c
5 b
庫結構大概這樣,這只是一個簡單的例子,實際狀況會複雜得多。
好比我想用一條語句查詢獲得name不重複的全部數據,那就必須使用distinct去掉多餘的重複記錄。
select distinct name from table
獲得的結果是:
----------
name
a
b
c
好像達到效果了,但是,我想要獲得的是id值呢?改一下查詢語句吧:
select distinct name, id from table
結果會是:
----------
id name
1 a
2 b
3 c
4 c
5 b
distinct怎麼沒起做用?做用是起了的,不過他同時做用了兩個字段,也就是必須得id與name都相同的纔會被排除。。。。。。。
咱們再改改查詢語句:
select id, distinct name from table
很遺憾,除了錯誤信息你什麼也得不到,distinct必須放在開頭。難到不能把distinct放到where條件裏?能,照樣報錯。
下面方法可行:
select *, count(distinct name) from table group by name
結果:
id name count(distinct name)
1 a 1
2 b 1
3 c 1
最後一項是多餘的,不用管就好了,目的達到。。。。。
group by 必須放在 order by 和 limit以前,否則會報錯
==============以上是關於Oracle的distinct的一種用法==============
用distinct關鍵字只能過濾查詢字段中全部記錄相同的(記錄集相同),而若是要指定一個字段卻沒有效果,另外distinct關鍵字會排序,效率很低。
select distinct name from t1 能消除重複記錄,但只能取一個字段,如今要同時取id,name這2個字段的值。
select distinct id,name from t1 能夠取多個字段,但只能消除這2個字段值所有相同的記錄
因此用distinct達不到想要的效果,用group by 能夠解決這個問題。
二. distinct使用
1 Distinct 位置
單獨的distinct只能放在開頭,不然報錯,語法錯誤
mysql> Select player_id,distinct(task_id) from task;
ERROR 1064 (42000): You havean error in your SQL syntax; check the manual that
corresponds to your MySQLserver version for the right syntax to use near 'disti
nct(task_id) from task' atline 1
如今把distinct放在開頭
mysql> Select distinct(task_id),taskid from task;
查詢成功
與其餘函數使用時候,沒有位置限制以下
Select player_id,count(distinct(task_id))from task;
這種狀況下是正確的,能夠使用。
2 Distinct用法
a.在count計算不重複的記錄的時候能用到
好比SELECT COUNT( DISTINCT player_id ) FROM task;
就是計算talbebname表中id不一樣的記錄有多少條
b,在須要返回記錄不一樣的id的具體值的時候能夠用
好比SELECT DISTINCT player_id FROM task;
返回talbebname表中不一樣的id的具體的值
c.上面的狀況2對於須要返回mysql表中2列以上的結果時會有歧義
好比SELECT DISTINCT player_id, task_id FROM task;
實際上返回的是player_id與task_id同時不相同的結果,也就是DISTINCT同時做用了兩個字段,必須得player_id與task_id都相同的才被排除了,與咱們指望的結果不同,咱們指望的是player_id不一樣被過濾
在這種狀況下,distinct同時做用了兩個字段,player_id,task_id
d.這時候能夠考慮使用group_concat函數來進行排除,不過這個mysql函數是在mysql4.1以上才支持的
e. 其實還有另一種解決方式,就是使用
SELECT player_id, task_id, count(DISTINCT player_id) FROM task.
雖然這樣的返回結果多了一列無用的count數據(有時也許就須要這個數據)
f 同時咱們還能夠利用下面的方式解決b遇到的歧義問題經過group by 分組
select player_id,task_id from task group by player_id
9:explain出來的各類item的意義
MySQL Explain詳解
原文連接:http://www.cnblogs.com/lurenjiashuo/p/mysql-explain-detail.html
在平常工做中,咱們會有時會開慢查詢去記錄一些執行時間比較久的SQL語句,找出這些SQL語句並不意味着完事了,些時咱們經常用到explain這個命令來查看一個這些SQL語句的執行計劃,查看該SQL語句有沒有使用上了索引,有沒有作全表掃描,這均可以經過explain命令來查看。因此咱們深刻了解MySQL的基於開銷的優化器,還能夠得到不少可能被優化器考慮到的訪問策略的細節,以及當運行SQL語句時哪一種策略預計會被優化器採用。(QEP:sql生成一個執行計劃query Execution plan)
mysql> explain select * from servers;
+----+-------------+---------+------+---------------+------+---------+------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+------+---------------+------+---------+------+------+-------+
| 1 | SIMPLE | servers | ALL | NULL | NULL | NULL | NULL | 1 | NULL |
+----+-------------+---------+------+---------------+------+---------+------+------+-------+
1 row in set (0.03 sec)
expain出來的信息有10列,分別是id、select_type、table、type、possible_keys、key、key_len、ref、rows、Extra,下面對這些字段出現的可能進行解釋:
1、 id
個人理解是SQL執行的順序的標識,SQL從大到小的執行
2、select_type
示查詢中每一個select子句的類型
(1) SIMPLE(簡單SELECT,不使用UNION或子查詢等)
(2) PRIMARY(查詢中若包含任何複雜的子部分,最外層的select被標記爲PRIMARY)
(3) UNION(UNION中的第二個或後面的SELECT語句)
(4) DEPENDENT UNION(UNION中的第二個或後面的SELECT語句,取決於外面的查詢)
(5) UNION RESULT(UNION的結果)
(6) SUBQUERY(子查詢中的第一個SELECT)
(7) DEPENDENT SUBQUERY(子查詢中的第一個SELECT,取決於外面的查詢)
(8) DERIVED(派生表的SELECT, FROM子句的子查詢)
(9) UNCACHEABLE SUBQUERY(一個子查詢的結果不能被緩存,必須從新評估外連接的第一行)
3、table
顯示這一行的數據是關於哪張表的,有時不是真實的表名字,看到的是derivedx(x是個數字,個人理解是第幾步執行的結果)
mysql> explain select * from (select * from ( select * from t1 where id=2602) a) b;
+----+-------------+------------+--------+-------------------+---------+---------+------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+--------+-------------------+---------+---------+------+------+-------+
| 1 | PRIMARY |
| 2 | DERIVED |
| 3 | DERIVED | t1 | const | PRIMARY,idx_t1_id | PRIMARY | 4 | | 1 | |
+----+-------------+------------+--------+-------------------+---------+---------+------+------+-------+
4、type
表示MySQL在表中找到所需行的方式,又稱「訪問類型」。
經常使用的類型有: ALL, index, range, ref, eq_ref, const, system, NULL(從左到右,性能從差到好)
ALL:Full Table Scan, MySQL將遍歷全表以找到匹配的行
index: Full Index Scan,index與ALL區別爲index類型只遍歷索引樹
range:只檢索給定範圍的行,使用一個索引來選擇行
ref: 表示上述表的鏈接匹配條件,即哪些列或常量被用於查找索引列上的值
eq_ref: 相似ref,區別就在使用的索引是惟一索引,對於每一個索引鍵值,表中只有一條記錄匹配,簡單來講,就是多表鏈接中使用primary key或者 unique key做爲關聯條件
const、system: 當MySQL對查詢某部分進行優化,並轉換爲一個常量時,使用這些類型訪問。如將主鍵置於where列表中,MySQL就能將該查詢轉換爲一個常量,system是const類型的特例,當查詢的表只有一行的狀況下,使用system
NULL: MySQL在優化過程當中分解語句,執行時甚至不用訪問表或索引,例如從一個索引列裏選取最小值能夠經過單獨索引查找完成。
5、possible_keys
指出MySQL能使用哪一個索引在表中找到記錄,查詢涉及到的字段上若存在索引,則該索引將被列出,但不必定被查詢使用
該列徹底獨立於EXPLAIN輸出所示的表的次序。這意味着在possible_keys中的某些鍵實際上不能按生成的表次序使用。
若是該列是NULL,則沒有相關的索引。在這種狀況下,能夠經過檢查WHERE子句看是否它引用某些列或適合索引的列來提升你的查詢性能。若是是這樣,創造一個適當的索引而且再次用EXPLAIN檢查查詢
6、Key
key列顯示MySQL實際決定使用的鍵(索引)
若是沒有選擇索引,鍵是NULL。要想強制MySQL使用或忽視possible_keys列中的索引,在查詢中使用FORCE INDEX、USE INDEX或者IGNORE INDEX。
7、key_len
表示索引中使用的字節數,可經過該列計算查詢中使用的索引的長度(key_len顯示的值爲索引字段的最大可能長度,並不是實際使用長度,即key_len是根據表定義計算而得,不是經過表內檢索出的)
不損失精確性的狀況下,長度越短越好
8、ref
表示上述表的鏈接匹配條件,即哪些列或常量被用於查找索引列上的值
9、rows
表示MySQL根據表統計信息及索引選用狀況,估算的找到所需的記錄所須要讀取的行數
10、Extra
該列包含MySQL解決查詢的詳細信息,有如下幾種狀況:
Using where:列數據是從僅僅使用了索引中的信息而沒有讀取實際的行動的表返回的,這發生在對錶的所有的請求列都是同一個索引的部分的時候,表示mysql服務器將在存儲引擎檢索行後再進行過濾
Using temporary:表示MySQL須要使用臨時表來存儲結果集,常見於排序和分組查詢
Using filesort:MySQL中沒法利用索引完成的排序操做稱爲「文件排序」
Using join buffer:改值強調了在獲取鏈接條件時沒有使用索引,而且須要鏈接緩衝區來存儲中間結果。若是出現了這個值,那應該注意,根據查詢的具體狀況可能須要添加索引來改進能。
Impossible where:這個值強調了where語句會致使沒有符合條件的行。
Select tables optimized away:這個值意味着僅經過使用索引,優化器可能僅從聚合函數結果中返回一行
總結:
• EXPLAIN不會告訴你關於觸發器、存儲過程的信息或用戶自定義函數對查詢的影響狀況
• EXPLAIN不考慮各類Cache
• EXPLAIN不能顯示MySQL在執行查詢時所做的優化工做
• 部分統計信息是估算的,並不是精確值
• EXPALIN只能解釋SELECT操做,其餘操做要重寫爲SELECT後查看執行計劃。
參考資料:http://dev.mysql.com/doc/refman/5.5/en/explain-output.html
http://www.cnitblog.com/aliyiyi08/archive/2008/09/09/48878.html
http://www.cnblogs.com/gomysql/p/3720123.html
10:profile的意義以及使用場景
mysql使用profile分析語句性能消耗
--查看profile是否開啓
mysql> show variables like '%profil%';
+------------------------+-------+
| Variable_name | Value |
+------------------------+-------+
| profiling | OFF | --開啓SQL語句剖析功能
| profiling_history_size | 15 | --設置保留profiling的數目,缺省爲15,範圍爲0至100,爲0時將禁用profiling
+------------------------+-------+
2 rows in set (0.00 sec)
--基於會話級別開啓
mysql> set profiling = 1; --關閉則用set profiling = off
Query OK, 0 rows affected (0.00 sec)
mysql> select distinct d.account,a.server_id from tab_appserver_user a
-> inner join tab_department_parent b on a.key_id = b.parent_id
-> inner join tab_department_member c on b.department_id = c.department_id and c.state=1
-> and c.isdefault=1 inner join tab_user_info d on c.user_id = d.user_id and d.state=1
-> where a.type=1
-> union
-> select distinct b.account,a.server_id from tab_appserver_user a
-> inner join tab_user_info b on a.key_id = b.user_id and b.state=1
-> where a.type=0;
--顯示緩存的profile
mysql> show profiles;
+----------+------------+-------------------------------------------------------+
| Query_ID | Duration | Query
1 | 0.86754250 | select distinct d.account,a.server_id from tab_appserver_user a
+----------+------------+-------------------------------------------------------+
4 rows in set (0.00 sec)
從上面能夠看到時間的消耗爲0.8秒
如下是具體的消耗,進行詳細的列出
mysql> show profile for query 1; --1是query_id
+--------------------------------+----------+
| Status | Duration |
+--------------------------------+----------+
| starting | 0.000018 |
| checking query cache for query | 0.000099 |
| Opening tables | 0.000963 |
| System lock | 0.000015 |
| Table lock | 0.000169 |
| optimizing | 0.000020 |
| statistics | 0.000027 |
| preparing | 0.000018 |
| Creating tmp table | 0.000055 |
| executing | 0.000003 |
| Copying to tmp table | 0.704845 | --最主要的消耗點
| Sending data | 0.130039 |
| optimizing | 0.000029 |
| statistics | 0.000029 |
| preparing | 0.000020 |
| Creating tmp table | 0.000142 |
| executing | 0.000003 |
| Copying to tmp table | 0.000086 |
| Sending data | 0.000067 |
| optimizing | 0.000004 |
| statistics | 0.000005 |
| preparing | 0.000005 |
| executing | 0.000002 |
| Sending data | 0.023963 |
| removing tmp table | 0.003420 |
| Sending data | 0.000005 |
| removing tmp table | 0.003308 |
| Sending data | 0.000006 |
| removing tmp table | 0.000007 |
| Sending data | 0.000009 |
| query end | 0.000003 |
| freeing items | 0.000144 |
| storing result in query cache | 0.000011 |
| logging slow query | 0.000003 |
| cleaning up | 0.000006 |
+--------------------------------+----------+
35 rows in set (0.00 sec)
--查看cpu的消耗狀況
mysql> show profile cpu for query 1;
+--------------------------------+----------+----------+------------+
| Status | Duration | CPU_user | CPU_system |
+--------------------------------+----------+----------+------------+
| starting | 0.000018 | NULL | NULL |
| checking query cache for query | 0.000099 | NULL | NULL |
| Opening tables | 0.000963 | NULL | NULL |
| System lock | 0.000015 | NULL | NULL |
| Table lock | 0.000169 | NULL | NULL |
| optimizing | 0.000020 | NULL | NULL |
| statistics | 0.000027 | NULL | NULL |
| preparing | 0.000018 | NULL | NULL |
| Creating tmp table | 0.000055 | NULL | NULL |
| executing | 0.000003 | NULL | NULL |
| Copying to tmp table | 0.704845 | NULL | NULL | --此項消耗cpu最多
| Sending data | 0.130039 | NULL | NULL |
| optimizing | 0.000029 | NULL | NULL |
| statistics | 0.000029 | NULL | NULL |
| preparing | 0.000020 | NULL | NULL |
| Creating tmp table | 0.000142 | NULL | NULL |
| executing | 0.000003 | NULL | NULL |
| Copying to tmp table | 0.000086 | NULL | NULL |
| Sending data | 0.000067 | NULL | NULL |
| optimizing | 0.000004 | NULL | NULL |
| statistics | 0.000005 | NULL | NULL |
| preparing | 0.000005 | NULL | NULL |
| executing | 0.000002 | NULL | NULL |
| Sending data | 0.023963 | NULL | NULL |
| removing tmp table | 0.003420 | NULL | NULL |
| Sending data | 0.000005 | NULL | NULL |
| removing tmp table | 0.003308 | NULL | NULL |
| Sending data | 0.000006 | NULL | NULL |
| removing tmp table | 0.000007 | NULL | NULL |
| Sending data | 0.000009 | NULL | NULL |
| query end | 0.000003 | NULL | NULL |
| freeing items | 0.000144 | NULL | NULL |
| storing result in query cache | 0.000011 | NULL | NULL |
| logging slow query | 0.000003 | NULL | NULL |
| cleaning up | 0.000006 | NULL | NULL |
+--------------------------------+----------+----------+------------+
35 rows in set (0.00 sec)
--查看內存消耗
mysql> show profile memory for query 1;
+--------------------------------+----------+
| Status | Duration |
+--------------------------------+----------+
| starting | 0.000018 |
| checking query cache for query | 0.000099 |
| Opening tables | 0.000963 |
| System lock | 0.000015 |
| Table lock | 0.000169 |
| optimizing | 0.000020 |
| statistics | 0.000027 |
| preparing | 0.000018 |
| Creating tmp table | 0.000055 |
| executing | 0.000003 |
| Copying to tmp table | 0.704845 |
| Sending data | 0.130039 |
| optimizing | 0.000029 |
| statistics | 0.000029 |
| preparing | 0.000020 |
| Creating tmp table | 0.000142 |
| executing | 0.000003 |
| Copying to tmp table | 0.000086 |
| Sending data | 0.000067 |
| optimizing | 0.000004 |
| statistics | 0.000005 |
| preparing | 0.000005 |
| executing | 0.000002 |
| Sending data | 0.023963 |
| removing tmp table | 0.003420 |
| Sending data | 0.000005 |
| removing tmp table | 0.003308 |
| Sending data | 0.000006 |
| removing tmp table | 0.000007 |
| Sending data | 0.000009 |
| query end | 0.000003 |
| freeing items | 0.000144 |
| storing result in query cache | 0.000011 |
| logging slow query | 0.000003 |
| cleaning up | 0.000006 |
+--------------------------------+----------+
--查看io及cpu的消耗
mysql> show profile block io,cpu for query 1;
+--------------------------------+----------+----------+------------+--------------+---------------+
| Status | Duration | CPU_user | CPU_system | Block_ops_in | Block_ops_out |
+--------------------------------+----------+----------+------------+--------------+---------------+
| starting | 0.000018 | NULL | NULL | NULL | NULL |
| checking query cache for query | 0.000099 | NULL | NULL | NULL | NULL |
| Opening tables | 0.000963 | NULL | NULL | NULL | NULL |
| System lock | 0.000015 | NULL | NULL | NULL | NULL |
| Table lock | 0.000169 | NULL | NULL | NULL | NULL |
| optimizing | 0.000020 | NULL | NULL | NULL | NULL |
| statistics | 0.000027 | NULL | NULL | NULL | NULL |
| preparing | 0.000018 | NULL | NULL | NULL | NULL |
| Creating tmp table | 0.000055 | NULL | NULL | NULL | NULL |
| executing | 0.000003 | NULL | NULL | NULL | NULL |
| Copying to tmp table | 0.704845 | NULL | NULL | NULL | NULL |
| Sending data | 0.130039 | NULL | NULL | NULL | NULL |
| optimizing | 0.000029 | NULL | NULL | NULL | NULL |
| statistics | 0.000029 | NULL | NULL | NULL | NULL |
| preparing | 0.000020 | NULL | NULL | NULL | NULL |
| Creating tmp table | 0.000142 | NULL | NULL | NULL | NULL |
| executing | 0.000003 | NULL | NULL | NULL | NULL |
| Copying to tmp table | 0.000086 | NULL | NULL | NULL | NULL |
| Sending data | 0.000067 | NULL | NULL | NULL | NULL |
| optimizing | 0.000004 | NULL | NULL | NULL | NULL |
| statistics | 0.000005 | NULL | NULL | NULL | NULL |
| preparing | 0.000005 | NULL | NULL | NULL | NULL |
| executing | 0.000002 | NULL | NULL | NULL | NULL |
| Sending data | 0.023963 | NULL | NULL | NULL | NULL |
| removing tmp table | 0.003420 | NULL | NULL | NULL | NULL |
| Sending data | 0.000005 | NULL | NULL | NULL | NULL |
| removing tmp table | 0.003308 | NULL | NULL | NULL | NULL |
| Sending data | 0.000006 | NULL | NULL | NULL | NULL |
| removing tmp table | 0.000007 | NULL | NULL | NULL | NULL |
| Sending data | 0.000009 | NULL | NULL | NULL | NULL |
| query end | 0.000003 | NULL | NULL | NULL | NULL |
| freeing items | 0.000144 | NULL | NULL | NULL | NULL |
| storing result in query cache | 0.000011 | NULL | NULL | NULL | NULL |
| logging slow query | 0.000003 | NULL | NULL | NULL | NULL |
| cleaning up | 0.000006 | NULL | NULL | NULL | NULL |
+--------------------------------+----------+----------+------------+--------------+---------------+
35 rows in set (0.00 sec)
--使用查詢語句對消耗進行排序
mysql> SELECT STATE, SUM(DURATION) AS Total_R,ROUND( 100 * SUM(DURATION) / (SE
CT SUM(DURATION)
-> FROM INFORMATION_SCHEMA.PROFILING WHERE QUERY_ID = 1), 2) AS Pct_R, CO
T() AS Calls,SUM(DURATION) / COUNT() AS "R/Call"
-> FROM INFORMATION_SCHEMA.PROFILING WHERE QUERY_ID = 1 GROUP BY STATE O
ER BY Total_R DESC;
+--------------------------------+----------+-------+-------+--------------+
| STATE | Total_R | Pct_R | Calls | R/Call |
+--------------------------------+----------+-------+-------+--------------+
| Copying to tmp table | 0.704931 | 81.26 | 2 | 0.3524655000 |
| Sending data | 0.154089 | 17.76 | 6 | 0.0256815000 |
| removing tmp table | 0.006735 | 0.78 | 3 | 0.0022450000 |
| Opening tables | 0.000963 | 0.11 | 1 | 0.0009630000 |
| Creating tmp table | 0.000197 | 0.02 | 2 | 0.0000985000 |
| Table lock | 0.000169 | 0.02 | 1 | 0.0001690000 |
| freeing items | 0.000144 | 0.02 | 1 | 0.0001440000 |
| checking query cache for query | 0.000099 | 0.01 | 1 | 0.0000990000 |
| statistics | 0.000061 | 0.01 | 3 | 0.0000203333 |
| optimizing | 0.000053 | 0.01 | 3 | 0.0000176667 |
| preparing | 0.000043 | 0.00 | 3 | 0.0000143333 |
| starting | 0.000018 | 0.00 | 1 | 0.0000180000 |
| System lock | 0.000015 | 0.00 | 1 | 0.0000150000 |
| storing result in query cache | 0.000011 | 0.00 | 1 | 0.0000110000 |
| executing | 0.000008 | 0.00 | 3 | 0.0000026667 |
| cleaning up | 0.000006 | 0.00 | 1 | 0.0000060000 |
| logging slow query | 0.000003 | 0.00 | 1 | 0.0000030000 |
| query end | 0.000003 | 0.00 | 1 | 0.0000030000 |
+--------------------------------+----------+-------+-------+--------------+
18 rows in set (0.01 sec)
--最後說明:
profile是一個很是量化的子標,能夠根據這些量化指標來比較各項資源的消耗,有利於咱們對該語句的總體把控
11:mysql如何實現高效分頁
MySQL高效分頁解決方案集(轉)
好久之前的一次面試中,被面試官問到這個問題,因爲平時用到的分頁方法很少,只從索引、分表、使用子查詢精準定位偏移之外,沒有使用到其它方法。
後來在看其它博客看到了一些不一樣的方案,也一直沒有整理。今天有時間,整理出來,分享給你們。
一,最多見MYSQL最基本的分頁方式:
select * from content order by id desc limit 0, 10
在中小數據量的狀況下,這樣的SQL足夠用了,惟一須要注意的問題就是確保使用了索引。隨着數據量的增長,頁數會愈來愈多,查看後幾頁的SQL就可能相似:
select * from content order by id desc limit 10000, 10
一言以蔽之,就是越日後分頁,LIMIT語句的偏移量就會越大,速度也會明顯變慢。
此時,咱們能夠經過2種方式:
一,子查詢的分頁方式來提升分頁效率,飄易用的SQL語句以下:
SELECT * FROM content
WHERE id> (SELECT id FROM content
ORDER BY id desc LIMIT ".(\(page-1)*\)pagesize.", 1) ORDER BY id desc LIMIT $pagesize
爲何會這樣呢?由於子查詢是在索引上完成的,而普通的查詢時在數據文件上完成的,一般來講,索引文件要比數據文件小得多,因此操做起來也會更有效率。(via)經過explain SQL語句發現:子查詢使用了索引!
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY content range PRIMARY PRIMARY 4 NULL 6264 Using where
2 SUBQUERY content index NULL PRIMARY 4 NULL 27085 Using index
通過飄易的實測,使用子查詢的分頁方式的效率比純LIMIT提升了14-20倍!
二,JOIN分頁方式
select * FROM content
AS t1
JOIN (SELECT id FROM content
ORDER BY id desc LIMIT ".(\(page-1)*\)pagesize.", 1) AS t2
WHERE t1.id
通過個人測試,join分頁和子查詢分頁的效率基本在一個等級上,消耗的時間也基本一致。explain SQL語句:
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY system NULL NULL NULL NULL 1
1 PRIMARY t1 range PRIMARY PRIMARY 4 NULL 6264 Using where
2 DERIVED content index NULL PRIMARY 4 NULL 27085 Using index
三,使用MYSQL的FOUND_ROWS()函數
Mysql FOUND_ROWS() 函數結合SQL_CALC_FOUND_ROWS在SELECT中能夠獲得兩個結果:
一、垂直(縱向)切分
垂直切分常見有垂直分庫和垂直分表兩種。
垂直分庫就是根據業務耦合性,將關聯度低的不一樣表存儲在不一樣的數據庫。作法與大系統拆分爲多個小系統相似,按業務分類進行獨立劃分。與"微服務治理"的作法類似,每一個微服務使用單獨的一個數據庫。如圖:
垂直分表是基於數據庫中的"列"進行,某個表字段較多,能夠新建一張擴展表,將不常常用或字段長度較大的字段拆分出去到擴展表中。在字段不少的狀況下(例如一個大表有100多個字段),經過"大表拆小表",更便於開發與維護,也能避免跨頁問題,MySQL底層是經過數據頁存儲的,一條記錄佔用空間過大會致使跨頁,形成額外的性能開銷。另外數據庫以行爲單位將數據加載到內存中,這樣表中字段長度較短且訪問頻率較高,內存能加載更多的數據,命中率更高,減小了磁盤IO,從而提高了數據庫性能。
垂直切分的優勢:
解決業務系統層面的耦合,業務清晰
與微服務的治理相似,也能對不一樣業務的數據進行分級管理、維護、監控、擴展等
高併發場景下,垂直切分必定程度的提高IO、數據庫鏈接數、單機硬件資源的瓶頸
缺點:
部分表沒法join,只能經過接口聚合方式解決,提高了開發的複雜度
分佈式事務處理複雜
依然存在單表數據量過大的問題(須要水平切分)
二、水平(橫向)切分
當一個應用難以再細粒度的垂直切分,或切分後數據量行數巨大,存在單庫讀寫、存儲性能瓶頸,這時候就須要進行水平切分了。
水平切分分爲庫內分表和分庫分表,是根據表內數據內在的邏輯關係,將同一個表按不一樣的條件分散到多個數據庫或多個表中,每一個表中只包含一部分數據,從而使得單個表的數據量變小,達到分佈式的效果。如圖所示:
庫內分表只解決了單一表數據量過大的問題,但沒有將表分佈到不一樣機器的庫上,所以對於減輕MySQL數據庫的壓力來講,幫助不是很大,你們仍是競爭同一個物理機的CPU、內存、網絡IO,最好經過分庫分表來解決。
水平切分的優勢:
不存在單庫數據量過大、高併發的性能瓶頸,提高系統穩定性和負載能力
應用端改造較小,不須要拆分業務模塊
缺點:
跨分片的事務一致性難以保證
跨庫的join關聯查詢性能較差
數據屢次擴展難度和維護量極大
水平切分後同一張表會出如今多個數據庫/表中,每一個庫/表的內容不一樣。幾種典型的數據分片規則爲:
一、根據數值範圍
按照時間區間或ID區間來切分。例如:按日期將不一樣月甚至是日的數據分散到不一樣的庫中;將userId爲1~9999的記錄分到第一個庫,10000~20000的分到第二個庫,以此類推。某種意義上,某些系統中使用的"冷熱數據分離",將一些使用較少的歷史數據遷移到其餘庫中,業務功能上只提供熱點數據的查詢,也是相似的實踐。
這樣的優勢在於:
單表大小可控
自然便於水平擴展,後期若是想對整個分片集羣擴容時,只須要添加節點便可,無需對其餘分片的數據進行遷移
使用分片字段進行範圍查找時,連續分片可快速定位分片進行快速查詢,有效避免跨分片查詢的問題。
缺點:
熱點數據成爲性能瓶頸。連續分片可能存在數據熱點,例如按時間字段分片,有些分片存儲最近時間段內的數據,可能會被頻繁的讀寫,而有些分片存儲的歷史數據,則不多被查詢
二、根據數值取模
通常採用hash取模mod的切分方式,例如:將 Customer 表根據 cusno 字段切分到4個庫中,餘數爲0的放到第一個庫,餘數爲1的放到第二個庫,以此類推。這樣同一個用戶的數據會分散到同一個庫中,若是查詢條件帶有cusno字段,則可明肯定位到相應庫去查詢。
優勢:
數據分片相對比較均勻,不容易出現熱點和併發訪問的瓶頸
缺點:
後期分片集羣擴容時,須要遷移舊的數據(使用一致性hash算法能較好的避免這個問題)
容易面臨跨分片查詢的複雜問題。好比上例中,若是頻繁用到的查詢條件中不帶cusno時,將會致使沒法定位數據庫,從而須要同時向4個庫發起查詢,再在內存中合併數據,取最小集返回給應用,分庫反而成爲拖累。
二. 分庫分錶帶來的問題
分庫分表能有效的環節單機和單庫帶來的性能瓶頸和壓力,突破網絡IO、硬件資源、鏈接數的瓶頸,同時也帶來了一些問題。下面將描述這些技術挑戰以及對應的解決思路。
一、事務一致性問題
分佈式事務
當更新內容同時分佈在不一樣庫中,不可避免會帶來跨庫事務問題。跨分片事務也是分佈式事務,沒有簡單的方案,通常可以使用"XA協議"和"兩階段提交"處理。
分佈式事務能最大限度保證了數據庫操做的原子性。但在提交事務時須要協調多個節點,推後了提交事務的時間點,延長了事務的執行時間。致使事務在訪問共享資源時發生衝突或死鎖的機率增高。隨着數據庫節點的增多,這種趨勢會愈來愈嚴重,從而成爲系統在數據庫層面上水平擴展的枷鎖。
最終一致性
對於那些性能要求很高,但對一致性要求不高的系統,每每不苛求系統的實時一致性,只要在容許的時間段內達到最終一致性便可,可採用事務補償的方式。與事務在執行中發生錯誤後當即回滾的方式不一樣,事務補償是一種過後檢查補救的措施,一些常見的實現方法有:對數據進行對帳檢查,基於日誌進行對比,按期同標準數據來源進行同步等等。事務補償還要結合業務系統來考慮。
二、跨節點關聯查詢 join 問題
切分以前,系統中不少列表和詳情頁所需的數據能夠經過sql join來完成。而切分以後,數據可能分佈在不一樣的節點上,此時join帶來的問題就比較麻煩了,考慮到性能,儘可能避免使用join查詢。
解決這個問題的一些方法:
1)全局表
全局表,也可看作是"數據字典表",就是系統中全部模塊均可能依賴的一些表,爲了不跨庫join查詢,能夠將這類表在每一個數據庫中都保存一份。這些數據一般不多會進行修改,因此也不擔憂一致性的問題。
2)字段冗餘
一種典型的反範式設計,利用空間換時間,爲了性能而避免join查詢。例如:訂單表保存userId時候,也將userName冗餘保存一份,這樣查詢訂單詳情時就不須要再去查詢"買家user表"了。
但這種方法適用場景也有限,比較適用於依賴字段比較少的狀況。而冗餘字段的數據一致性也較難保證,就像上面訂單表的例子,買家修改了userName後,是否須要在歷史訂單中同步更新呢?這也要結合實際業務場景進行考慮。
3)數據組裝
在系統層面,分兩次查詢,第一次查詢的結果集中找出關聯數據id,而後根據id發起第二次請求獲得關聯數據。最後將得到到的數據進行字段拼裝。
4)ER分片
關係型數據庫中,若是能夠先肯定表之間的關聯關係,並將那些存在關聯關係的表記錄存放在同一個分片上,那麼就能較好的避免跨分片join問題。在1:1或1:n的狀況下,一般按照主表的ID主鍵切分。以下圖所示:
這樣一來,Data Node1上面的order訂單表與orderdetail訂單詳情表就能夠經過orderId進行局部的關聯查詢了,Data Node2上也同樣。
三、跨節點分頁、排序、函數問題
跨節點多庫進行查詢時,會出現limit分頁、order by排序等問題。分頁須要按照指定字段進行排序,當排序字段就是分片字段時,經過分片規則就比較容易定位到指定的分片;當排序字段非分片字段時,就變得比較複雜了。須要先在不一樣的分片節點中將數據進行排序並返回,而後將不一樣分片返回的結果集進行彙總和再次排序,最終返回給用戶。如圖所示:
上圖中只是取第一頁的數據,對性能影響還不是很大。可是若是取得頁數很大,狀況則變得複雜不少,由於各分片節點中的數據多是隨機的,爲了排序的準確性,須要將全部節點的前N頁數據都排序好作合併,最後再進行總體的排序,這樣的操做時很耗費CPU和內存資源的,因此頁數越大,系統的性能也會越差。
在使用Max、Min、Sum、Count之類的函數進行計算的時候,也須要先在每一個分片上執行相應的函數,而後將各個分片的結果集進行彙總和再次計算,最終將結果返回。如圖所示:
四、全局主鍵避重問題
在分庫分表環境中,因爲表中數據同時存在不一樣數據庫中,主鍵值平時使用的自增加將無用武之地,某個分區數據庫自生成的ID沒法保證全局惟一。所以須要單獨設計全局主鍵,以免跨庫主鍵重複問題。有一些常見的主鍵生成策略:
1)UUID
UUID標準形式包含32個16進制數字,分爲5段,形式爲8-4-4-4-12的36個字符,例如:550e8400-e29b-41d4-a716-446655440000
UUID是主鍵是最簡單的方案,本地生成,性能高,沒有網絡耗時。但缺點也很明顯,因爲UUID很是長,會佔用大量的存儲空間;另外,做爲主鍵創建索引和基於索引進行查詢時都會存在性能問題,在InnoDB下,UUID的無序性會引發數據位置頻繁變更,致使分頁。
2)結合數據庫維護主鍵ID表
在數據庫中創建 sequence 表:
CREATE TABLE sequence
( id
bigint(20) unsigned NOT NULL auto_increment, stub
char(1) NOT NULL default '', PRIMARY KEY (id
), UNIQUE KEY stub
(stub
) ) ENGINE=MyISAM;
stub字段設置爲惟一索引,同一stub值在sequence表中只有一條記錄,能夠同時爲多張表生成全局ID。sequence表的內容,以下所示:
+-------------------+------+ | id | stub |+-------------------+------+ | 72157623227190423 | a |+-------------------+------+
使用 MyISAM 存儲引擎而不是 InnoDB,以獲取更高的性能。MyISAM使用的是表級別的鎖,對錶的讀寫是串行的,因此不用擔憂在併發時兩次讀取同一個ID值。
當須要全局惟一的64位ID時,執行:
REPLACE INTO sequence (stub) VALUES ('a'); SELECT LAST_INSERT_ID();
這兩條語句是Connection級別的,select last_insert_id() 必須與 replace into 在同一數據庫鏈接下才能獲得剛剛插入的新ID。
使用replace into代替insert into好處是避免了錶行數過大,不須要另外按期清理。
此方案較爲簡單,但缺點也明顯:存在單點問題,強依賴DB,當DB異常時,整個系統都不可用。配置主從能夠增長可用性,但當主庫掛了,主從切換時,數據一致性在特殊狀況下難以保證。另外性能瓶頸限制在單臺MySQL的讀寫性能。
flickr團隊使用的一種主鍵生成策略,與上面的sequence表方案相似,但更好的解決了單點和性能瓶頸的問題。
這一方案的總體思想是:創建2個以上的全局ID生成的服務器,每一個服務器上只部署一個數據庫,每一個庫有一張sequence表用於記錄當前全局ID。表中ID增加的步長是庫的數量,起始值依次錯開,這樣能將ID的生成散列到各個數據庫上。以下圖所示:
由兩個數據庫服務器生成ID,設置不一樣的auto_increment值。第一臺sequence的起始值爲1,每次步長增加2,另外一臺的sequence起始值爲2,每次步長增加也是2。結果第一臺生成的ID都是奇數(1, 3, 5, 7 ...),第二臺生成的ID都是偶數(2, 4, 6, 8 ...)。
這種方案將生成ID的壓力均勻分佈在兩臺機器上。同時提供了系統容錯,第一臺出現了錯誤,能夠自動切換到第二臺機器上獲取ID。但有如下幾個缺點:系統添加機器,水平擴展時較複雜;每次獲取ID都要讀寫一次DB,DB的壓力仍是很大,只能靠堆機器來提高性能。
能夠基於flickr的方案繼續優化,使用批量的方式下降數據庫的寫壓力,每次獲取一段區間的ID號段,用完以後再去數據庫獲取,能夠大大減輕數據庫的壓力。以下圖所示:
仍是使用兩臺DB保證可用性,數據庫中只存儲當前的最大ID。ID生成服務每次批量拉取6個ID,先將max_id修改成5,當應用訪問ID生成服務時,就不須要訪問數據庫,從號段緩存中依次派發0~5的ID。當這些ID發完後,再將max_id修改成11,下次就能派發6~11的ID。因而,數據庫的壓力下降爲原來的1/6。
3)Snowflake分佈式自增ID算法
Twitter的snowflake算法解決了分佈式系統生成全局ID的需求,生成64位的Long型數字,組成部分:
第一位未使用
接下來41位是毫秒級時間,41位的長度能夠表示69年的時間
5位datacenterId,5位workerId。10位的長度最多支持部署1024個節點
最後12位是毫秒內的計數,12位的計數順序號支持每一個節點每毫秒產生4096個ID序列
這樣的好處是:毫秒數在高位,生成的ID總體上按時間趨勢遞增;不依賴第三方系統,穩定性和效率較高,理論上QPS約爲409.6w/s(1000*2^12),而且整個分佈式系統內不會產生ID碰撞;可根據自身業務靈活分配bit位。
不足就在於:強依賴機器時鐘,若是時鐘回撥,則可能致使生成ID重複。
綜上
結合數據庫和snowflake的惟一ID方案,能夠參考業界較爲成熟的解法:Leaf——美團點評分佈式ID生成系統,並考慮到了高可用、容災、分佈式下時鐘等問題。
五、數據遷移、擴容問題
當業務高速發展,面臨性能和存儲的瓶頸時,纔會考慮分片設計,此時就不可避免的須要考慮歷史數據遷移的問題。通常作法是先讀出歷史數據,而後按指定的分片規則再將數據寫入到各個分片節點中。此外還須要根據當前的數據量和QPS,以及業務發展的速度,進行容量規劃,推算出大概須要多少分片(通常建議單個分片上的單表數據量不超過1000W)
若是採用數值範圍分片,只須要添加節點就能夠進行擴容了,不須要對分片數據遷移。若是採用的是數值取模分片,則考慮後期的擴容問題就相對比較麻煩。
三. 何時考慮切分
下面講述一下何時須要考慮作數據切分。
一、能不切分儘可能不要切分
並非全部表都須要進行切分,主要仍是看數據的增加速度。切分後會在某種程度上提高業務的複雜度,數據庫除了承載數據的存儲和查詢外,協助業務更好的實現需求也是其重要工做之一。
不到萬不得已不用輕易使用分庫分表這個大招,避免"過分設計"和"過早優化"。分庫分表以前,不要爲分而分,先盡力去作力所能及的事情,例如:升級硬件、升級網絡、讀寫分離、索引優化等等。當數據量達到單表的瓶頸時候,再考慮分庫分表。
二、數據量過大,正常運維影響業務訪問
這裏說的運維,指:
1)對數據庫備份,若是單表太大,備份時須要大量的磁盤IO和網絡IO。例如1T的數據,網絡傳輸佔50MB時候,須要20000秒才能傳輸完畢,整個過程的風險都是比較高的
2)對一個很大的表進行DDL修改時,MySQL會鎖住全表,這個時間會很長,這段時間業務不能訪問此表,影響很大。若是使用pt-online-schema-change,使用過程當中會建立觸發器和影子表,也須要很長的時間。在此操做過程當中,都算爲風險時間。將數據表拆分,總量減小,有助於下降這個風險。
3)大表會常常訪問與更新,就更有可能出現鎖等待。將數據切分,用空間換時間,變相下降訪問壓力
三、隨着業務發展,須要對某些字段垂直拆分
舉個例子,假如項目一開始設計的用戶表以下:
id bigint #用戶的ID name varchar #用戶的名字 last_login_time datetime #最近登陸時間 personal_info text #私人信息 ..... #其餘信息字段
在項目初始階段,這種設計是知足簡單的業務需求的,也方便快速迭代開發。而當業務快速發展時,用戶量從10w激增到10億,用戶很是的活躍,每次登陸會更新 last_login_name 字段,使得 user 表被不斷update,壓力很大。而其餘字段:id, name, personal_info 是不變的或不多更新的,此時在業務角度,就要將 last_login_time 拆分出去,新建一個 user_time 表。
personal_info 屬性是更新和查詢頻率較低的,而且text字段佔據了太多的空間。這時候,就要對此垂直拆分出 user_ext 表了。
四、數據量快速增加
隨着業務的快速發展,單表中的數據量會持續增加,當性能接近瓶頸時,就須要考慮水平切分,作分庫分表了。此時必定要選擇合適的切分規則,提早預估好數據容量
五、安全性和可用性
雞蛋不要放在一個籃子裏。在業務層面上垂直切分,將不相關的業務的數據庫分隔,由於每一個業務的數據量、訪問量都不一樣,不能由於一個業務把數據庫搞掛而牽連到其餘業務。利用水平切分,當一個數據庫出現問題時,不會影響到100%的用戶,每一個庫只承擔業務的一部分數據,這樣總體的可用性就能提升。
四. 案例分析
一、用戶中心業務場景
用戶中心是一個很是常見的業務,主要提供用戶註冊、登陸、查詢/修改等功能,其核心表爲:
User(uid, login_name, passwd, sex, age, nickname) uid爲用戶ID, 主鍵 login_name, passwd, sex, age, nickname, 用戶屬性
任何脫離業務的架構設計都是耍流氓,在進行分庫分表前,須要對業務場景需求進行梳理:
用戶側:前臺訪問,訪問量較大,須要保證高可用和高一致性。主要有兩類需求:
用戶登陸:經過login_name/phone/email查詢用戶信息,1%請求屬於這種類型
用戶信息查詢:登陸以後,經過uid來查詢用戶信息,99%請求屬這種類型
運營側:後臺訪問,支持運營需求,按照年齡、性別、登錄時間、註冊時間等進行分頁的查詢。是內部系統,訪問量較低,對可用性、一致性的要求不高。
二、水平切分方法
當數據量愈來愈大時,須要對數據庫進行水平切分,上文描述的切分方法有"根據數值範圍"和"根據數值取模"。
"根據數值範圍":以主鍵uid爲劃分依據,按uid的範圍將數據水平切分到多個數據庫上。例如:user-db1存儲uid範圍爲0~1000w的數據,user-db2存儲uid範圍爲1000w~2000wuid數據。
優勢是:擴容簡單,若是容量不夠,只要增長新db便可。
不足是:請求量不均勻,通常新註冊的用戶活躍度會比較高,因此新的user-db2會比user-db1負載高,致使服務器利用率不平衡
"根據數值取模":也是以主鍵uid爲劃分依據,按uid取模的值將數據水平切分到多個數據庫上。例如:user-db1存儲uid取模得1的數據,user-db2存儲uid取模得0的uid數據。
優勢是:數據量和請求量分佈均均勻
不足是:擴容麻煩,當容量不夠時,新增長db,須要rehash。須要考慮對數據進行平滑的遷移。
三、非uid的查詢方法
水平切分後,對於按uid查詢的需求能很好的知足,能夠直接路由到具體數據庫。而按非uid的查詢,例如login_name,就不知道具體該訪問哪一個庫了,此時須要遍歷全部庫,性能會下降不少。
對於用戶側,能夠採用"創建非uid屬性到uid的映射關係"的方案;對於運營側,能夠採用"前臺與後臺分離"的方案。
3.一、創建非uid屬性到uid的映射關係
1)映射關係
例如:login_name不能直接定位到數據庫,能夠創建login_name→uid的映射關係,用索引表或緩存來存儲。當訪問login_name時,先經過映射表查詢出login_name對應的uid,再經過uid定位到具體的庫。
映射表只有兩列,能夠承載不少數據,當數據量過大時,也能夠對映射表再作水平切分。這類kv格式的索引結構,能夠很好的使用cache來優化查詢性能,並且映射關係不會頻繁變動,緩存命中率會很高。
2)基因法
分庫基因:假如經過uid分庫,分爲8個庫,採用uid%8的方式進行路由,此時是由uid的最後3bit來決定這行User數據具體落到哪一個庫上,那麼這3bit能夠看爲分庫基因。
上面的映射關係的方法須要額外存儲映射表,按非uid字段查詢時,還須要多一次數據庫或cache的訪問。若是想要消除多餘的存儲和查詢,能夠經過f函數取login_name的基因做爲uid的分庫基因。生成uid時,參考上文所述的分佈式惟一ID生成方案,再加上最後3位bit值=f(login_name)。當查詢login_name時,只需計算f(login_name)%8的值,就能夠定位到具體的庫。不過這樣須要提早作好容量規劃,預估將來幾年的數據量須要分多少庫,要預留必定bit的分庫基因。
3.二、前臺與後臺分離
對於用戶側,主要需求是以單行查詢爲主,須要創建login_name/phone/email到uid的映射關係,能夠解決這些字段的查詢問題。
而對於運營側,不少批量分頁且條件多樣的查詢,這類查詢計算量大,返回數據量大,對數據庫的性能消耗較高。此時,若是和用戶側公用同一批服務或數據庫,可能由於後臺的少許請求,佔用大量數據庫資源,而致使用戶側訪問性能下降或超時。
這類業務最好採用"前臺與後臺分離"的方案,運營側後臺業務抽取獨立的service和db,解決和前臺業務系統的耦合。因爲運營側對可用性、一致性的要求不高,能夠不訪問實時庫,而是經過binlog異步同步數據到運營庫進行訪問。在數據量很大的狀況下,還能夠使用ES搜索引擎或Hive來知足後臺複雜的查詢方式。
五. 支持分庫分表中間件
站在巨人的肩膀上能省力不少,目前分庫分表已經有一些較爲成熟的開源解決方案:
sharding-jdbc(噹噹)
TSharding(蘑菇街)
Atlas(奇虎360)
Cobar(阿里巴巴)
MyCAT(基於Cobar)
Oceanus(58同城)
Vitess(谷歌)
六. 參考
數據庫分佈式架構掃盲——分庫分表(及銀行核心系統適用性思考)
分庫分表的思想
水平分庫分表的關鍵步驟以及可能遇到的問題
從原則、方案、策略及難點闡述分庫分表
Leaf——美團點評分佈式ID生成系統
數據庫水平切分架構實踐-【架構師之路】公衆號
13:新建立的數據庫,須要調整哪些參數
MySQL數據庫建表、優化、算法、分區分庫分表總結
11.11 智慧上雲
雲服務器企業新用戶優先購,享雙11同等價格
在這篇文章中:
建表
1、主鍵設置
2、數據庫表建立注意事項
3、字段設置
優化
1、爲何使用數據索引能提升效率?
2、關於 MySQL 聯合索引
3、關於MVVC
4、行級鎖定的優缺點
5、MySQL優化
6、key和index的區別
算法
1、B+樹索引和哈希索引的區別?
2、哈希索引的優點:
3、B 樹和 B+ 樹的區別?
4、爲何說B+比B樹更適合實際應用中操做系統的文件索引和數據庫索引?
5、四種隔離級別
6、Mysql 中 MyISAM 和 InnoDB 的區別有哪些?
分區
建表
1、主鍵設置
主鍵爲何不推薦有業務含義?
一、由於任何有業務含義的列都有改變的可能性,主鍵一旦帶上了業務含義,那麼主鍵就有可能發生變動。主鍵一旦發生變動,該數據在磁盤上的存儲位置就會發生變動,有可能會引起頁分裂,產生空間碎片。
二、帶有業務含義的主鍵,不必定是順序自增的。那麼就會致使數據的插入順序,並不能保證後面插入數據的主鍵必定比前面的數據大。若是出現了,後面插入數據的主鍵比前面的小,就有可能引起頁分裂,產生空間碎片
使用自增主鍵有什麼好處
一、若是咱們定義了主鍵(PRIMARY KEY),那麼InnoDB會選擇主鍵做爲彙集索引。
若是沒有顯式定義主鍵,則InnoDB會選擇第一個不包含有NULL值的惟一索引做爲主鍵索引。
若是也沒有這樣的惟一索引,則InnoDB會選擇內置6字節長的ROWID做爲隱含的彙集索引(ROWID隨着行記錄的寫入而主鍵遞增,這個ROWID不像ORACLE的ROWID那樣可引用,是隱含的)。因此最好必定要設一個主鍵。
二、數據記錄自己被存於主索引(一顆B+Tree)的葉子節點上,這就要求同一個葉子節點內(大小爲一個內存頁或磁盤頁)的各條數據記錄按主鍵順序存放
所以每當有一條新的記錄插入時,MySQL會根據其主鍵將其插入適當的節點和位置,若是頁面達到裝載因子(InnoDB默認爲15/16),則開闢一個新的頁(節點)
三、若是表使用自增主鍵,那麼每次插入新的記錄,記錄就會順序添加到當前索引節點的後續位置,當一頁寫滿,就會自動開闢一個新的頁
四、若是使用非自增主鍵(如UUID等),因爲每次插入主鍵的值近似於隨機,所以每次新紀錄都要被插到現有索引頁得中間某個位置
此時MySQL不得不爲了將新記錄插到合適位置而移動數據,甚至目標頁面可能已經被回寫到磁盤上而從緩存中清掉,此時又要從磁盤上讀回來,這增長了不少開銷
同時頻繁的移動、分頁操做形成了大量的碎片,獲得了不夠緊湊的索引結構,後續不得不經過OPTIMIZE TABLE來重建表並優化填充頁面。
自增主鍵用完了怎麼辦
無符號整型存儲範圍爲0~4294967295,約43億。一旦自增id達到最大值,此時數據繼續插入是會報一個主鍵衝突異常。此時將Int類型改成BigInt類型,就不用考慮達到最大值這個問題。每秒十億條數據,跑100年,都尚未達到BigInt的上限。
若是已經上線,如何在線更改表結構
一、使用mysql5.6+提供的在線修改功能
能夠支持在線修改表結構的pt-osc/gh-ost第三方工具,防止表阻塞。執行ALTER語句
mysql> ALTER TABLE table_name CHANGE old_field_name new_field_name field_type;
二、改從庫表結構,而後主從切換
惟一須要注意的是,主從切換過程當中可能會有數據丟失的狀況!
三、使用全局惟一的ID號生成策略來支持分庫分表
自增主鍵id的數據範圍爲0~2147483648,也就是單表21億條數據,通常達不到最大值,咱們就分庫分表了
2、數據庫表建立注意事項
一、字段名及字段配製合理性
剔除關係不密切的字段;
字段命名要有規則及相對應的含義(不要一部分英文,一部分拼音,還有相似a.b.c這樣不明含義的字段);
字段命名儘可能不要使用縮寫(大多數縮寫都不能明確字段含義);
字段不要大小寫混用(想要具備可讀性,多個英文單詞可以使用下劃線形式鏈接);
字段名不要使用保留字或者關鍵字;
保持字段名和類型的一致性;
慎重選擇數字類型;
給文本字段留足餘量;
二、系統特殊字段處理及建成後建議
添加刪除標記(例如操做人、刪除時間);
創建版本機制;
三、表結構合理性配置
多型字段的處理,就是表中是否存在字段可以分解成更小獨立的幾部分(例如:人能夠分爲男人和女人);
多值字段的處理,能夠將表分爲三張表,這樣使得檢索和排序更加有調理,且保證數據的完整性!
四、其它建議
對於大數據字段,獨立表進行存儲,以便影響性能(例如:簡介字段);
使用varchar類型代替char,由於varchar會動態分配長度,char指定長度是固定的;
給表建立主鍵,對於沒有主鍵的表,在查詢和索引定義上有必定的影響;
避免表字段運行爲null,建議設置默認值(例如:int類型設置默認值爲0)在索引查詢上,效率立顯;
創建索引,最好創建在惟一和非空的字段上,創建太多的索引對後期插入、更新都存在必定的影響(考慮實際狀況來建立);
3、字段設置
一、表示枚舉的字段爲何不用enum類型? 通常用tinyint類型,由於: (1)ENUM類型的ORDER BY操做效率低,須要額外操做 (2)若是枚舉值是數值,有陷阱 舉個例子,表結構以下
CREATE TABLE test (foobar ENUM('0', '1', '2'));
此時,你執行語句
mysql> INSERT INTO test VALUES (1);
查詢出的結果爲foobar:0
就產生了一個坑爹的結果。 插入語句應該像下面這麼寫,插入的纔是1
mysql> INSERT INTO test VALUES (1
);
二、貨幣字段用什麼類型?回答:若是貨幣單位是分,能夠用Int類型。若是堅持用元,用Decimal。 千萬不要答float和double,由於float和double是以二進制存儲的,因此有必定的偏差。 打個比方,你建一個列以下
CREATE TABLE t
(price
float(10,2) DEFAULT NULL,) ENGINE=InnoDB DEFAULT CHARSET=utf8
而後insert給price列一個數據爲1234567.23,你會發現顯示出來的數據變爲1234567.25,精度失準!
三、時間字段用什麼類型? (1)varchar,若是用varchar類型來存時間,優勢在於顯示直觀。可是坑的地方也是挺多的。好比,插入的數據沒有校驗,你可能某天就發現一條數據爲2013111的數據,請問這是表明2013年1月11日,仍是2013年11月1日? 其次,作時間比較運算,你須要用STR_TO_DATE等函數將其轉化爲時間類型,你會發現這麼寫是沒法命中索引的。數據量一大,是個坑!
(2)timestamp,該類型是四個字節的整數,它能表示的時間範圍爲1970-01-01 08:00:01到2038-01-19 11:14:07。2038年之後的時間,是沒法用timestamp類型存儲的。 可是它有一個優點,timestamp類型是帶有時區信息的。一旦你係統中的時區發生改變,例如你修改了時區
SET TIME_ZONE = "america/new_york";
你會發現,項目中的該字段的值本身會發生變動。這個特性用來作一些國際化大項目,跨時區的應用時,特別注意!
(3)datetime,datetime儲存佔用8個字節,它存儲的時間範圍爲1000-01-01 00:00:00 ~ 9999-12-31 23:59:59。顯然,存儲時間範圍更大。可是它坑的地方在於,他存儲的是時間絕對值,不帶有時區信息。若是你改變數據庫的時區,該項的值不會本身發生變動!
(4)bigint,也是8個字節,本身維護一個時間戳,表示範圍比timestamp大多了,就是要本身維護,不大方便。
四、爲何不直接存儲圖片、音頻、視頻等大容量內容? 在實際應用中,都是用HDFS來存儲文件。而後mysql中,只存文件的存放路徑。mysql中有兩個字段類型被用來設計存放大容量文件,也就是text和blob類型。可是在生產中,基本不用這兩個類型! 主要緣由有以下兩點
(1)Mysql內存臨時表不支持TEXT、BLOB這樣的大數據類型,若是查詢中包含這樣的數據,在排序等操做時,就不能使用內存臨時表,必須使用磁盤臨時表進行。致使查詢效率緩慢
(2)binlog內容太多。由於你數據內容比較大,就會形成binlog內容比較多。你們也知道,主從同步是靠binlog進行同步,binlog太大了,就會致使主從同步效率問題!
五、字段爲何要定義爲NOT NULL? (1)索引性能很差
Mysql難以優化引用可空列查詢,它會使索引、索引統計和值更加複雜。可空列須要更多的存儲空間,還須要mysql內部進行特殊處理。可空列被索引後,每條記錄都須要一個額外的字節,還能致使MYisam 中固定大小的索引變成可變大小的索引。 —— 出自《高性能mysql第二版》
(2)查詢會出現一些不可預料的結果 這裏舉一個例子,你們就懂了。假設,表結構以下
create table table_2 (id
INT (11) NOT NULL, name varchar(20) NOT NULL )
表數據是這樣的
id
name
1
張三
3
null
5
李四
7
null
你執行語句
select count(name) from table_2;
你會發現結果爲2,可是其實是有四條數據的!相似的查詢問題,其實有不少,不一一列舉。 記住,由於null列的存在,會出現不少出人意料的結果,從而浪費開發時間去排查Bug.
優化
1、爲何使用數據索引能提升效率?
數據索引的存儲是有序的
在有序的狀況下,經過索引查詢一個數據是無需遍歷索引記錄的
極端狀況下,數據索引的查詢效率爲二分法查詢效率,趨近於 log2(N)
2、關於 MySQL 聯合索引
一、聯合索引是兩個或更多個列上的索引。
對於聯合索引:Mysql從左到右的使用索引中的字段,一個查詢能夠只使用索引中的一部份,但只能是最左側部分。
例如索引是key index (a,b,c). 能夠支持a 、 a,b 、 a,b,c 3種組合進行查找,但不支持 b,c進行查找 .當最左側字段是常量引用時,索引就十分有效。
二、利用索引中的附加列,您能夠縮小搜索的範圍,但使用一個具備兩列的索引不一樣於使用兩個單獨的索引。
複合索引的結構與電話簿相似,人名由姓和名構成,電話簿首先按姓氏對進行排序,而後按名字對有相同姓氏的人進行排序。
若是您知道姓,電話簿將很是有用;若是您知道姓和名,電話簿則更爲有用,但若是您只知道名不知道姓,電話簿將沒有用處。
什麼狀況下應不建或少建索引?
一、表記錄太少
二、常常插入、刪除、修改的表
三、數據重複且分佈平均的表字段,假如一個表有10萬行記錄,有一個字段A只有T和F兩種值,且每一個值的分佈機率大約爲50%,那麼對這種表A字段建索引通常不會提升數據庫的查詢速度。
四、常常和主字段一塊查詢但主字段索引值比較多的表字段
3、關於MVVC
MySQL InnoDB存儲引擎,實現的是基於多版本的併發控制協議——MVCC (Multi-Version Concurrency Control)
注:與MVCC相對的,是基於鎖的併發控制,Lock-Based Concurrency Control
MVCC最大的好處:讀不加鎖,讀寫不衝突。在讀多寫少的OLTP應用中,讀寫不衝突是很是重要的,極大的增長了系統的併發性能,現階段幾乎全部的RDBMS,都支持了MVCC。
LBCC:Lock-Based Concurrency Control,基於鎖的併發控制
MVCC:Multi-Version Concurrency Control 基於多版本的併發控制協議。純粹基於鎖的併發機制併發量低,MVCC是在基於鎖的併發控制上的改進,主要是在讀操做上提升了併發量。
在MVCC併發控制中,讀操做能夠分紅兩類:
快照讀 (snapshot read):讀取的是記錄的可見版本 (有多是歷史版本),不用加鎖(共享讀鎖s鎖也不加,因此不會阻塞其餘事務的寫)
當前讀 (current read):讀取的是記錄的最新版本,而且,當前讀返回的記錄,都會加上鎖,保證其餘事務不會再併發修改這條記錄
4、行級鎖定的優缺點
優勢:
一、當在許多線程中訪問不一樣的行時只存在少許鎖定衝突。
二、回滾時只有少許的更改
三、能夠長時間鎖定單一的行。
缺點:
比頁級或表級鎖定佔用更多的內存。
當在表的大部分中使用時,比頁級或表級鎖定速度慢,由於你必須獲取更多的鎖。
若是你在大部分數據上常常進行GROUP BY操做或者必須常常掃描整個表,比其它鎖定明顯慢不少。
用高級別鎖定,經過支持不一樣的類型鎖定,你也能夠很容易地調節應用程序,由於其鎖成本小於行級鎖定。
5、MySQL優化
開啓查詢緩存,優化查詢
explain你的select查詢,這能夠幫你分析你的查詢語句或是表結構的性能瓶頸。EXPLAIN 的查詢結果還會告訴你你的索引主鍵被如何利用的,你的數據表是如何被搜索和排序的
當只要一行數據時使用limit 1,MySQL數據庫引擎會在找到一條數據後中止搜索,而不是繼續日後查少下一條符合記錄的數據
爲搜索字段建索引
使用 ENUM 而不是 VARCHAR。若是你有一個字段,好比「性別」,「國家」,「民族」,「狀態」或「部門」,你知道這些字段的取值是有限並且固定的,那麼,你應該使用 ENUM 而不是VARCHAR
Prepared StatementsPrepared Statements很像存儲過程,是一種運行在後臺的SQL語句集合,咱們能夠從使用 prepared statements 得到不少好處,不管是性能問題仍是安全問題。 Prepared Statements 能夠檢查一些你綁定好的變量,這樣能夠保護你的程序不會受到「SQL注入式」攻擊
垂直分表
選擇正確的存儲引擎
6、key和index的區別
key 是數據庫的物理結構,它包含兩層意義和做用,一是約束(偏重於約束和規範數據庫的結構完整性),二是索引(輔助查詢用的)。包括primary key, unique key, foreign key 等
index是數據庫的物理結構,它只是輔助查詢的,它建立時會在另外的表空間(mysql中的innodb表空間)以一個相似目錄的結構存儲。索引要分類的話,分爲前綴索引、全文本索引等;
算法
1、B+樹索引和哈希索引的區別?
B+樹是一個平衡的多叉樹,從根節點到每一個葉子節點的高度差值不超過1,並且同層級的節點間有指針相互連接,是有序的,以下圖:
哈希索引就是採用必定的哈希算法,把鍵值換算成新的哈希值,檢索時不須要相似B+樹那樣從根節點到葉子節點逐級查找,只需一次哈希算法便可,是無序的
以下圖所示:
2、哈希索引的優點:
等值查詢,哈希索引具備絕對優點(前提是:沒有大量重複鍵值,若是大量重複鍵值時,哈希索引的效率很低,由於存在所謂的哈希碰撞問題。)
哈希索引不適用的場景:
不支持範圍查詢
不支持索引完成排序
不支持聯合索引的最左前綴匹配規則
一般,B+樹索引結構適用於絕大多數場景,像下面這種場景用哈希索引才更有優點:
在HEAP表中,若是存儲的數據重複度很低(也就是說基數很大),對該列數據以等值查詢爲主,沒有範圍查詢、沒有排序的時候,特別適合採用哈希索引,例如這種SQL:
select id, name from table where name='李明';
而經常使用的 InnoDB 引擎中默認使用的是B+樹索引,它會實時監控表上索引的使用狀況。
若是認爲創建哈希索引能夠提升查詢效率,則自動在內存中的「自適應哈希索引緩衝區」創建哈希索引(在InnoDB中默認開啓自適應哈希索引)。
經過觀察搜索模式,MySQL會利用index key的前綴創建哈希索引,若是一個表幾乎大部分都在緩衝池中,那麼創建一個哈希索引可以加快等值查詢。
注意:在某些工做負載下,經過哈希索引查找帶來的性能提高遠大於額外的監控索引搜索狀況和保持這個哈希表結構所帶來的開銷。
但某些時候,在負載高的狀況下,自適應哈希索引中添加的read/write鎖也會帶來競爭,好比高併發的join操做。like操做和%的通配符操做也不適用於自適應哈希索引,可能要關閉自適應哈希索引。
3、B 樹和 B+ 樹的區別?
一、B樹,每一個節點都存儲key和data,全部節點組成這棵樹,而且葉子節點指針爲nul,葉子結點不包含任何關鍵字信息。
二、B+樹,全部的葉子結點中包含了所有關鍵字的信息,及指向含有這些關鍵字記錄的指針,且葉子結點自己依關鍵字的大小自小而大的順序連接
全部的非終端結點能夠當作是索引部分,結點中僅含有其子樹根結點中最大(或最小)關鍵字。(而B 樹的非終節點也包含須要查找的有效信息)
4、爲何說B+比B樹更適合實際應用中操做系統的文件索引和數據庫索引? 一、B+的磁盤讀寫代價更低。 B+的內部結點並無指向關鍵字具體信息的指針,所以其內部結點相對B樹更小。 若是把全部同一內部結點的關鍵字存放在同一盤塊中,那麼盤塊所能容納的關鍵字數量也越多。一次性讀入內存中的須要查找的關鍵字也就越多。相對來講IO讀寫次數也就下降了。 二、B+-tree的查詢效率更加穩定。 因爲非終結點並非最終指向文件內容的結點,而只是葉子結點中關鍵字的索引。因此任何關鍵字的查找必須走一條從根結點到葉子結點的路。全部關鍵字查詢的路徑長度相同,致使每個數據的查詢效率至關。 5、四種隔離級別 Serializable (串行化):可避免髒讀、不可重複讀、幻讀的發生。 Repeatable read (可重複讀):可避免髒讀、不可重複讀的發生。 Read committed (讀已提交):可避免髒讀的發生。 Read uncommitted (讀未提交):最低級別,任何狀況都沒法保證。 6、Mysql 中 MyISAM 和 InnoDB 的區別有哪些? 區別: InnoDB支持事務,MyISAM不支持 對於InnoDB每一條SQL語言都默認封裝成事務,自動提交,這樣會影響速度,因此最好把多條SQL語言放在begin和commit之間,組成一個事務; InnoDB支持外鍵,而MyISAM不支持。對一個包含外鍵的InnoDB錶轉爲MYISAM會失敗; InnoDB是彙集索引,數據文件是和索引綁在一塊兒的,必需要有主鍵,經過主鍵索引效率很高。 可是輔助索引須要兩次查詢,先查詢到主鍵,而後再經過主鍵查詢到數據。所以主鍵不該該過大,由於主鍵太大,其餘索引也都會很大。 而MyISAM是非彙集索引,數據文件是分離的,索引保存的是數據文件的指針。主鍵索引和輔助索引是獨立的。 InnoDB不保存表的具體行數,執行select count(*) from table時須要全表掃描。而MyISAM用一個變量保存了整個表的行數,執行上述語句時只須要讀出該變量便可,速度很快; Innodb不支持全文索引,而MyISAM支持全文索引,查詢效率上MyISAM要高; 如何選擇: 是否要支持事務,若是要請選擇innodb,若是不須要能夠考慮MyISAM; 若是表中絕大多數都只是讀查詢,能夠考慮MyISAM,若是既有讀寫也挺頻繁,請使用InnoDB 系統奔潰後,MyISAM恢復起來更困難,可否接受; MySQL5.5版本開始Innodb已經成爲Mysql的默認引擎(以前是MyISAM),說明其優點是有目共睹的,若是你不知道用什麼,那就用InnoDB,至少不會差。 分區 1、什麼是表分區? 表分區,是指根據必定規則,將數據庫中的一張表分解成多個更小的,容易管理的部分。從邏輯上看,只有一張表,可是底層倒是由多個物理分區組成。 2、表分區與分表的區別? 分表:指的是經過必定規則,將一張表分解成多張不一樣的表。好比將用戶訂單記錄根據時間成多個表。 分表與分區的區別在於:分區從邏輯上來說只有一張表,而分表則是將一張表分解成多張表。 3、表分區有什麼好處? 一、存儲更多數據。分區表的數據能夠分佈在不一樣的物理設備上,從而高效地利用多個硬件設備。和單個磁盤或者文件系統相比,能夠存儲更多數據 二、優化查詢。在where語句中包含分區條件時,能夠只掃描一個或多個分區表來提升查詢效率;涉及sum和count語句時,也能夠在多個分區上並行處理,最後彙總結果。 三、分區表更容易維護。例如:想批量刪除大量數據能夠清除整個分區。 四、避免某些特殊的瓶頸,例如InnoDB的單個索引的互斥訪問,ext3問價你係統的inode鎖競爭等。 4、分區表的限制因素 一個表最多隻能有1024個分區 MySQL5.1中,分區表達式必須是整數,或者返回整數的表達式。在MySQL5.5中提供了非整數表達式分區的支持。 若是分區字段中有主鍵或者惟一索引的列,那麼多有主鍵列和惟一索引列都必須包含進來。即:分區字段要麼不包含主鍵或者索引列,要麼包含所有主鍵和索引列。 分區表中沒法使用外鍵約束 MySQL的分區適用於一個表的全部數據和索引,不能只對表數據分區而不對索引分區,也不能只對索引分區而不對錶分區,也不能只對表的一部分數據分區。 5、如何判斷當前MySQL是否支持分區? 命令:show variables like '%partition%' 運行結果: mysql> show variables like '%partition%'; +-------------------+-------+| Variable_name | Value |+-------------------+-------+| have_partitioning | YES |+-------------------+-------+1 row in set (0.00 sec) have_partintioning 的值爲YES,表示支持分區。 6、MySQL支持的分區類型有哪些? RANGE分區:這種模式容許將數據劃分不一樣範圍。例如能夠將一個表經過年份劃分紅若干個分區 LIST分區:這種模式容許系統經過預約義的列表的值來對數據進行分割。按照List中的值分區,與RANGE的區別是,range分區的區間範圍值是連續的。 HASH分區 :這中模式容許經過對錶的一個或多個列的Hash Key進行計算,最後經過這個Hash碼不一樣數值對應的數據區域進行分區。例如能夠創建一個對錶主鍵進行分區的表。 KEY分區 :上面Hash模式的一種延伸,這裏的Hash Key是MySQL系統產生的。