《高性能MySQL》筆記前篇

一、mysql基礎知識

Mysql是最流行的關係型數據庫管理系統,在WEB應用方面MySQL是最好的RDBMS(Relational Database Management System:關係數據庫管理系統)應用軟件之一。

什麼是數據庫?
數據庫(Database)是按照數據結構來組織、存儲和管理數據的倉庫,
每個數據庫都有一個或多個不同的API用於創建,訪問,管理,搜索和複製所保存的數據。
我們也可以將數據存儲在文件中,但是在文件中讀寫數據速度相對較慢。
三範式:
1NF:字段不可分;
2NF:有主鍵,非主鍵字段依賴主鍵; 
3NF:非主鍵字段不能相互依賴; 

解釋:   
1NF:原子性 字段不可再分,否則就不是關係數據庫;  
2NF:唯一性 一個表只說明一個事物;   
3NF:每列都與主鍵有直接關係,不存在傳遞依賴;
臨時表:
 CREATE TEMPORARY TABLE 表名();  
臨時表只在當前連接可見,當關閉連接時,Mysql會自動刪除表並釋放所有空間。
獲取MySQL服務器信息:
SELECT VERSION( )   服務器版本信息  
SELECT DATABASE( )  當前數據庫名 (或者返回空)  
SELECT USER( )  當前用戶名  
SHOW STATUS 服務器狀態  
SHOW VARIABLES  服務器配置變量  
複製表:
1、CREATE TABLE tmp SELECT * from table1; 
2、create table tmp like table1; 
   insert into tmp (values) select * from table1; 
NSERT IGNORE INTO 、REPLACE INTO 與 INSERT INTO 的區別:
INSERT IGNORE會忽略數據庫中已經存在的數據,如果數據庫沒有數據,就插入新的數據,如果有數據的話就跳過這條數據。
這樣就可以保留數據庫中已經存在數據,達到在間隙中插入數據的目的。  
INSERT INTO 向數據表中插入重複數據,執行後會出錯;  
REPLACE INTO 如果存在primary 或 unique相同的記錄,則先刪除掉。再插入新記錄。

二、事務

1、事務的ACID概念:
原子性:一個事務必須被視爲一個不可分割的最小工作單元,整個事務中的所有操作要麼全部提交成功,要麼全部失敗回滾。
一致性:數據庫總是從一個一致性的狀態轉換到另外一個一致性的狀態。
隔離性:一個事務所做的修改再最終提交以前,對其它事務是不可見的;
持久性:一旦事務提交,則其所做的修改就會永久保存到數據庫中。
2、隔離級別

低級別的隔離級一般支持更高的併發處理,並擁有更低的系統開銷。詳解

READ UNCOMMITTED(未提交讀):事務中的修改,即使沒有提交,對其它事務也都是可見的;
READ COMMITTED(提交讀):一個事務從開始直到提交之前,所做的任何修改對其它事務都是不可見的;
REPEATABLE READ(可重複讀):在同一個事務中多次讀取同樣記錄的結果是一致的;(MySQL默認事務隔離級別)
SERIALIZABLE(可串行化):最高的隔離級別。通過強制事務串行化執行,避免幻讀的問題。SERIALIZABLE會在讀取的每一行數
    據上都加鎖,可能導致大量的超市和鎖爭用的問題。只有在非常需要去報數據一致性且可以接受沒有併發的情況下使用。  
3、死鎖
指兩個或多個事務在同一資源上互相佔用,並請求鎖定對方佔用的資源,從而導致惡性循環的現象。
innodb解決方法:將持有最少行級排他鎖的事務進行回滾。
4、提交事務
MySQL默認採用自動提交(AUTOCOMMIT)模式。如果不是顯示開始一個事務,則每個查詢都被當作一個事務執行提交操作。
可以通過設置AUTOCOMMIT變量來啓用或者禁用自動提交模式:
mysql> show variables like 'AUTOCOMMIT';
mysql> set AUTOCOMMIT=1;

*注意:alter table , lock tables  命令在執行之前會強制執行COMMIT提交當前的活動事務;

三、剖析查詢語句

1、慢查詢日誌

MySQL慢查詢日誌是一種輕量而且功能全面的性能剖析工具、CPU開銷小; 詳解

2、定位最差的查詢語句:

pt-query-digest工具

(1) 分析慢查詢日誌並生成剖析報告的工具:pt-query-digest;
(2) 定位報告中的MySQL:找到 # Query 1: … byte 3214 __ ; 
(3) 通過字節偏移值(3214)直接跳轉到日誌的對應部分,命令: 
    tail -c +3214 保存慢查詢日誌路徑 | head -n100 ;
# 320ms user time, 70ms system time, 26.20M rss, 239.07M vsz
# Current date: Sat Mar 24 19:16:27 2018
# Hostname: localhost.localdomain
# Files: /var/log/mariadb/slow_query_log.txt
# Overall: 2 total, 1 unique, 0.09 QPS, 0.65x concurrency ________________
# Time range: 2018-03-24 18:54:22 to 18:54:45
# Attribute          total     min     max     avg     95%  stddev  median
# ============     ======= ======= ======= ======= ======= ======= =======
# Exec time            15s      5s     10s      8s     10s      4s      8s
# Lock time              0       0       0       0       0       0       0
# Rows sent              2       1       1       1       1       0       1
# Rows examine           0       0       0       0       0       0       0
# Rows affecte           0       0       0       0       0       0       0
# Query size            31      15      16   15.50      16    0.71   15.50

# Profile
# Rank Query ID           Response time  Calls R/Call V/M   Item
# ==== ================== ============== ===== ====== ===== ======
#    1 0xF9A57DD5A41825CA 15.0024 100.0%     2 7.5012  1.67 SELECT

# Query 1: 0.09 QPS, 0.65x concurrency, ID 0xF9A57DD5A41825CA at byte 0 __
# This item is included in the report because it matches --limit.
# Scores: V/M = 1.67
# Time range: 2018-03-24 18:54:22 to 18:54:45
# Attribute    pct   total     min     max     avg     95%  stddev  median
# ============ === ======= ======= ======= ======= ======= ======= =======
# Count        100       2
# Exec time    100     15s      5s     10s      8s     10s      4s      8s
# Lock time      0       0       0       0       0       0       0       0
# Rows sent    100       2       1       1       1       1       0       1
# Rows examine   0       0       0       0       0       0       0       0
# Rows affecte   0       0       0       0       0       0       0       0
# Query size   100      31      15      16   15.50      16    0.71   15.50
# String:
# Databases    db_test
# Hosts        localhost
# Users        root
# Query_time distribution
#   1us
#  10us
# 100us
#   1ms
#  10ms
# 100ms
#    1s  ################################################################
#  10s+  ################################################################
# EXPLAIN /*!50100 PARTITIONS*/
select sleep(10)\G
3、剖析單條查詢;
1、使用SHOW PROFILE;
mysql> set profiling = 1; #每個查詢信息都會保存到一張臨時表;
mysql> select * from zhang;
mysql> show profiles;
mysql> show profile for query 臨時表id; # 提高精度,確定單條語句花費的時間在哪個步驟

*缺點:無法對花費的時間order by 排序;
+--------------------------------+-----------+
| Status                         | Duration  |
+--------------------------------+-----------+
| starting                       |  0.000033 |
| Waiting for query cache lock   |  0.000009 |
| init                           |  0.000008 |
| checking query cache for query |  0.001796 |
| checking permissions           |  0.000102 |
| Opening tables                 |  0.000660 |
| After opening tables           |  0.000017 |
| System lock                    |  0.000061 |
| Table lock                     |  0.000024 |
| Waiting for query cache lock   |  0.000101 |
| init                           |  0.000291 |
| optimizing                     |  0.000188 |
| statistics                     |  0.000245 |
| preparing                      |  0.000423 |
| executing                      |  0.000110 |
| Sending data                   | 18.697863 |
| end                            |  0.000044 |

耗費時間排序解決方案:
截圖

2、使用SHOW STATUS

說明:show status 命令返回會話級別的計數器,但不是剖析工具;可以用來猜測哪些操作代價較高或者消耗的時間較多。最有用的計數器包括句柄計數器(handler counter)、臨時文件和表計數器等;

mysql> flush status; #將會話級別的計數器重置爲0
mysql> select * from zhang;
mysql> show status where variable_name like 'Handler%' or variable_name like 'Created%';

四、索引

索引優點:
1、索引大大減少了服務器所需要掃描的數據量。
2、索引可以幫助服務器避免排序和臨時表。
3、索引可以將隨機I/O 變爲順序I/O。

1、哈希索引

1.1 什麼是哈希索引?
哈希索引基於哈希表實現,只有精確匹配索引所有列的查詢纔有效。對於每一行數據,存儲引擎都會對所有的索引列計算一個哈希碼。
哈希索引將所有的哈希碼存儲在索引中,同時在哈希表中保存指向每個數據行的指針。(只有Memory引擎顯式支持哈希索引)
1.2 InnoDB 的“自適應哈希索引”
InnoDB引擎有一個特殊的功能叫“自適應哈希索引”。當InnoDB注意到某些索引值被使用得非常頻繁時,它會在內存中基於B-Tree索引之上再創建一個哈希索引。
這是一個完全自動的、內部的行爲,用戶無法控制或者配置。

2、僞哈希索引

創建僞哈希索引策略:
1、使用這個策略前,要先了解哈希索引的優劣和原理;
2、思路:在B-Tree基礎上創建夜歌僞哈希索引,這和真正的哈希索引不是一回事。它是使用哈希值而
不是鍵本身進行索引查找,需要在查詢的where子句中手動指定使用哈希函數;
實例
需要存儲大量的url , 並需要根據url進行搜索查找。直接用B-Tree來存儲url存儲的內容就會很大,
正常情況的查詢有:
mysql> select id from url where url="http://www.mysql.com";
若刪除原來的url列上的索引,而新增一個被索引的url_crc列 ,使用CRC32做哈希,就可以使用下面的方式查詢:
mysql> select id from url where url="http://www.mysql.com" and url_crc=CRC32("http://www.mysql.com");
 這樣做的性能會非常高,因爲MySQL優化器會使用這個選擇性很高而體積很小的基於url_crc列的索引來完成查找
(上例中索引值爲:1544886819)。

缺陷:需要維護哈希值。可以手動維護,也可以使用觸發器實現。
1、使用觸發器實現在插入和更新是維護url_crc列:
創建如下表:
create table pseudohash(
    id int unsigned not null auto_increment,
    url varchar(255) not null,
    url_crc int unsigned not null default 0,
    primary key(id)
);

創建觸發器,先臨時修改一下語句分隔符,這樣就可以在觸發器定義中使用分號:
mysql> delimiter //
mysql> create trigger pseudohash_crc_ins before insert on pseudohash for each row begin set new.url_crc=crc32(new.url); end;//
mysql> create trigger pseudohash_crc_upd before update on pseudohash for each row begin set new.url_crc=crc32(new.url); end;//
mysql> delimiter ;#注意這裏的空格鍵

那麼,新增或更新url同時url_crc也回自動更新;
2、注意
    採用這種方式切記不要使用SHA1()和MD5()作爲哈希函數。這兩個函數是強加密函數,計算出來的hash值時非常長的字符
串,會浪費大量空間,比較時也會更慢。它們設計目標是最大限度消除衝突(這裏不需要這樣高的要求)。如果數據表非常大,
crc32()會出現大量的哈希衝突,可以考慮實現一個簡單的64位哈希函數(返回整數),例如:
mysql> select CONV(RIGHT(MD5('http://www.mysql.com/'),16),16,10) as hash64;

爲了避免哈希衝突導致查詢無法正常工作,where條件包含常量值url='http://www.mysql.com':
select id from url where url="http://www.mysql.com" and url_crc=CRC32("http://www.mysql.com");

3、前綴索引

什麼是前綴索引?
有時候需要索引很長的字符列,這會讓索引變得很大且慢。一個方法使用僞哈希索引,另外還可以是前綴索引;
前綴索引:索引開始的部分字符。可以大大節約索引空間,從而提高索引效率。但會降低索引的選擇性(基數);
3.1 計算不同前綴長度的選擇性:

計算索引基數實例

如果繼續增加前綴長度,選擇性提升的幅度已經很小就不需在加;

2、創建前綴索引
alter table zhang add index `indexname`(col(6)); // 選擇索引前綴長度爲6
親測,前綴索引和普通索引:alter table zhang add index `indexname`(col);
普通索引查詢速度更快(可能測試列的字符較少,可比性低),但兩種索引都可以提升查詢性能;

4、聚簇索引

什麼是聚簇索引?
聚簇索引並不是一種單獨索引類型,而是一種數據存儲方式。InnoDB的聚簇索引實際上在同一個結構中保存了B-Tree索引和數據行。
如果沒有定義主鍵,InnoDB會選擇唯一的非空索引替代。如果沒有這樣的索引,InnoDB會隱式定義一個主鍵來作爲聚簇索引。
聚集的數據優點:
1、可以把相關的數據保存在一起;
2、數據訪問更快;
3、使用覆蓋索引掃描的查詢可以直接使用主鍵值;
缺點
1、如果數據全部放到內存中,聚簇索引沒什麼優勢;
2、更新聚簇索引列的代價很高,因爲會強制InnoDB將每個被更新的行移動到新的位置;
3、二級索引可能比想象的要更大,因爲二級索引的葉子節點包含了引用行的主鍵列;
4、二級索引訪問需要兩次索引查找;


* 二級索引葉子結點保存的不是指向行的物理位置的指針,而是行的主鍵值;意味着通過二級索引查找行,
存儲引擎需要找到二級索引的葉子結點獲得對應的主鍵值,然後根據這個值去聚簇索引中查找到對應的行。
兩次B-Tree查找,自適應哈希索引能夠減少這樣的重複工作。(好處:在移動行數據時無須更新二級索引的這個“指針”)

5、覆蓋索引

什麼是覆蓋索引?
如果一個索引包含所有需要查詢的字段的值,我們就稱“覆蓋索引”;
覆蓋索引的好處
1、索引條目通常遠小於數據行大小,所以如果只需要讀取索引,那mysql就會極大的減少數據訪問量;
2、如果二級主鍵能夠覆蓋查詢,則可以避免對主鍵索引的二次查詢;
覆蓋索引查詢

在EXPLAIN的Extra列可以看到“Using index”信息;

+------+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
| id   | select_type | table | type  | possible_keys | key     | key_len | ref   | rows | Extra       |
+------+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+
|    1 | SIMPLE      | zhang | const | PRIMARY       | PRIMARY | 4       | const |    1 | Using index |
+------+-------------+-------+-------+---------------+---------+---------+-------+------+-------------+

6、順序主鍵什麼時候會造成更壞的結果?

對於高併發工作負載,在innodb中主鍵順序插入可能會造成明顯的爭用。主鍵的上界會成爲"熱點",併發插入可能導致間隙鎖競爭。
另一個熱點可能是AUTO_INCREMENT鎖機制;
解決:重新設計表或應用,或更改innodb_autoinc_lock_mode配置(可能不支持);

7、索引條件下推

5.6版本的索引條件下推: 參考

索引條件下推可以解釋聯合索引查詢快: 
走索引的查詢,只能走最左前綴列,其他聯合索引的列在索引後再通過where過濾;

五、查詢性能優化

1、查看執行計劃重構後的查詢:

explain extended 查詢語句;
show warnings;

2、關於IN() 子查詢

在IN()裏面使用子查詢,性能會非常糟糕;因爲MySQL會將相關的外層表壓倒子查詢中,先對外表做全表掃描,
然後根據返回的字段逐個執行子查詢;
例子:
select * from new_zhang a where id in(select b.id from new_zhang b where name like '%2018-01-26%');

MySQL重構查詢:
select * from new_zhang a where exists(select b.id from new_zhang b where name like '%2018-01-26%' and a.id=b.id);
優化策略:
   1、直接使用重構查詢的方法;省略重構查詢步驟
   2、使用子查詢:
   select * from new_zhang a inner join new_zhang b using(id) where b.name like '%2018-01-26%';
   3、使用函數GROUP_CONCAT()構造由逗號分隔的列表,分兩次查詢;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章