查詢優化器 :MySQL會對咱們的SQL語句進行優化;html
存儲引擎mysql
查詢數據庫支持的存儲引擎 show engines;
SQL優化sql
索引數據庫
B-Tree (平衡多路查找樹):能夠顯著減小定位記錄時所經歷的中間過程,從而加快存取速度服務器
一個M階的B-Tree具備的特性 > 每一個節點最多有m個孩子 > 根節點的孩子數>=2(前提:樹的高度大於一) > 除了根節點和葉子結點,其餘節點的孩子數(ceil(m/2));向上取整 > 全部的葉子結點都在一層 > 各個結點包含n個關鍵字信息:(P0,K1,P1,K2,P2......Kn,Pn) 其中 - Ki(i=1,2......n)爲關鍵字,且K(i-1)<Ki,即從小到大排序 - 關鍵字的個數n必須知足:[ceil(m/2)-1,m-1] - Pi指向子樹,且指針P(i-1)所指向的子樹結點中全部關鍵字均小於Ki。即:父結點中任何關鍵字的左孩子都小於它,右孩子大於它
B-Tree示例圖:數據結構
B+Tree :B+Tree是在B-Tree基礎上的一種優化,使其更適合實現外存儲索引結構,InnoDB存儲引擎就是用B+Tree實現其索引結構架構
B+Tree相對於B-Tree有幾點不一樣: 非葉子節點只存儲鍵值信息; 全部葉子節點之間都有一個鏈指針; 數據記錄都存放在葉子節點中;
B+Tree示例圖:併發
單列;一個表能夠有多個單值索引分佈式
建立索引高併發
-- 給users表的name屬性建立索引 create index name_index on users(name) ; alter table users add index name_index(name); -- 查詢索引 show index from users(表名); -- 刪除索引 drop index name_index(索引名) on users(表名);
單列;一個表只有一個惟一索引;主鍵默認惟一索引
-- 給users表的id屬性建立索引 create unique index id_index on users(id) ; alter table users add unique index id_index(id);
多列;
-- 給users表的birthday,name屬性建立索引 -- 先找birthday,若是birthday相同則再去name找 create index birthday_name_index on users(birthday,name) ; alter table users add index birthday_name_index(birthday,name);
主鍵索引不能爲null,單值索引能夠爲null;
聚簇索引:主索引葉子結點指向數據,輔助索引葉子結點指向對應數據的主索引;
非聚簇索引:索引表和數據表分開,主索引和輔助索引的葉子結點都指向數據;
優點:以快速檢索,減小I/O次數,加快檢索速度;根據索引分組和排序,能夠加快分組和排序;
劣勢:索引自己也是表,所以會佔用存儲空間。索引的維護和建立須要時間成本,這個成本隨着數據量增大而增大;構建索引會下降數據表的修改操做(刪除,添加,修改)的效率,由於在修改數據表的同時還須要修改索引表。
使用關鍵字explain ,分析分析SQL執行計劃
mysql> explain select * from user; +----+-------------+-------+------+---------------+------+---------+------+------+-------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+------+---------------+------+---------+------+------+-------+ | 1 | SIMPLE | user | ALL | NULL | NULL | NULL | NULL | 3 | | +----+-------------+-------+------+---------------+------+---------+------+------+-------+
-- 建表準備數據 CREATE TABLE course( cid INT(2), cname VARCHAR(10), tid INT(2) ); CREATE TABLE teacher( tid INT(2), tname VARCHAR(10), tcid INT(2) ); CREATE TABLE teacher_card( tcid INT(2)teacher_card, tcdesc VARCHAR(10) );
-- 查詢課程編號爲2 或 教師證爲3的老師 mysql> explain SELECT * -> FROM teacher t,course c, teacher_card tc -> WHERE t.tid=c.tid AND t.tcid= tc.tcid AND (c.cid=2 OR tc.tcid=3); +----+-------------+-------+------ | id | select_type | table | ... | 1 | SIMPLE | t | ... | 1 | SIMPLE | tc | ... | 1 | SIMPLE | c | ... -- 在鏈接查詢中,id值相同 -- t - tc - c 已知記錄數順序 3- 3- 4;==>執行順序:記錄數少的先執行; -- 1- 查詢產生的笛卡爾積 -- > 3 * 3 = 9 ;9 * 4 = 36 -- 2- 若是記錄數多的先執行 -- > 4 * 3 = 12 ; 12 * 3= 36 -- 雖然最後的笛卡爾積數量不變,可是中間產生笛卡爾積數量不同,第一種中間記錄數少,佔用的資源少; -- 查詢教英語的老師描述 mysql> EXPLAIN SELECT tc.tcdesc -> FROM teacher_card tc -> WHERE tc.tcid = (SELECT t.tcid FROM teacher t -> WHERE t.tid = (SELECT c.tid FROM course c WHERE c.cname = '英語')); +----+-------------+-------+------+ | id | select_type | table | ... | 1 | PRIMARY | tc | ... | 2 | SUBQUERY | t | ... | 3 | SUBQUERY | c | ... -- 在子查詢中,id有 3-2-1 執行順序 c-t-tc -- 越內層的子查詢,越先執行
id : id值相同 ,從上往下順序執行 ;id值不一樣,從大到小順序執行;
select_type
type
SYSTEM>CONST> EQ_REF> REF>RANGE>IANDEX> ALL(要對type優化,前提是有索引)
SYSTEM:只有一條數據的系統表、主查詢的衍生表只有一條數據;(查詢結果只有一條)
-- 例如,這條SQL的主查詢知足條件爲system類型 mysql>ALTER TABLE teacher ADD INDEX tid_index(tid); mysql> EXPLAIN SELECT * -> FROM (SELECT * FROM teacher t WHERE t.tid = 1) t_derived; +----+-------------+------------+--------+---------------+-----------+---------+------+-- | id | select_type | table | type | possible_keys | key | key_len | ref | +----+-------------+------------+--------+---------------+-----------+---------+------+--- | 1 | PRIMARY | <derived2> | system | NULL | NULL | NULL | NULL | | 2 | DERIVED | t | ref | tid_index | tid_index | 5 | | +----+-------------+------------+--------+---------------+-----------+---------+------+--
CONST:在主鍵索引或惟一索引的狀況下,只查詢一條數據;(查詢結果只有一條)
mysql> ALTER TABLE teacher ADD PRIMARY KEY tid_primary(tid); -- 主鍵索引是tid,type是const mysql> EXPLAIN SELECT * FROM teacher t WHERE t.tid = 1; +----+-------------+-------+-------+-------------------+---------+---------+-------+------ | id | select_type | table | type | possible_keys | key | key_len | ref | rows +----+-------------+-------+-------+-------------------+---------+---------+-------+------ | 1 | SIMPLE | t | const | PRIMARY,tid_index | PRIMARY | 4 | const | 1 +----+-------------+-------+-------+-------------------+---------+---------+-------+----- -- 查詢tcid ,type 是 all mysql> EXPLAIN SELECT * FROM teacher t WHERE t.tcid = 1; +----+-------------+-------+------+---------------+------+---------+------+------+------- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra +----+-------------+-------+------+---------------+------+---------+------+------+------- | 1 | SIMPLE | t | ALL | NULL | NULL | NULL | NULL | 3 | Using +----+-------------+-------+------+---------------+------+---------+------+------+-------
EQ_REF:惟一性索引; 對於每一個索引鍵的查詢,必須返回有且只有一條數據; (查詢結果多條)
-- 將列改成惟一性索引 mysql> ALTER TABLE teacher ADD UNIQUE INDEX tcid_index(tcid); mysql> ALTER TABLE teacher_card ADD PRIMARY KEY tcid_primary(tcid); -- 此時teacher和teacher_card的數據條數爲3 -- 查詢索引鍵,若兩邊數據條數一致(必須是惟一性索引) mysql> EXPLAIN SELECT t.tcid FROM teacher t,teacher_card tc WHERE t.tcid = tc.tcid; +----+-------------+-------+--------+---------------+------------+---------+------------- | id | select_type | table | type | possible_keys | key | key_len | ref +----+-------------+-------+--------+---------------+------------+---------+------------- | 1 | SIMPLE | t | index | tcid_index | tcid_index | 5 | NULL | 1 | SIMPLE | tc | eq_ref | PRIMARY | PRIMARY | 4 | mydb3.t.tcid +----+-------------+-------+--------+---------------+------------+---------+-------------
REF:非惟一性索引; 對於每一個索引鍵的查詢,返回n條數據(能夠n=0);(查詢結果多條)
-- 給teacher添加一條數據 -- 查詢索引鍵,若兩邊數據條數不一致(能夠爲非惟一性索引) mysql> EXPLAIN SELECT t.tcid FROM teacher t,teacher_card tc WHERE t.tcid = tc.tcid; +----+-------------+-------+--------+---------------+------------+---------+------------- | id | select_type | table | type | possible_keys | key | key_len | ref +----+-------------+-------+--------+---------------+------------+---------+------------- | 1 | SIMPLE | t | index | tcid_index | tcid_index | 5 | NULL | 1 | SIMPLE | tc | ref | PRIMARY | PRIMARY | 4 | mydb3.t.tcid +----+-------------+-------+--------+---------------+------------+---------+-------------
RANGE:檢索指定範圍的行的索引鍵 (關鍵字in有時候會失效);(查詢結果多條)
mysql> EXPLAIN SELECT t.tcid FROM teacher t WHERE t.tcid > 1; +----+-------------+-------+-------+---------------+------------+---------+------+------+- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | +----+-------------+-------+-------+---------------+------------+---------+------+------+- | 1 | SIMPLE | t | range | tcid_index | tcid_index | 5 | NULL | 3 | +----+-------------+-------+-------+---------------+------------+---------+------+------+-
INDEX:查詢數據的索引鍵;
ALL:查詢全部數據;
possible_keys:預測用到的索引(實際可能發生索引失效)
key:實際用到的索引
key_len :索引的計算長度
# char和varchar類型key_len計算公式 varchr(N)變長字段且容許NULL =\ N * ( character set:utf8=3,gbk=2,latin1=1)+1(NULL)+2(變長字段) varchr(N)變長字段且不容許NULL = \ N * ( character set:utf8=3,gbk=2,latin1=1)+2(變長字段) char(N)固定字段且容許NULL = N * ( character set:utf8=3,gbk=2,latin1=1)+1(NULL) char(N)固定字段且容許NULL = N * ( character set:utf8=3,gbk=2,latin1=1) # 與數據類型長度無關 # 數值數據的key_len計算公式: TINYINT容許NULL = 1 + 1(NULL) TINYINT不容許NULL = 1 SMALLINT容許爲NULL = 2+1(NULL) SMALLINT不容許爲NULL = 2 INT容許爲NULL = 4+1(NULL) INT不容許爲NULL = 4 # 日期時間型的key_len計算:(針對mysql5.5) DATETIME容許爲NULL = 8 + 1(NULL) DATETIME不容許爲NULL = 8 TIMESTAMP容許爲NULL = 4 + 1(NULL) TIMESTAMP不容許爲NULL = 4
ref:指明當前表參照的字段(null,const常量,參照的字段)
-- tc 表參照了mydb3.t.tcid 字段 mysql> EXPLAIN SELECT t.tcid FROM teacher t,teacher_card tc WHERE t.tcid = tc.tcid; +----+-------------+-------+--------+---------------+------------+---------+------------- | id | select_type | table | type | possible_keys | key | key_len | ref +----+-------------+-------+--------+---------------+------------+---------+------------- | 1 | SIMPLE | t | index | tcid_index | tcid_index | 5 | NULL | 1 | SIMPLE | tc | ref | PRIMARY | PRIMARY | 4 | mydb3.t.tcid +----+-------------+-------+--------+---------------+------------+---------+-------------
rows:實際查詢的記錄數
mysql> EXPLAIN SELECT t.tcid FROM teacher_card tc,teacher t WHERE t.tcid = tc.tcid; +----+-------------+-------+--------+---------------+------------+---------+------------- | id | select_type | table | type | possible_keys | key | key_len | ref +----+-------------+-------+--------+---------------+------------+---------+------------- | 1 | SIMPLE | t | index | tcid_index | tcid_index | 5 | NULL | 1 | SIMPLE | tc | ref | PRIMARY | PRIMARY | 4 | mydb3.t.tcid +----+-------------+-------+--------+---------------+------------+---------+------------- +------+--------------------------+ | rows | Extra | +------+--------------------------+ | 3 | Using index | | 1 | Using where; Using index | +------+--------------------------+ mysql> SELECT t.tcid FROM teacher_card tc,teacher t WHERE t.tcid = tc.tcid; +------+ | tcid | +------+ | 1 | | 2 | | 3 | +------+
extra
Using filesort:沒法利用索引來完成的排序
-- 單列索引 CREATE TABLE a( a INT, b INT, c INT, INDEX a_index(a), INDEX b_index(b), INDEX c_index(c) ); -- 查詢的 a 可是額外用b排了序,若是用a排序則不會出現Using filesort mysql> EXPLAIN SELECT * FROM a WHERE a='' ORDER BY b; +------+--------------------------+ | rows | Extra | +------+--------------------------+ | 1 | Using where; Using filesort | +------+--------------------------+ -- 複合索引 不能跨列 要知足最佳左前綴,不然出現Using filesort DROP INDEX a_index ON a; DROP INDEX b_index ON a; DROP INDEX c_index ON a; ALTER TABLE a ADD INDEX a_b_c_index(a,b,c); -- 知足最佳左前綴,沒有跨列 mysql> EXPLAIN SELECT * FROM a WHERE a='' ORDER BY b; +------+--------------------------+ | rows | Extra | +------+--------------------------+ | 1 | Using where; Using index | +------+--------------------------+ -- 跨列 mysql> EXPLAIN SELECT * FROM a WHERE a='' ORDER BY c; +------+--------------------------+ | rows | Extra | +------+--------------------------+ | 1 | Using where; Using index; Using filesort | +------+--------------------------+ -- 不知足最佳左前綴 mysql>EXPLAIN SELECT * FROM a WHERE b='' ORDER BY c; +------+--------------------------+ | rows | Extra | +------+--------------------------+ | 1 | Using where; Using index; Using filesort | +------+--------------------------+
(補充: 回表查詢:指的是在索引中沒有要查詢的數據,須要回到表中查詢)
-- 單表優化 -- 建立表 CREATE TABLE book( bid INT NOT NULL PRIMARY KEY, NAME VARCHAR(20), authorid INT NOT NULL, publicid INT NOT NULL, typeid INT NOT NULL ); -- 查詢authorID=1,typeid=2或3的 bid mysql> EXPLAIN SELECT bid FROM book WHERE typeid IN (2,3) AND authorid = 1 ORDER BY typeid DESC; +----+-------------+-------+------+---------------+------+---------+------+------+----------- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | | +----+-------------+-------+------+---------------+------+---------+------+------+----------- | 1 | SIMPLE | book | ALL | NULL | NULL | NULL | NULL | 5 | +----+-------------+-------+------+---------------+------+---------+------+------+-----------------------------+ Extra ------------------+ Using where; Using filesort ------------------+ -- 優化 1 添加索引,(消除了Using filesort,提升了type等級index) ALTER TABLE book ADD INDEX b_t_a_index(bid,typeid,authorid); mysql> EXPLAIN SELECT bid FROM book WHERE typeid IN (2,3) AND authorid = 1 ORDER BY typeid DESC; +----+-------------+-------+------+---------------+------+---------+------+------+----------- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | | +----+-------------+-------+------+---------------+------+---------+------+------+----------- | 1 | SIMPLE | book | index | NULL | NULL | 12 | NULL | 5 | +----+-------------+-------+------+---------------+------+---------+------+------+----------- ------------------+ Extra ------------------+ Using where; Using index;Using filesort ------------------+ -- 優化2 -- 修改索引順序(提升了type等級 ref) -- 含IN 的查詢放到後面,避免可能的失效 ALTER TABLE book ADD INDEX atb_index(authorid,typeid,bid); mysql> EXPLAIN SELECT bid FROM book WHERE authorid = 1 AND typeid IN (2,3) ORDER BY typeid DESC; +----+-------------+-------+------+---------------+-----------+---------+-------+------+----- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | +----+-------------+-------+------+---------------+-----------+---------+-------+------+----- | 1 | SIMPLE | book | ref | atb_index | atb_index | 4 | const | 2 | +----+-------------+-------+------+---------------+-----------+---------+-------+------+--------------------------+ Extra | ---------------------+ Using where; Using index | ---------------------+
EXPLAIN SELECT * FROM teacher t , course c WHERE t.tid = c.tid AND c.cname = '語文'; -- 或者 EXPLAIN SELECT * FROM teacher t LEFT JOIN course c ON t.tid = c.tid WHERE c.cname = '語文'; +----+-------------+-------+------+---------------+------+---------+------+------+----------- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | +----+-------------+-------+------+---------------+------+---------+------+------+----------- | 1 | SIMPLE | t | ALL | NULL | NULL | NULL | NULL | 3 | | 1 | SIMPLE | c | ALL | NULL | NULL | NULL | NULL | 4 | +----+-------------+-------+------+---------------+------+---------+------+------+--------------------------------+ Extra --------------------+ Using where; Using join buffer | --------------------+ -- 優化 1 添加索引 -- 給哪張表添加索引 --> 知足原則"小表驅動大表"; -- --> 索引常用,不常改變
IN和EXIST的使用:
order by 的使用
Using filesort 分爲雙路排序[1掃描排序字段,2掃描其它字段]、單路排序[掃描全度字段](根據磁盤IO次數)
能夠經過切換排序方法和增大排序使用的buffer
大小
保證排序的一致性
避免使用SELECT *
記錄mysql中超過閥值(long query time)默認10秒;
-- 查看慢查詢日誌是否開啓 show variables like "%slow_query_log%"; -- 臨時開啓 set globle slow_query_log = 1; -- 永久開啓 -- 修改mysql的配置文件 -- slow_query_log = 1 -- slow_query_log_file = /var/mysql/slow.log 制定一個路徑尋訪日誌 -- 查看閥值大小 show variables like "%long_query_time%"; -- 臨時設置大小(重啓生效) set globle long_query_time = 時長(s); -- 永久設置大小(重啓生效) -- 修改mysql的配置文件 -- long_query_time = 時長 -- 查詢超過閥值的SQL show globle status like "%slow_queris%"; -- 數量 -- mysql的工具,查看慢日誌記錄 mysqldumpslow
分析執行SQL的在各環節的執行時間
-- 查看執行SQL的時間和query_id show profiles -- show variables like "%profiling%"; -- 開啓profiling屬性 set profiling = on; -- 分析執行SQL的在各環節的執行時間 show profile all for query 1; -- 1是查出來的query_id
能夠記錄全部執行的SQL
-- 查看是否開啓全局日誌 show variables like "%general_log%"; -- 開啓全局日誌,記錄到mysql.general_log表中 set globle general_log = 1; set globle log_output = 'table';
解決資源共享形成的併發問題 參考[mydddfly]博客;
開銷 | 加鎖速度 | 死鎖 | 粒度 | 併發性能 | |
---|---|---|---|---|---|
行級鎖 | 小 | 快 | 不會死鎖 | 大(鎖衝突機率高) | 低 |
表級鎖 | 大 | 慢 | 會出現死鎖 | 小(鎖衝突機率低) | 高 |
頁面鎖 | 中 | 中 | 會出現死鎖 | 中 | 中 |
MySQL的表級鎖有兩種模式:表共享讀鎖(Table Read Lock)和表獨佔寫鎖(Table Write Lock)
MyISAM在執行查詢語句(SELECT)前,會自動給涉及的全部表加讀鎖,在執行更新操做(UPDATE、DELETE、INSERT等)前,會自動給涉及的表加寫鎖;
-- 經過檢查table_locks_waited和table_locks_immediate狀態變量來分析系統上的表鎖定爭奪狀況; -- 值越大爭奪越嚴重 mysql> show status like 'table%'; +-----------------------+-------+ | Variable_name | Value | +-----------------------+-------+ | Table_locks_immediate | 46 | | Table_locks_waited | 0 | +-----------------------+-------+
如何給表加鎖
-- lock table 表名 read/write 給表加上讀/寫鎖 mysql> lock table teacher read; -- 查看錶是否使用鎖 mysql> show open tables; +--------------------+----------------------------------------------+--------+-------------+ | Database | Table | In_use | Name_locked | +--------------------+----------------------------------------------+--------+-------------+ | mysql | time_zone_transition_type | 0 | 0 | | mydb3 | teacher | 1 | 0 | +--------------------+----------------------------------------------+--------+-------------+
(爲了容許行鎖和表鎖共存,實現多粒度鎖機制,InnoDB還有兩種內部使用的意向鎖(Intention Locks),這兩種意向鎖都是表鎖)
-- 能夠經過檢查InnoDB_row_lock狀態變量來分析系統上的行鎖的爭奪狀況: show status like 'innodb_row_lock%'; Show innodb status; -- 共享鎖 select * from teacher where tid = 1 lock in share mode; -- 排它鎖 select * from teacher where tid = 1 for update;
MySQL之間數據複製的基礎是二進制日誌文件(binary log file)。一臺MySQL數據庫一旦啓用二進制日誌後,其做爲master,它的數據庫中全部操做都會以「事件」的方式記錄在二進制日誌中,其餘數據庫做爲slave經過一個I/O線程與主服務器保持通訊,並監控master的二進制日誌文件的變化,若是發現master二進制日誌文件發生變化,則會把變化複製到本身的中繼日誌中,而後slave的一個SQL線程會把相關的「事件」執行到本身的數據庫中,以此實現從數據庫和主數據庫的一致性,也就實現了主從複製;
主服務器配置
-- 修改配置文件 [mysqld] log-bin = mysql-bin server-id = 1 -- 建立用戶並受權 mysql> CREATE USER 'flynn'@'localhost' IDENTIFIED BY '123456';#建立用戶 mysql> GRANT REPLICATION SLAVE ON *.* TO 'flynn'@'localhost';#分配權限 mysql>flush privileges; #刷新權限 -- 查看master狀態,記錄二進制文件名和位置: show master status;
從服務器配置、
-- 配置文件 server-id = 2 -- 重啓mysql,打開mysql會話,執行同步SQL語句 mysql> CHANGE MASTER TO -> MASTER_HOST='', -> MASTER_USER='', -> MASTER_PASSWORD='', -> MASTER_LOG_FILE='二進制文件名', -> MASTER_LOG_POS='位置'; -- 啓動slave 同步 mysql> start slave; -- 查看狀態 mysql> show slave status
對主服務器配置約束
[mysqld] # 不一樣步哪些數據庫 binlog-ignore-db = '' # 只同步哪些數據庫,除此以外,其餘不一樣步 binlog-do-db = ''
自增的主鍵是連續的在插入過程當中儘可能減小頁分裂,即便進行頁分裂,也只會分裂不多的一部分。而且能減小數據的移動,每次插入都是插入到最後。總之就是減小分裂和移動的頻率;
InNoDB使用的是聚簇索引,使用輔助索引要查詢兩次才能拿到數據;而主鍵索引只須要查一次;
B-Tree的非葉子結點也會存儲數據,這樣致使了非葉子結點上存儲的指針變少,數據量大時只能增長樹的高度(即增長了IO訪問的次數);