MySQL | InnoDB | MyISAM |
---|---|---|
事物支持 | 是 | 否 |
外鍵支持 | 是 | 否 |
鎖 | 行級 | 表級 |
全文索引 | 否 | 是【只支持英文】 |
崩潰安全恢復支持 | 是 | 否 |
選擇:MyISAM相對簡單,因此在效率上要優於InnoDB。若是系統插入和查詢操做多,不須要事務和外鍵,選擇MyISAM,若是須要頻繁的更新、刪除操做,或者須要事務、外鍵、行級鎖的時候,選擇InnoDB。java
小結:不可重複讀的和幻讀很容易混淆,不可重複讀側重於修改,幻讀側重於新增或刪除。解決不可重複讀的問題只需鎖住知足條件的行,解決幻讀須要鎖表。mysql
事務隔離級別 | 讀數據一致性 | 髒讀 | 不可重複讀 | 幻讀 |
---|---|---|---|---|
讀未提交(read-uncommitted) | 最低級別 | 是 | 是 | 是 |
讀已提交(read-committed) | 語句級 | 否 | 是 | 是 |
可重複讀(repeatable-read) | 事務級 | 否 | 否 | 是 |
串行化(serializable) | 最高級別,事務級 | 否 | 否 | 否 |
①. 優點:ios
②. 劣勢:程序員
③. 加鎖方式:面試
自動加鎖。sql
對於UPDATE、DELETE和INSERT語句,InnoDB會自動給涉及的數據集加排他鎖;對於普通SELECT語句,InnoDB不會加任何鎖;固然咱們也能夠顯示的加鎖: 加共享鎖:select from tableName where ... lock in share mode 加排他鎖:select from tableName where ... for update數據庫
④. 間隙鎖【Next-Key鎖】:編程
當咱們用範圍條件檢索數據,並請求共享或排他鎖時,InnoDB會給符合條件的已有數據記錄的索引項加鎖;對於鍵值在條件範圍內但並不存在的記錄,叫作"間隙(GAP)"。InnoDB也會對這個"間隙"加鎖,這種鎖機制就是所謂的間隙鎖(Next-Key鎖)。數組
危害(坑):若執行的條件範圍過大,則InnoDB會將整個範圍內全部的索引鍵值所有鎖定,很容易對性能形成影響。緩存
Transaction-A mysql> update innodb_lock set k=66 where id >=6; Query OK, 1 row affected (0.63 sec) mysql> commit; Transaction-B mysql> insert into innodb_lock (id,k,v) values(7,'7','7000'); Query OK, 1 row affected (18.99 sec)
⑤. 排他鎖:
排他鎖,也稱寫鎖,獨佔鎖,當前寫操做沒有完成前,它會阻斷其餘寫鎖和讀鎖。
# Transaction_A mysql> set autocommit=0; mysql> select * from innodb_lock where id=4 for update; +----+------+------+ | id | k | v | +----+------+------+ | 4 | 4 | 4000 | +----+------+------+ 1 row in set (0.00 sec) mysql> update innodb_lock set v='4001' where id=4; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> commit; Query OK, 0 rows affected (0.04 sec) # Transaction_B mysql> select * from innodb_lock where id=4 for update; +----+------+------+ | id | k | v | +----+------+------+ | 4 | 4 | 4001 | +----+------+------+ 1 row in set (9.53 sec)
⑥. 共享鎖:
共享鎖,也稱讀鎖,多用於判斷數據是否存在,多個讀操做能夠同時進行而不會互相影響。若是事務對讀鎖進行修改操做,極可能會形成死鎖。以下圖所示。
# Transaction_A mysql> set autocommit=0; mysql> select * from innodb_lock where id=4 lock in share mode; +----+------+------+ | id | k | v | +----+------+------+ | 4 | 4 | 4001 | +----+------+------+ 1 row in set (0.00 sec) mysql> update innodb_lock set v='4002' where id=4; Query OK, 1 row affected (31.29 sec) Rows matched: 1 Changed: 1 Warnings: 0 # Transaction_B mysql> set autocommit=0; mysql> select * from innodb_lock where id=4 lock in share mode; +----+------+------+ | id | k | v | +----+------+------+ | 4 | 4 | 4001 | +----+------+------+ 1 row in set (0.00 sec) mysql> update innodb_lock set v='4002' where id=4; ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
⑦. 分析行鎖定:
經過檢查InnoDB_row_lock 狀態變量分析系統上的行鎖的爭奪狀況,命令:
show status like 'innodb_row_lock%'
mysql> show status like 'innodb_row_lock%'; +-------------------------------+-------+ | Variable_name | Value | +-------------------------------+-------+ | Innodb_row_lock_current_waits | 0 | | Innodb_row_lock_time | 0 | | Innodb_row_lock_time_avg | 0 | | Innodb_row_lock_time_max | 0 | | Innodb_row_lock_waits | 0 | +-------------------------------+-------+
innodb_row_lock_current_waits: 當前正在等待鎖定的數量 innodb_row_lock_time: 從系統啓動到如今鎖定總時間長度;【很是重要的參數】 innodb_row_lock_time_avg: 每次等待所花平均時間;【很是重要的參數】 innodb_row_lock_time_max: 從系統啓動到如今等待最長的一次所花的時間; innodb_row_lock_waits: 系統啓動後到如今總共等待的次數;很是重要的參數,直接決定優化的方向和策略。
⑧. 行鎖優化:
①. 優點:
②. 劣勢:
③. 加鎖方式:
自動加鎖。
查詢操做(SELECT),會自動給涉及的全部表加讀鎖,更新操做(UPDATE、DELETE、INSERT),會自動給涉及的表加寫鎖。也能夠顯示加鎖:
共享讀鎖:lock table tableName read; 獨佔寫鎖:lock table tableName write; 批量解鎖:unlock tables;
④. 共享讀鎖:
對MyISAM表的讀操做(加讀鎖),不會阻塞其餘進程對同一表的讀操做,但會阻塞對同一表的寫操做。只有當讀鎖釋放後,才能執行其餘進程的寫操做。
Transaction-A mysql> lock table myisam_lock read; Query OK, 0 rows affected (0.00 sec) mysql> select * from myisam_lock; 9 rows in set (0.00 sec) mysql> select * from innodb_lock; ERROR 1100 (HY000): Table 'innodb_lock' was not locked with LOCK TABLES mysql> update myisam_lock set v='1001' where k='1'; ERROR 1099 (HY000): Table 'myisam_lock' was locked with a READ lock and can't be updated mysql> unlock tables; Query OK, 0 rows affected (0.00 sec) Transaction-B mysql> select * from myisam_lock; 9 rows in set (0.00 sec) mysql> select * from innodb_lock; 8 rows in set (0.01 sec) mysql> update myisam_lock set v='1001' where k='1'; Query OK, 1 row affected (18.67 sec)
⑤. 獨佔寫鎖:
對MyISAM表的寫操做(加寫鎖),會阻塞其餘進程對同一表的讀和寫操做,只有當寫鎖釋放後,纔會執行其餘進程的讀寫操做。
Transaction-A mysql> set autocommit=0; Query OK, 0 rows affected (0.05 sec) mysql> lock table myisam_lock write; Query OK, 0 rows affected (0.03 sec) mysql> update myisam_lock set v='2001' where k='2'; Query OK, 1 row affected (0.00 sec) mysql> select * from myisam_lock; 9 rows in set (0.00 sec) mysql> update innodb_lock set v='1001' where k='1'; ERROR 1100 (HY000): Table 'innodb_lock' was not locked with LOCK TABLES mysql> unlock tables; Query OK, 0 rows affected (0.00 sec) Transaction-B mysql> select * from myisam_lock; 9 rows in set (42.83 sec)
小結:表鎖,讀鎖會阻塞寫,不會阻塞讀。而寫鎖則會把讀、寫都阻塞。
⑥. 查看加鎖狀況:
show open tables; 1表示加鎖,0表示未加鎖。
mysql> show open tables where in_use > 0; +----------+-------------+--------+-------------+ | Database | Table | In_use | Name_locked | +----------+-------------+--------+-------------+ | lock | myisam_lock | 1 | 0 | +----------+-------------+--------+-------------+
⑦. 分析表鎖定:
經過檢查table_locks_waited 和 table_locks_immediate 狀態變量分析系統上的表鎖定,命令:
show status like 'table_locks%';
mysql> show status like 'table_locks%'; +----------------------------+-------+ | Variable_name | Value | +----------------------------+-------+ | Table_locks_immediate | 104 | | Table_locks_waited | 0 | +----------------------------+-------+
此外,MyISAM的讀寫鎖調度是寫優先,這也是MyISAM不適合作寫爲主的存儲引擎。由於寫鎖後,其餘線程不能作任何操做,大量的更新會使查詢很可貴到鎖,從而形成永久阻塞。
InnoDB默認採用行鎖,在未使用索引字段查詢時升級爲表鎖。MySQL這樣設計並非給你挖坑。它有本身的設計目的。 即使你在條件中使用了索引字段,MySQL會根據自身的執行計劃,考慮是否使用索引(因此explain命令中會有possible_key 和 key)。若是MySQL認爲全表掃描效率更高,它就不會使用索引,這種狀況下InnoDB將使用表鎖,而不是行鎖。所以,在分析鎖衝突時,別忘了檢查SQL的執行計劃,以確認是否真正使用了索引。
咱們拿出一本新華字典,它的目錄實際上就是一種索引:非彙集索引。咱們能夠經過目錄迅速定位咱們要查的字。而字典的內容部分通常都是按照拼音排序的,這實際上又是一種索引:彙集索引。彙集索引這種實現方式使得按主鍵的搜索十分高效,可是輔助索引搜索須要檢索兩遍索引:首先檢索輔助索引得到主鍵,而後用主鍵到主索引中檢索得到記錄。
①、mysql主要使用B+樹來構建索引,爲何不用二叉樹和紅黑樹?
因磁盤I/O效率低下,mysql爲了儘可能減小磁盤IO的存取次數,須要利用了磁盤存取的局部性原理進行磁盤預讀。
在InnoDB裏,每一個頁默認16KB,假設索引的是8B的long型數據,每一個key後有個頁號4B,還有6B的其餘數據(參考《MySQL技術內幕:InnoDB存儲引擎》P193的頁面數據),那麼每一個頁的扇出係數爲16KB/(8B+4B+6B)≈1000,即每一個頁能夠索引1000個key。在高度h=3時,s=1000^3=10億!!也就是說,InnoDB經過三次索引頁的I/O,便可索引10億的key。一般來講,索引樹的高度在2~4。
②. B+Tree與B-Tree的區別
M階B-Tree
定義:
特性:
M階B+Tree
定義:
特性:
③. B+Tree與B-Tree的區別
④. 爲何mysql的索引使用B+樹而不是B樹呢?
MySQL自身常見的性能問題有磁盤空間不足,磁盤I/O太大,服務器硬件性能低。
使用explain關鍵字能夠模擬優化器執行sql查詢語句,從而得知MySQL 是如何處理sql語句。
①. id
select 查詢的序列號,包含一組能夠重複的數字,表示查詢中sql語句的執行順序。通常有三種狀況: 第一種:id所有相同,sql的執行順序是由上至下; 第二種:id所有不一樣,sql的執行順序是根據id大的優先執行(若是是子查詢,id的序號會遞增); 第三種:id既存在相同,又存在不一樣的。先根據id大的優先執行,再根據相同id從上至下的執行。
②. select_type
select 查詢的類型,主要是用於區別普通查詢,聯合查詢,嵌套的複雜查詢:
subquery和union 還能夠被標記爲dependent和uncacheable。 dependent意味着select依賴於外層查詢中發現的數據。 uncacheable意味着select中的某些特性阻止結果被緩存於一個item_cache中。
③. table
查詢結果來自於哪一個表。
④. partitions
表所使用的分區,若是要統計十年公司訂單的金額,能夠把數據分爲十個區,每年表明一個區。這樣能夠大大的提升查詢效率。
⑤. type
這是一個很是重要的參數,鏈接類型,常見的有:all , index , range , ref , eq_ref , const , system , null 八個級別。
性能從最優到最差的排序:null > system > const > eq_ref > ref > range > index > all
對java程序員來講,若保證查詢至少達到range級別或者最好能達到ref則算是一個優秀而又負責的程序員。
explain select * from t1 where name='yayun';
explain select t1.name from t1, t2 where t1.id=t2.id;
explain select * from t1 where id = 1
explain select * from t1 where id = (select min(id) from t2);
⑥. possible_keys
顯示查詢語句可能用到的索引(即查詢涉及字段中存在索引的字段,可能爲一個、多個或爲null),不必定被查詢實際使用,僅供參考使用。
⑦. key
顯示查詢語句實際使用的索引字段。若爲null,則表示沒有使用索引。
⑧. key_len
顯示索引中使用的字節數,可經過key_len計算查詢中使用的索引長度。
在不損失精確性的狀況下索引長度越短越好。key_len 顯示的值爲索引字段的最可能長度,並不是實際使用長度,即key_len是根據表定義計算而得,並非經過表內檢索出的。
⑨. ref
表示上述表的鏈接匹配條件,即哪些列或常量被用於查找索引列上的值。即顯示使用哪一個列或常數與key一塊兒從表中選擇行。
**⑩. rows
根據表統計信息及索引選用狀況,大體估算出找到所需的記錄所須要讀取的行數,值越大越很差。
即根據查詢語句及索引選用狀況,大體估算出要獲得查詢結果,所須要在表中讀取的行數。
⑪. filtered
一個百分比的值,和rows 列的值一塊兒使用,能夠估計出查詢執行計劃(QEP)中的前一個表的結果集,從而肯定join操做的循環次數。小表驅動大表,減輕鏈接的次數。
⑫. extra
包含不適合在其餘列中顯示但又十分重要的額外信息。
經過explain的參數介紹,咱們能夠得知:
可從如下幾個方面對數據庫性能進行優化:
①. 數據庫設計
等。
②. 索引設計
③. 總結
就一句話:使用合適的數據類型,選擇合適的索引:
使用合適的數據類型
選擇合適的索引列(即哪些列適合添加索引)
①. 分區
把一張表的數據分紅N個區塊,在邏輯上看最終只是一張表,但底層是由N個物理區塊組成的,經過將不一樣數據按必定規則放到不一樣的區塊中提高表的查詢效率。
②. 分表
③. 分庫
面對高併發的讀寫訪問,當數據庫沒法承載寫操做壓力時,無論如何擴展slave服務器,此時都沒有意義了。所以需對數據庫進行拆分,從而提升數據庫寫入能力,這就是分庫。
④. 問題
對於不少的數據庫系統都可以緩存執行計劃,對於徹底相同的sql, 可使用已經已經存在的執行計劃,從而跳過解析和生成執行計劃的過程。MYSQL提供了更爲高級的查詢結果緩存功能,對於徹底相同的SQL (字符串徹底相同且大小寫敏感) 能夠執行返回查詢結果。
MySQL緩存機制簡單的說就是緩存sql文本及查詢結果,若是運行相同的sql,服務器直接從緩存中取到結果,而不須要再去解析和執行sql。若是表更改了,那麼使用這個表的全部緩衝查詢將再也不有效,查詢緩存值的相關條目被清空。更改指的是表中任何數據或是結構的改變,包括INSERT、UPDATE、 DELETE、TRUNCATE(截斷)、ALTER TABLE、DROP TABLE或DROP DATABASE等,也包括那些映射到改變了的表的使用MERGE表的查詢。顯然,這對於頻繁更新的表,查詢緩存是不適合的,而對於一些不常改變數據且有 大量相同sql查詢的表,查詢緩存會節約很大的性能。
問:有個表特別大,字段是姓名、年齡、班級,若是調用select * from table where name = xxx and age = xxx該如何經過創建索引的方式優化查詢速度?
答:因爲mysql查詢每次只能使用一個索引,若是在name、age兩列上建立複合索引的話將帶來更高的效率。若是咱們建立了(name, age)的複合索引,那麼其實至關於建立了(name)、(name, age)兩個索引,這被稱爲最佳左前綴特性。所以咱們在建立複合索引時應該將最經常使用做限制條件的列放在最左邊,依次遞減。其次還要考慮該列的數據離散程度,若是有不少不一樣的值的話建議放在左邊,name的離散程度也大於age。
問:max(xxx)如何用索引優化?
答:在xxx列上創建索引,由於索引是B+樹順序排列的,鎖在下次查詢的時候就會使用索引來查詢到最大的值是哪一個。
問:如何對分頁進行優化?
答:SELECT * FROM big_table order by xx LIMIT 1000000,20,這條語句會查詢出1000020條的全部數據而後丟棄掉前1000000條,爲了不全表掃描的操做,在order by的列上加索引就能經過掃描索引來查詢。可是這條語句會查詢仍是會掃描1000020條,還能改進成select id from big_table where id >= 1000000 order by xx LIMIT 0,20,用ID做爲過濾條件將不須要查詢的數據直接去除。
針對Java程序員,筆者最近整理了一份完整的一線互聯網大廠面試真題,包含了Kafka、Mysql、Tomcat、Docker、Spring、MyBatis、Nginx、Netty、Dubbo、Redis、Netty、Spring cloud、分佈式、高併發、性能調優、微服務等架構技術。
須要的朋友請點擊下方傳送門免費領取
如下是部分面試題截圖