mysql 索引系列、讀寫分離、主從複製

mysql server組成

數據庫連接池:採用連接池,減少頻繁的開關連接
連接器-->分析器-->優化器-->執行器
連接器:控制用戶的連接
分析器:詞法分析,語法分析
優化器:優化sql語句,規定執行流程
執行器:sql語句的實際執行組件

存儲引擎:存放具體數據文件

選擇的存儲引擎不同,數據存放的位置不同,不同的文件格式。默認:innodb

innodb、mylsam:磁盤

memory:內存

查詢多使用 mylsam,增刪改多使用 innodb。

 

 

索引

存儲引擎的數據文件和索引存放位置不同,所以分爲聚簇索引和非聚簇索引
聚簇索引:數據和索引放在一起(innodb)
非聚簇索引:數據、索引都有單獨的文件(mylsam)
  .frm存放的是表結構、ibd存放的是數據和索引

默認所有的數據文件放到表空間中,不會有單獨的ibd文件,需要自行設置

索引文件的結構(實現原理)
hash:散列表,對值取hash值,取模。hash衝突解決:讓高位參與運算(擾動函數)
二叉樹:左子樹必須小於根節點,右子樹大於根節點。二分查找。缺點:深度無法控制,插入時性能低。
BTree: 非葉子節點二元組[key,data],即非葉子節點也會存儲數據。
B+Tree:只有葉子節點才存儲數據,葉子節點帶順序索引。

拓展:AVL樹,自旋樹。二叉樹的缺點:很容易發展成一個鏈表,右邊的節點一直增長。所以纔會有AVL樹

 

索引的優缺點

優勢:可以快速檢索,減少I/O次數,加快檢索速度;根據索引分組和排序,可以加快分組和排序;

劣勢:索引本身也是表,因此會佔用存儲空間,一般來說,索引表佔用的空間的數據表的1.5倍;索引表的維護和創建需要時間成本,這個成本隨着數據量增大而增大;構建索引會降低數據表的修改操作(刪除,添加,修改)的效率,因爲在修改數據表的同時還需要修改索引表;

 

常用索引

主鍵索引、唯一索引、普通索引、組合索引、全文索引

普通索引:用表中的普通列構建的索引,沒有任何限制(覆蓋索引 見下方講解)

全文索引(FULLTEXT):用大文本對象的列構建的索引(下一部分會講解)

組合索引:用多個列組合構建的索引,這多個列中的值不允許有空值(最左匹配匹配)

 

覆蓋索引:表A,主鍵a,字段b(增加索引),字段c 。 語句select id from A where b = N , 使用了覆蓋索引,b字段的索引中存放了主鍵a的值,可以直接返回。但如果是*的話,就需要進行回表了。

或者表A,字段d,字段h  ,語句 select d from A ,沒用覆蓋索引。對 d 增加索引,即實現了覆蓋索引

官網解釋覆蓋索引:只需要在一棵索引樹上就能獲取SQL所需的所有列數據,無需回表,速度更快

 

最左匹配:表A ,字段c,字段d,字段h。組合索引 d,h 。語句 select * from A where h=N,不會使用。

select * from A where d = N, 使用組合索引。即d,h索引 最左邊的 d 必須先匹配到

 

索引下推:索引下推一般可用於所求查詢字段(select列)不是/不全是聯合索引的字段,查詢條件爲多條件查詢且查詢條件子句(where/order by)字段全是聯合索引

例如表A ,字段a,字段b,字段c,聯合索引 b,c。

select * from A where b = N and c > 20  一般會使用聯合索引,將 符合條件 b = N 的數據查出來,然後回表查詢數據,再進行 c > 20的數據篩選。但是索引下推之後就: 符合條件的 b=N,聯合索引,所以 再從c 中找出 > 20 的,不符合條件的不要,再進行回表。

 

*在使用組合索引的時候可能因爲列名長度過長而導致索引的key太大,導致效率降低

解決:可以只取a和b的前幾個字符作爲索引

例如:ALTER TABLE 'table_name' ADD INDEX index_name(a(4),b(3));

 

InnoDB 是通過B+Tree結構對主鍵創建索引,如果沒有主鍵,會選擇唯一鍵,如果都沒有,會自動生成一個6位用戶不可見的id鍵。主鍵或唯一鍵創建索引,在節點的葉子節點存儲數據。

回表

當對非主鍵或唯一鍵創建索引時,例如對 name鍵創建索引,則該索引葉子節點存放的是主鍵或唯一鍵的值,需要回到上面再進行一次查找返回數據。

 

索引何時失效

    (1)組合索引未使用最左前綴,例如組合索引(A,B),where B=b不會使用索引;

    (2)like未使用最左前綴,where A like '%China';

    (3)搜索一個索引而在另一個索引上做order by,where A=a order by B,只使用A上的索引,因爲查詢只使用一個索引 ;

    (4)or會使索引失效。如果查詢字段相同,也可以使用索引。例如where A=a1 or A=a2(生效),where A=a or B=b(失效)

    (5)如果列類型是字符串,要使用引號。例如where A='China',否則索引失效(會進行類型轉換);

    (6)在索引列上的操作,函數(upper()等)、or、!=(<>)、not in等;

 

優化小細節:

  1. 不要對主鍵使用表達式  select * from table where id+1=5; 表達式不會使用索引
  2. 儘量使用主鍵查詢,而不是其它索引,主鍵查詢不會觸發回表
  3. 儘量使用前綴索引,例如一個字段很長,只使用前幾個字符作爲索引
  4. 查詢時,儘量使用最左匹配,例如where 和 order by 的字段。 order by 後面的字段不要一個 asc 一個 desc
  5. 能使用in 就不要使用 or ,能使用 union all 不使用 union  ,union 會進行distinct  : SELECT country FROM Websites  UNION ALL SELECT country FROM apps ORDER BY country,
  6. 更新頻繁、字段區分度不高的不建議建立索引
  7. 創建索引的列,不允許爲null
  8. 當需要進行表連接的時候,最好不要超過3張表
  9. 如果明確知道只有一條數據返回,limit 1 能提高效率。limit  限制輸出
  10. 單表索引控制在5個內
  11. 創建索引的時候避免:過早優化,對系統不瞭解的情況下進行優化

遷移數據時先將索引關閉,遷移完成後再打開。

 

join 優化

  1. 儘量使用index 列
  2. 使用 left join 或者 right  join  ,而不是 join on,強制控制表的查詢順序。
  3. join 的時候,小表 loop 大表。例如 A表數據比較多, 則  b left join A, 或者  A  right  join B

 

索引監控

Handler_read_first  通過 index 獲取數據的次數

Handler_read_key  讀取索引第一個條目的次數

Handler_read_last  讀取索引最後一個條目的次數

Handler_read_prev  通過索引讀取下一條數據的次數

Handler_read_prev 通過索引讀取上一條數據的次數

Handler_read_rnd  從固定未知讀取數據的次數

Handler_read_rnd_next  從數據節點讀取嚇一跳數據的次數

 

CBO 基於成本的查詢優化

RBO 基於規則的查詢優化

查詢優化

  1. 查詢時使用: explain 查詢語句
  2. show profile;
  3. limit 第一個參數值不宜過大
  4. 多表查詢時不要使用*,而是指定具體需要返回的列名。儘量使用別名
  5. 查詢時排除不需要的列,而不是在應用中再獲取前多少條
  6. 將外連接轉換爲內連接,內連接比外連接效率高。內連接:關鍵字:inner join,外連接 left join、right join
  7. 能用一個表達式完成的不要用兩個。例如 a != 4 ,而不是 a > 4 or a <4

 

mysql 一頁大小 16K

 

 

binlog 和 redo log

爲什麼了有了binlog還要有redo log

redo log的兩階段

 

ACID

A :原子性  , C:一致性 , I : 隔離性 , D : 持久性。

原子性 undolog 實現,隔離性通過鎖機制實現,持久性 通過redolog實現,一致性其它三者。

mysql默認事務隔離級別:可重複讀 

 

鎖機制

mylsam 默認表鎖

innodb 默認行鎖

innodb 的行鎖模式及加鎖方法

共享鎖(讀共享鎖)、排它鎖(寫獨佔鎖)

innodb 默認 對 update、delete、insert 自動給涉及的數據加上排它鎖。select語句默認不會加任何鎖類型。

innodb 只有通過索引條件檢索數據,才使用行鎖,否則,將使用表鎖。

排它鎖: select * from table where id = 1 for update;

 

mysql 主從複製和讀寫分離

 

mysql複製原理

1.master服務器將數據的改變記錄二進制binlog日誌,當master的數據發生改變時,將其改變寫入二進制日誌。

2.slave服務器會在一定時間間隔對master二進制日誌進行探測是否改變,發生改變,則開啓一個I/O Thread請求master二進制事件。

3.同時主節點爲每個I/O線程啓動一個dump線程,向其發送二進制事件。並保存至從節點本地的中繼日誌中,從節點將啓動mysql線程從中繼日誌讀取二進制日誌。在本地重放,使得其數據和主節點的保持一致。最後I/O Thread 和sqlThread 將進入睡眠狀態,等待下一次被喚醒。

就是說,主庫會生成兩個線程,一個I/O線程,一個sql線程。I/O線程會取請求主庫的binlog,並將得到的binlog寫道本地的relay-log(中繼)中。主庫會生成一個log dump線程,給從庫I/O線程傳binlog。sql 線程會讀取relay log文件的日誌,並解析成sql語句逐一執行。

注意:

1.master將操作語句記錄到binlog日誌中,然後授予slave遠程連接的權限(master一定要開啓binlog二進制日誌功能,通常爲了數據安全考慮,slave也開啓binlog功能)

2.slave開啓兩個線程:IO線程和sql線程。其中I/O線程負責讀取master的binlog內容到中繼日誌relay log中。sql線程負責從relay log日誌裏讀出binlog內容,並更新到slave數據庫裏。這樣就能保證slave數據和master數據一致。

3.mysql複製至少需要兩個mysql的服務,當然mysql服務可以在不同服務器上,也可以在同一臺上。

4.mysql複製最好確保master和 slave服務器 版本相同。如不能滿足,那麼保證master版本低於slave版本。

5.master和slave節點時間需同步。

 

具體步驟:

1.從庫通過手工執行change master to 語句連接主庫,提供了連接的用戶一切條件(user、password、ip、port),並且讓從庫知道,二進制日誌的起點位置(file名postion號);start slave

2.從庫的IO線程和主庫的dump線程建立連接

3.從庫根據change master to 語句提供的file名和position 號,IO線程向主庫發起binlog的請求

4.主庫dump線程根據從庫的請求,將本地binlog以events的方式發給從庫IO線程

5.從庫IO線程接收到binlog events,並存到本地relay-log 中,傳送過來的信息,會記錄到master.info中

6.從庫sql線程應用relay-log,並且把應用過的記錄到relay-log.info中,默認情況下,已經應用過的relay會自動被清理purge

 

mysql主從形式

1、一主一從

2、主主複製

3、一主多從

4、多主一從

5.聯級複製

步驟:

1. 創建數據庫 master、slave 執行 create database msb;  use msb;  msb:自己起名

2.在主(node1)服務器進行如下配置:

修改配置文件,執行以下命令打開mysql配置文件

vi /etc/my.cnf

在mysqld 模塊中添加如下配置信息

log-bin=master-bin #二進制文件名

binlog-format=Row #二進制日誌格式,有row、statement、mixed三種格式,row指的是把改變的內容複製過去,而不是把命令從服務器執行一遍。statement指的是在主服務器上執行的sql語句,在從服務器上執行同樣的語句,mysql默認採用基於語句的複製,效率比較高。mixed指的是默認採用基於語句的複製,一旦發現基於語句的無法精確的複製時,就會採用基於行的複製。

server-id=1  #要求各個服務器的id必須不一樣

binlog-do-db=msb #同步的數據庫名稱

 

3.配置從服務器登錄主服務器的賬號授權

--授權操作

set global validate_password_policy=0;

set global validate_password_length=1;

grant replication slave on *.* to 'root'@'%' identified by '123456';

--刷新權限

flush privileges;

4.從服務器的配置

#修改配置文件,執行以下命令打開mysql配置文件

vi /etc/my.cnf

#在mysqld模塊中添加如下配置信息

log-bin=master-bin #二進制的文件名

binlog-format=Row #二進制文件的格式

server-id=2  #服務器的id

5.重啓主服務器的mysqld服務

#重啓mysql服務

service mysqld restart

#登錄mysql數據庫

mysql -uroot -p

#查看master狀態

show master status;

6.重啓從服務器並進行相關配置

#重啓mysql服務

service mysqld restart

#登錄mysql數據庫

mysql -uroot -p

#連接主服務器

change master to

master_host='masterIp',master_user='root',master_password='1234556',master_port=3306,master_log_file='master-bin.001',master_log_pos=154;

#啓動slave

start slave

#查看slave 的狀態

show slave status\G (注意沒有分號)

7.此時可以在主服務器進行相關的數據添加刪除工作,在從服務器看相關的狀態

mysql主從同步延遲分析 (5.7版本之前存在)

   mysql的主從複製都是單線程的操作,主庫對所有DDL和DML 產生的日誌寫進binlog,由於binlog是順序寫,所以效率很高。slave的sql Thread線程將主庫的DDL和DML操作事件在slave中重放。DML和DDL的IO操作是隨機的,不是順序,所以成本要高很多。另一方面,由於slq Thread也是單線程的,當主庫的併發比較高時,產生的DML數量超過slave的SQL Thread所能處理的速度,或者當slave中有大型query語句產生了鎖等待,那麼延時就產生了。

解決方案:

1.業務的持久化層的實現採用分庫架構,mysql服務可平行擴展,分散壓力。

2.單個庫讀寫分離,一主多從,主寫從讀,分散壓力。這樣從庫壓力主庫高,保護主庫。

3.服務的基礎架構在業務和mysql之間加入memecache或者redis的cache層。降低mysql的讀壓力。

4.不同業務的mysql物理上放在不同機器,分散壓力。

5.使用比主庫更好的硬件設備作爲slave,mysql壓力小,延遲自然小。

6.使用更加強勁的硬件設備。

MySQL 5.7新特性:並行複製原理(MTS)MySQL 5.7版本後,複製延遲問題永不存在

https://blog.csdn.net/A_man_only/article/details/84257702

 

Mysql 讀寫分離

生產不推薦 mysql-proxy ,性能不高

 

amoeba  -- 阿里巴巴

具有負載均衡、高可用性、sql過濾、讀寫分離、可路由相關的query到目標數據庫、可併發請求多臺數據庫合併結果

主要解決:

1.降低數據切分帶來的複雜多數據庫結構

2.提供切分規則,並降低數據切分規則給應用帶來的影響

3.降低db與客戶端的連接數

4.讀寫分離

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章