MySQL之四 存儲引擎

1.介紹

存儲引擎MySQL中的「文件系統」html

MySQL體系結構

 

 

 InnoDB存儲引擎介紹python

 

 

 

My1SAM 和InnoDB區別

 mysql
 MariaDB [(none)]> show engines; 能夠查看當前My1SAM和InnoDB信息
 +--------------------+---------+----------------------------------------------------------------------------+--------------+------+------------+
 | Engine             | Support | Comment                                                                   | Transactions | XA   | Savepoints |
 +--------------------+---------+----------------------------------------------------------------------------+--------------+------+------------+
 | MEMORY             | YES     | Hash based, stored in memory, useful for temporary tables                 | NO           | NO   | NO         |
 | MRG_MYISAM         | YES     | Collection of identical MyISAM tables                                     | NO           | NO   | NO         |
 | CSV               | YES     | CSV storage engine                                                         | NO           | NO   | NO         |
 | BLACKHOLE         | YES     | /dev/null storage engine (anything you write to it disappears)             | NO           | NO   | NO         |
 | MyISAM             | YES     | MyISAM storage engine                                                     | NO           | NO   | NO         |
 | InnoDB             | DEFAULT | Percona-XtraDB, Supports transactions, row-level locking, and foreign keys | YES

My1SAM 和InnoDB

MyISAM InnoDB
不支持事務 支持事務,適合處理大量短時間事務
表級鎖,當表鎖定時,其餘人都沒法使用,影響併發性範圍大 行級鎖
讀寫相互阻塞,寫入不能讀,讀時不能寫 讀寫阻塞與事務隔離級別相關
只緩存索引 可緩存數據和索引
不支持外鍵約束 支持外鍵
不支持聚簇索引 支持聚簇索引
讀取數據較快,佔用資源較少 MySQL5.5後支持全文索引
不支持MVCC(多版本併發控制機制)高併發 支持MVCC高併發
崩潰恢復性差 崩潰恢復性好
MySQL5.5.5前默認的數據庫引擎 MySQL5.5.5後默認的數據庫引擎
適用只讀(或者寫較少)、表較小(能夠接受長時間進行修復操做)的場景 系統表空間文件:ibddata1, ibddata2, ...
tb_name.frm 表結構,tb_name.MYD 數據行,tb_name.MYI 索引 每表兩個數據庫文件:tb_name.frm 每表表結構,tb_name.ibd 數據行和索引

彩蛋:InnoDB 核心特性有哪些? InnoDB和MyISAM區別有哪些? InnoDB支持事務、MVCC、聚簇索引、外鍵、緩衝區、AHI、CR、DW,MyISAM不支持。 InnoDB支持行級鎖,MyISAM支持表級鎖。 InnoDB支持熱備(業務正常運行,影響低),MyISAM支持溫備份(鎖表備份)。 InnoDB支持CR(自動故障恢復),宕機自動故障恢復,數據安全和一致性能夠獲得保證。MyISAM不支持,宕機可能丟失當前修改。mysql

種類

Oracle MySQL自帶的存儲引擎種類

 mysql> show engines;
 MRG_MYISAM      
 CSV              
 MyISAM                      
 BLACKHOLE        
 PERFORMANCE_SCHEMA
 InnoDB                
 ARCHIVE            
 MEMORY            
 FEDERATED  

MyISAM引擎特色

 不支持事務
 表級鎖定,當表鎖定時,其餘人都沒法使用,影響併發性範圍大
 讀寫相互阻塞,寫入不能讀,讀時不能寫
 只緩存索引
 不支持外鍵約束
 不支持聚簇索引
 讀取數據較快,佔用資源較少
 不支持MVCC(多版本併發控制機制)高併發
 崩潰恢復性較差
 MySQL5.5.5前默認的數據庫引擎<br> 數據庫有三個文件,有frm、MYD、MYI後綴的文件

MyISAM存儲引擎適用場景

 只讀(或者寫較少)、表較小(能夠接受長時間進行修復操做)

MyISAM引擎文件

 tbl_name.frm 表格式定義
 tbl_name.MYD 數據文件
 tbl_name.MYI 索引文件

InnoDB引擎特色

 行級鎖
 支持事務,適合處理大量短時間事務
 讀寫阻塞與事務隔離級別相關
 可緩存數據和索引
 支持聚簇索引
 崩潰恢復性更好
 支持MVCC高併發
 從MySQL5.5後支持全文索引
 從MySQL5.5.5開始爲默認的數據庫引擎<br> 數據庫有兩個文件frm和idb後綴的文件

InnoDB支持更好的特性:Percona-XtraDB, Supports transactions(支持事務), row-level locking(行級鎖), and foreign keys(支持外鍵)git

事務含義:

 1.由一系列動做組合起來的一個完整的總體,須要將全部的動做所有作掉,要麼全不作,具備原子性。
 2.(rollback)支持回滾、撤銷以前未作完的事務。

InnoDB數據庫文件

 全部InnoDB表的數據和索引放置於同一個表空間中
 表空間文件:datadir定義的目錄下
 數據文件:ibddata1, ibddata2, ...
 每一個表單獨使用一個表空間存儲表的數據和索引
 啓用:innodb_file_per_table=ON

參看:https://mariadb.com/kb/en/library/xtradbinnodb-server-system-variables/#innodb_file_per_table ON (>= MariaDB 5.5) 兩類文件放在數據庫獨立目錄中github

 數據文件(存儲數據和索引):tb_name.ibd
 表格式定義:tb_name.frm

其它存儲引擎面試

 Performance_Schema:Performance_Schema數據庫使用
 Memory :將全部數據存儲在RAM中,以便在須要快速查找參考和其餘相似數據的環境中進行快速訪問。適用存放臨時數據。引擎之前被稱爲HEAP引擎
 MRG_MyISAM:使MySQL DBA或開發人員可以對一系列相同的MyISAM表進行邏輯分組,並將它們做爲一個對象引用。適用於VLDB(Very Large Data Base)環境,如數據倉庫
 Archive :爲存儲和檢索大量不多參考的存檔或安全審覈信息,只支持SELECT和INSERT操做;支持行級鎖和專用緩存區
 Federated聯合:用於訪問其它遠程MySQL服務器一個代理,它經過建立一個到遠程MySQL服務器的客戶端鏈接,並將查詢傳輸到遠程服務器執行,然後完成數據存取,提供連接單獨MySQL服務器的能力,以便從多個物理服務器建立一個邏輯數據庫。很是適合分佈式或數據集市環境
 BDB:可替代InnoDB的事務引擎,支持COMMIT、ROLLBACK和其餘事務特性
 Cluster/NDB:MySQL的簇式數據庫引擎,尤爲適合於具備高性能查找要求的應用程序,這類查找需求還要求具備最高的正常工做時間和可用性
 CSV:CSV存儲引擎使用逗號分隔值格式將數據存儲在文本文件中。可使用CSV引擎以CSV格式導入和導出其餘軟件和應用程序之間的數據交換
 BLACKHOLE :黑洞存儲引擎接受但不存儲數據,檢索老是返回一個空集。該功能可用於分佈式數據庫設計,數據自動複製,但不是本地存儲
 example:「stub」引擎,它什麼都不作。可使用此引擎建立表,但不能將數據存儲在其中或從中檢索。目的是做爲例子來講明如何開始編寫新的存儲引擎
 MariaDB支持的其它存儲引擎:
 OQGraph
 SphinxSE
 TokuDB
 Cassandra
 CONNECT
 SQUENCE

3、管理存儲引擎

 查看mysql支持的存儲引擎
 show engines;
 查看當前默認的存儲引擎
 show variables like '%storage_engine%';
 設置默認的存儲引擎
 vim /etc/my.conf
 [mysqld]
 default_storage_engine= InnoDB
 查看庫中全部表使用的存儲引擎
 show table status from db_name;
 查看庫中指定表的存儲引擎
 show table status like ' tb_name ';
 show create table tb_name;
 設置表的存儲引擎:
 CREATE TABLE tb_name(... ) ENGINE=InnoDB;
 ALTER TABLE tb_name ENGINE=InnoDB;

面試題:請你列舉MySQL中支持的存儲引擎種類?

 InnoDB、MyISAM、CSV、MEMORY

分支產品的引擎種類介紹

 - PerconaDB:默認是XtraDB
 - MariaDB:默認是InnoDB
 - 其餘引擎:TokuDB、MyRocks、Rocksdb
  - 特色:壓縮比15倍以上,插入數據性能快3-5倍

適應場景: sql

 例如Zabbix監控類的平臺、歸檔庫、歷史數據存儲業務
 InnoDB存儲引擎特性
 MVCC       : 多版本併發控制
 聚簇索引   : 用來組織存儲數據和優化查詢,IOT。
 支持事務   : 數據安全保證
 支持行級鎖 : 控制併發
 外鍵
 多緩衝區支持
 自適應Hash索引: AHI
 複製中支持高級特性。
 備份恢復: 支持熱備。
 自動故障恢復:CR Crash Recovery
 雙寫機制:DWB  Double Write Buffer

案例1 (項目)

 某期學員負責: 運維 + MySQL 工做
  環境: zabbix 3.2+centos7.3+mariaDB 5.5 InnoDB引擎,zabbix系統 監控了2000多個節點服務
 現象:每隔一段時間zabbix卡的要死,每隔3-4個月,都要從新搭建一遍zabbix,存儲空間常常爆滿.
  問題
 zabbix 版本太低。
  數據庫版本
 zabbix數據庫500G,存在一個文件裏ibdata1,手工刪除1個月以前的數據,空間不釋放。

優化建議:shell

 數據庫版本升級到percona 5.7+ 版本 mariadb 10.x+,zabbix升級更高版本
 存儲引擎改成tokudb
 監控數據按月份進行切割(二次開發:zabbix 數據保留機制功能重寫,數據庫分表)
 關閉binlog和雙1
 參數調整....
 優化結果:
 監控狀態良好

參考: https://www.jianshu.com/p/898d2e4bd3a7數據庫

爲何選用tokudb?json

 MariaDB 10.0.9原生態支持TokuDB,另外通過測試環境,5.7要比5.5 版本性能 高  2-3倍
 TokuDB:insert數據比Innodb快的多,數據壓縮比要Innodb高
 監控數據按月份進行切割,爲了可以truncate每一個分區表,當即釋放空間
 關閉binlog ----->減小無關日誌的記錄.
 參數調整...----->安全性參數關閉,提升性能.

擴展:部署 zabbix新版+ 新版本 tokudb VS 部署 zabbix + 低版本mariadb Tokudb特性:

TokuDB獨有的其餘功能包括:

  • 高達25倍的數據壓縮

  • 快速插入

  • 經過無讀複製消除從機延遲

  • 熱架構更改

  • 熱索引建立 - TokuDB表支持插入、刪除和查詢,而索引添加到該表時沒有停機時間

  • 熱列添加、刪除、擴展和重命名 — 當 alter table 添加、刪除、擴展或重命名列時,TokuDB表支持不停機插入、刪除和查詢

  • 在線備份

參考內容: https://www.jianshu.com/p/898d2e4bd3a7 https://mariadb.com/kb/en/installing-tokudb/ https://www.percona.com/doc/percona-server/5.7/tokudb/tokudb_installation.html

 Additional features unique to TokuDB include:
 Up to 25x Data Compression
 Fast Inserts
 Eliminates Slave Lag with Read Free Replication
 Hot Schema Changes
 Hot Index Creation - TokuDB tables support insertions, deletions and queries with no down time while indexes are being added to that table
 Hot column addition, deletion, expansion, and rename - TokuDB tables support insertions, deletions and queries without down-time when an alter table adds, deletes, expands, or renames columns
 On-line Backup

案例2:(項目)

環境: centos 5.8 ,MySQL 5.0版本,MyISAM存儲引擎,網站業務(LNMP),數據量50G左右 現象問題: 業務壓力大的時候,很是卡;經歷過宕機,會有部分數據丟失.

問題分析:

 1.MyISAM存儲引擎表級鎖,在高併發時,會有很高鎖等待
 2.MyISAM存儲引擎不支持事務,在斷電時,會有可能丟失數據

職責

 1.監控鎖的狀況:有不少的表鎖等待
 2.存儲引擎查看:全部表默認是MyISAM

解決方案:

 升級MySQL 5.6.1x版本
 升級遷移全部表到新環境,調整存儲引擎爲InnoDB
 開啓雙1安全參數
 重構主從

存儲引擎的基本操做

查看存儲引擎 查詢支持的存儲引擎

 mysql> show engines;

查詢、設置默認存儲引擎

 -- 會話級別(僅影響當前會話)
 set default_storage_engine=myisam;
 -- 全局級別(僅影響新會話)重啓失效
 set global default_storage_engine=myisam;
 -- 寫入配置文件,重啓永久生效
 vim /etc/my.cnf
 [mysqld]
 default_storage_engine=InnoDB

存儲引擎是做用在表上的,也就意味着,不一樣的表能夠有不一樣的存儲引擎類型。

查看、設定 表的存儲引擎

(1) 查看某張表的存儲引擎

 SHOW create table 表名;
 use 表名;
 SHOW TABLE STATUS LIKE 'countrylanguage'\G

(2) 查詢系統中全部業務表的存儲引擎信息

  mysql> select table_schema,table_name ,engine 
  from information_schema.tables  
  where table_schema not in ('sys','mysql','information_schema','performance_schema');

(3)建立表設定存儲引擎

 create table 表名 (id int) engine=innodb charset=utf8mb4;

(4)修改已有表的存儲引擎

 alter table 庫名.表名 engine=innodb;

項目:將全部的非InnoDB引擎的表查詢出來,批量修改成InnoDB

  1. 查詢:

 SELECT table_schema, table_name, ENGINE 
 FROM information_schema.tables  
 WHERE table_schema NOT IN ('sys','mysql','information_schema','performance_schema')
 AND ENGINE !='innodb';
  1. 開啓導出文件功能

 vim /etc/my.cnf 
 [mysqld]
 secure-file-priv=/tmp
  1. 構建批量修改語句:

 SELECT CONCAT("alter table ",table_schema,".",table_name," engine=innodb;") 
 FROM information_schema.tables 
 WHERE table_schema NOT IN ('sys','mysql','information_schema','performance_schema') 
 AND ENGINE !='innodb' INTO OUTFILE '/tmp/a.sql';
  1. 執行批量修改語句:

 source /tmp/a.sql

InnoDB 存儲引擎的體系結構

磁盤結構 (on-disk)

ibdata1:系統數據字典信息(統計信息),UNDO表空間等數據 ib_logfile0 ~ ib_logfile1: REDO日誌文件,事務日誌文件。 ibtmp1: 臨時表空間磁盤位置,存儲臨時表 frm:存儲表的列信息 ibd:表的數據行和索引

myisam InnoDB
.frm 數據字典 .ibd 數據行和索引
.myd 數據行 .frm 單表數據字典
.myi 索引 ibdata1

表空間結構

介紹:表空間的概念源於Oracle數據庫。最初的目的是爲了可以很好的作存儲的擴容。

  共享(系統)表空間
  存儲方式
 ibdata1~ibdataN, 5.5版本默認的表空間類型.
 ibdata1共享表空間在各個版本的變化

5.5版本:

 系統相關:(全局)數據字典信息(表基本結構信息、狀態、系統參數、屬性..)、UNDO回滾日誌(記錄撤銷操做)、Double Write Buffer信息、臨時表信息、change buffer
  用戶數據:表數據行、表的索引數據

5.6版本:

 共享表空間只存儲於系統數據,把用戶數據獨立了,獨立表空間管理。
 系統相關:(全局)數據字典信息、UNDO回滾信息、Double Write信息、臨時表信息、change buffer

5.7版本:

 在5.6基礎上,把臨時表獨立出來,UNDO也能夠設定爲獨立
 系統相關:(全局)數據字典信息、UNDO回滾信息、Double Write信息、change buffer

 

8.0.11~8.0.19版本:

 在5.7的基礎上將UNDO回滾信息默認獨立,數據字典再也不集中存儲了。
 系統相關:Double Write信息、change buffer

8.0.20版本:

https://dev.mysql.com/doc/refman/5.7/en/innodb-architecture.html

在以前版本基礎上,獨立 Double Write信息 系統相關:change buffer

 

 

總結:

 對於InnoDB表來說,例如 city表 
 city.ibd
 city.frm
 ibdata1
 只是經過cp備份ibd和frm文件沒法實現,數據表的恢復

雙一標準

 innoda_file_per_table=1,1表明獨立表空間,5.6版默認模式;0表明共享表空間,5.6以前的默認模式
 5.6版以前表空間沒有獨立出來,存放在ibdata1文件中。設爲1後建立的表會在data目錄中生成表名.ibd文件,
 設置爲0後建立的表不會生成該文件,會把.ibd中的內容存放到ibdata1文件中。
 # 存儲引擎配置:
 default_storage_engine=innodb(5.6的默認引擎)
 # 配置共享表空間文件個數和大小(即ibdata1文件,該文件成爲共享表空間):
 參考:https://www.cnblogs.com/quzq/p/12833135.html
 innodb_data_file_path=ibdata1:512M:ibdata2:512M:autoextend
 該配置一般在初始化以前配好,會生成兩個文件
 # 雙一標準的其中一個(默認是1)
 innodb_flush_log_at_trx_commit=1,用於控制redo log buffer中數據寫入磁盤redo log文件的。
 值1表明什麼呢?(redo log buffer,data buffer poll, undo log buffer都是存在於mysql內存中的)
 mysql啓動後會向操做系統申請專用的內存空間,配置爲1表明在commit命令後會當即把redo log buffer
 遞交到操做系統內存中,而後由操做系統再當即寫入到磁盤的redo log文件中。
 值0表明每秒執行一次把redo log buffer遞交到操做系統內存,操做系統內存也每秒往redo log中寫入一次。
 由於是每秒一次,若是在1秒內發生大量的事務遞交,忽然宕機,會形成1秒間隔內發生的事務數據丟失
 值2表明每次commit後當即把redo log buffer數據遞交到操做系統內存,而後操做系統每秒往redo log中寫入一次
 缺點和0同樣,只不過能好一點,若是隻是mysql服務宕機的話,提交到操做系統內存的事務還不會丟失。
 補充:不管哪一個值,redo log buffer遞交到操做系統內存的日誌都會包含全部,不管該事務是否commit.
 # 雙一表中的另外一個
 sync_binlog=1   每次事務遞交都當即把二進制日誌刷寫到磁盤。
 雙一標準都是用來控制mysql內存數據刷寫到磁盤的頻率,一個用來控制redo log, 一個用來控制二進制日誌的
 二進制日誌相關參考:https://www.cnblogs.com/quzq/p/12866410.html
 # 控制mysql內存中logs到磁盤的過程
 innodb_flush_method=o_direct或fsync或o_dsync, 控制的是redo log buffer和data buffer pool,過程以下:
 默認使用的是fsync模式,建議使用o_direct模式
 #結合上兩個參數給出個建議配置以下:
 1.最高安全模式:
  innodb_flush_log_at_trx_commit=1
  innodb_flush_method=o_direct
 2.最高性能模式(安全不是特別重要場景):
  innodb_flush_log_at_trx_commit=0
  innodb_flush_method=fsync
 # 三個和redo日誌設置有關的參數:
  1.innodb_log_buffer_size=16777216, 設置redo log     buffer內存區的大小
  2.innodb_log_file_size=50331648, 設置redo log的兩個文件大小
  3.innodb_log_files_in_group=3,控制redo log的文件數,默認是0和1兩個文件
 # 髒頁刷寫策略:
 innodb_max_dirty_pages_pct=75, 75爲百分比,控制data buffer pool中髒頁數據佔比達到75%時自動觸發CKPT
 和WAL機制把data buffer pool中的信息刷寫到ibd文件中,固然日誌也是優先於數據寫入到redo log中的。
 補充:哪些場景會觸發髒頁數據寫入ibd文件(CKPT)呢?
 CSR機制。 參考:https://www.cnblogs.com/quzq/p/12839958.html
 redo文件滿了。一般redo log中的信息當髒頁數據寫回ibd後,redo log中的日誌就沒用了,能夠被覆蓋寫掉。

 

 

 

 

 

 

 

共享表空間管理

擴容共享表空間

 mysql> select @@innodb_data_file_path;
 +-------------------------+
 | @@innodb_data_file_path |
 +-------------------------+
 | ibdata1:12M:autoextend |
 +-------------------------+
 1 row in set (0.00 sec)
 mysql> select @@innodb_autoextend_increment;
 +-------------------------------+
 | @@innodb_autoextend_increment |
 +-------------------------------+
 |                            64 |
 +-------------------------------+
 1 row in set (0.00 sec)

參數用途:ibdata1文件,默認初始大小12M,不夠用會自動擴展,默認每次擴展64M擴容

① 初始化後設置共享表空間,重啓生效。

 vim /etc/my.cnf
 [mysqld]
 innodb_data_file_path=ibdata1:12M;ibdata2:64M;ibdata3:64M:autoextend

注意:ibdata1必須和當前文件時間大小一致

錯誤處理:

ibdata1設置值和當前文件實際大小不一致,重啓數據庫報錯,查看日誌文件

 tail -10 /data/3306/data/db01.err | grep ERROR
 ... ...
 [ERROR] InnoDB: The innodb_system data file './ibdata1' is of a different size 4864 pages (rounded down to MB) than the 768 pages specified in the .cnf file!
 ... ...

實際大小:4864*16K/1024=76M

my.cnf文件設置大小:768*16K/1024=12M

查看ibdata1實際大小

 [root@db01 ~]# ls -lh /data/3306/data/ibdata1 
 -rw-r----- 1 mysql mysql 76M May  6 17:11 ibdata1

② 初始化前設置共享表空間(生產建議)

5.7 中建議:設置共享表空間2-3個,大小建議1G或者4G,最後一個定製爲自動擴展。

8.0 中建議:設置1-2個就ok,大小建議1G或者4G,最後一個定製爲自動擴展。


獨立表空間

從5.6開始,默認表空間再也不使用共享表空間,替換爲獨立表空間。

 主要存儲的是用戶數據
 存儲特色爲:一個表一個ibd文件,存儲數據行和索引信息
 基本表結構元數據存儲:
 xxx.frm    
 最終結論:
       元數據            數據行+索引
 mysql表數據    =(ibdataX+frm)+ibd(段、區、頁)
         DDL             DML+DQL
 
 MySQL的存儲引擎日誌:
 Redo Log: ib_logfile0  ib_logfile1,重作日誌
 Undo Log: ibdata1 ibdata2(存儲在共享表空間中),回滾日誌
 臨時表:ibtmp1,在作join union操做產生臨時數據,用完就自動

獨立表空間設置

 -- 查看控制參數
 select @@innodb_file_per_table;
 -- 獨立表空間存儲用戶數據
 set global innodb_file_per_table=1;
 -- 共享表空間存儲用戶數據
 set global innodb_file_per_table=0;

案例背景:

 硬件及軟件環境:
 聯想服務器(IBM)
 磁盤500G 沒有raid
 centos 6.8
 mysql 5.6.33  innodb引擎 獨立表空間
 備份沒有,日誌也沒開
 
 開發用戶專用庫:
 jira(bug追蹤) 、 confluence(內部知識庫)    ------>LNMT

故障描述:

 斷電了,啓動完成後「/」 只讀
 fsck 重啓,系統成功啓動,mysql啓動不了。
 結果:confulence庫在 , jira庫不見了
 求助:
 這種狀況怎麼恢復?
 我問:
 有備份沒
 求助:
 連二進制日誌都沒有,沒有備份,沒有主從
 我說:
 沒招了,jira須要硬盤恢復了。
 求助:
 1、jira問題拉倒中關村了
 2、能不能暫時把confulence庫先打開用着
 將生產庫confulence,拷貝到1:1虛擬機上/var/lib/mysql,直接訪問時訪問不了的
 
 問:有沒有工具能直接讀取ibd
 我說:我查查,最後發現沒有

我想出一個辦法來:

 表空間遷移:
 create table xxx
 alter table  confulence.t1 discard tablespace;
 alter table confulence.t1 import tablespace;
 虛擬機測試可行。

處理問題思路:

 confulence庫中一共有107張表。
 一、建立107和和原來如出一轍的表。
 他有2016年的歷史庫,我讓他去他同時電腦上 mysqldump備份confulence庫
 mysqldump -uroot -ppassw0rd -B confulence --no-data >test.sql
 拿到你的測試庫,進行恢復
 到這步爲止,表結構有了。
 二、表空間刪除。
 select concat('alter table ',table_schema,'.'table_name,' discard tablespace;') from information_schema.tables where table_schema='confluence' into outfile '/tmp/discad.sql';
 source /tmp/discard.sql
 執行過程當中發現,有20-30個表沒法成功。主外鍵關係
 很絕望,一個表一個表分析表結構,很痛苦。
 set foreign_key_checks=0 跳過外鍵檢查。
 把有問題的表表空間也刪掉了。
 三、拷貝生產中confulence庫下的全部表的ibd文件拷貝到準備好的環境中
 select concat('alter table ',table_schema,'.'table_name,' import tablespace;') from information_schema.tables where table_schema='confluence' into outfile '/tmp/discad.sql';
 四、驗證數據
 表均可以訪問了,數據挽回到了出現問題時刻的狀態
  課後練習做業:
 案例2 : MySQL 5.7 中誤刪除了ibdata1文件,致使數據沒法啓動,如何恢復t100w,假設一共100張表,表結構沒法經過show create table 得到。
 提示:萬一是自研數據庫,怎麼辦?又沒備份,那怎麼辦?
 mysql工具包,mysqlfrm 讀取frm文件得到表結構。
 ./mysqlfrm /data/3306/data/test/t100w.frm --diagnostic
 select concat('alter table ',table_schema,'.'table_name,' discard tablespace;') from information_schema.tables where table_schema='confluence' into outfile '/tmp/discad.sql';
 source /tmp/discard.sql
 #刪除ib_logfile0
 #刪除ib_logfile1
 不要刪除ibdata1 ibdata2 ibdata3
獲取表結構

8.0以前

可使用MySQL Utilities提供的mysqlfrm用來讀取.frm文件,並從該文件中找到表定義數據,生成CREATE語句。

 cd /opt
 wget https://downloads.mysql.com/archives/get/p/30/file/mysql-utilities-1.6.5.tar.gz
 tar -xvzf mysql-utilities-1.6.5.tar.gz
 python /opt/mysql-utilities-1.6.5/setup.py build
 python /opt/mysql-utilities-1.6.5/setup.py install
 # 獲取獨立表空間的表結構
 mysqlfrm --diagnostic 表名.frm | grep -v "^#" > /tmp/db_table.sql

注意:.frm文件中沒有外鍵約束和自增加序列的信息

刪除表空間前能夠設置跳過外鍵檢查來規避問題

 set foreign_key_checks=0

8.0以後

可使用ibd2sdi離線的將ibd文件中的冗餘存儲的SDI信息提取出來,並以json的格式輸出到終端。

參考文章:英文原文 中文翻譯

  1. 把 表名.ibd 中的表結構以json的格式輸出到 dbsdi.json文件

 ibd2sdi --dump-file=dbsdi.json 表名.ibd

注意:當存在中文註釋時,解析出來的註釋多是亂碼的,並且大機率會觸發ibd2sdi的bug(中文亂碼致使json格式錯誤,好比缺乏引號)。

此時可使用vscode打開dbsdi.json,vscode會高亮json文件格式正確的部分,手動修復不正確的格式,保存。

  1. 使用jq提取json裏的數據

    CentOS 使用yum安裝

    通用命令

     ibd2sdi 表名.ibd |jq  '.[]?|.[]?|.dd_object?|({table:.name?},(.columns?|.[]?|{name:.name,type:.column_type_utf8}))' > dbsdi-jq.json

    Windows 下載可執行文件安裝

    Powershell調用jq解析json文件

     Get-Content -Path dbsdi.json |jq  '.[]?|.[]?|.dd_object?|({table:.name?},(.columns?|.[]?|{name:.name,type:.column_type_utf8}))' > dbsdi-jq.json

撤銷表空間

存儲撤消日誌,用來回滾事務。

撤銷表空間查看配置參數
 -- 打開獨立undo模式,並設置undo的個數,建議3-5個,8.0棄用
 SELECT @@innodb_undo_tablespaces;
 -- undo日誌的大小,默認1G
 SELECT @@innodb_max_undo_log_size;
 -- 開啓undo自動回收的機制(undo_purge)
 SELECT @@innodb_undo_log_truncate;
 -- 觸發自動回收的條件,單位是檢測次數
 SELECT @@innodb_purge_rseg_truncate_frequency;
 -- undo文件存儲路徑
 SELECT @@innodb_undo_directory;
撤銷表空間配置

5.7版本

默認存儲在共享表空間中(ibdataN),生產中必須手工獨立出來,不然影響高併發效率。

只能在初始化時配置undo個數,而且是固定的。

 # 1.建立目錄
 [root@db01 ~]# mkdir /data/3357/{data,etc,socket,log,pid,undologs} -pv
 [root@db01 ~]# chown -R mysql. /data/*
 ​
 # 2.添加參數
 [root@db01 ~]# vim /data/3357/my.cnf
 [mysqld]
 innodb_undo_tablespaces=3           
 innodb_max_undo_log_size=128M
 innodb_undo_log_truncate=ON
 innodb_purge_rseg_truncate_frequency=32
 innodb_undo_directory=/data/3357/undologs
 # 3.初始化數據庫
 [root@db01 ~]# /usr/local/mysql57/bin/mysqld --defaults-file=/data/3357/my.cnf  
--initialize-insecure --user=mysql --basedir=/usr/local/mysql57 --datadir=/data/3357/data # 4.啓動數據庫 [root@db01 ~]# /etc/init.d/mysqld start # 5.查看結果 [root@db01 ~]# ll /data/3357/undologs/ -rw-r----- 1 mysql mysql 10485760 May 11 15:39 /data/3357/undologs/undo001 -rw-r----- 1 mysql mysql 10485760 May 11 15:39 /data/3357/undologs/undo002 -rw-r----- 1 mysql mysql 10485760 May 11 15:39 /data/3357/undologs/undo003

8.0版本

默認就是獨立的(undo_001-undo_002),能夠隨時配置,innodb_undo_tablespaces選項已過期。

 -- 查詢全部表空間文件
 SELECT TABLESPACE_NAME, FILE_NAME FROM INFORMATION_SCHEMA.FILES;
 
 -- 查詢undo表空間
 SELECT TABLESPACE_NAME, FILE_NAME FROM INFORMATION_SCHEMA.FILES WHERE FILE_TYPE LIKE 'UNDO LOG';
 
 -- 添加undo表空間
 CREATE UNDO TABLESPACE tablespace_name ADD DATAFILE 'file_name.ibu';
 
 -- 刪除undo表空間
 -- 必須爲空,先標記爲非活動狀態,再刪除
 ALTER UNDO TABLESPACE tablespace_name SET INACTIVE;
 DROP UNDO TABLESPACE tablespace_name;
 
 -- 監視undo表空間的狀態
 SELECT NAME, STATE FROM INFORMATION_SCHEMA.INNODB_TABLESPACES WHERE NAME LIKE 'tablespace_name';

undo 表空間管理

查看undo的配置參數

 SELECT @@innodb_undo_tablespaces;  ---->3-5個   #打開獨立undo模式,並設置undo的個數。
 SELECT @@innodb_max_undo_log_size;               #undo日誌的大小,默認1G。
 SELECT @@innodb_undo_log_truncate;               #開啓undo自動回收的機制(undo_purge)。
 SELECT @@innodb_purge_rseg_truncate_frequency;   #觸發自動回收的條件,單位是檢測次數。

#若是進行undo獨立存儲到其餘文件系統

 a. 關閉數據庫:
 [root@db01 data]# systemctl start mysqld3357
 b.設定路徑參數 
 innodb_undo_directory=/data/3357/undologs 
 c. 建立目錄,並拷貝文件
 [root@db01 data]# systemctl stop mysqld3357
 mkdir -p  /data/3357/undologs 
 chown -R mysql. /data/* 
 cp -a /data/3357/data/undo* /data/3357/undologs 

注: 8.0 undo表空間與5.7的區別

參考

https://dev.mysql.com/doc/refman/8.0/en/innodb-undo-tablespaces.html

臨時表空間

5.7版本

臨時表空間(ibtmp1)用於存儲臨時表。建議數據初始化以前設定好,通常2-3個,大小512M-1G。

臨時表空間查看配置參數

 mysql> select @@innodb_temp_data_file_path;
 +------------------------------+
 | @@innodb_temp_data_file_path |
 +------------------------------+
 | ibtmp1:12M:autoextend       |
 +------------------------------+

配置文件設置,重啓生效

 [root@db01 ~]# vim /etc/my.cnf
 [mysqld]
 innodb_temp_data_file_path=ibtmp1:12M;ibtmp2:128M:autoextend:max:500M

8.0版本

分爲會話臨時表空間和全局臨時表空間

  • 會話臨時表空間(temp_N.ibt)用於存儲臨時表。

    位置參數

     mysql> select @@innodb_temp_tablespaces_dir;
     +-------------------------------+
     | @@innodb_temp_tablespaces_dir |
     +-------------------------------+
     | ./#innodb_temp/               |
     +-------------------------------+
  • 全局臨時表空間(ibtmp1)用於存儲對用戶建立的臨時表進行更改的回滾段。

    配置同5.7版本的臨時表空間


重作日誌(Redo Log)

Redo Log 記錄內存數據頁的變化(數據頁的變化信息+數據頁當時的LSN號)。實現「前滾」的功能。

存儲在數據路徑下(ib_logfile0,ib_logfile1,...),輪序覆蓋記錄日誌。

刷新策略:commit提交後,刷新當前事務的 redo buffer 到磁盤,還會順便將一部分 redo buffer 中沒有提交的事務日誌也刷新到磁盤。

WAL(write ahead log):保證 Redo Log 優先於數據寫入磁盤。


查詢配置參數

 mysql> show variables like '%innodb_log_file%';
 +---------------------------+----------+
 | Variable_name             | Value   |
 +---------------------------+----------+
 | innodb_log_file_size     | 50331648 |
 | innodb_log_files_in_group | 2       |
 +---------------------------+----------+

設置

生產建議: 設置3-5組,512M-4G

配置文件添加參數,重啓生效

 [root@db01 ~]# vim /etc/my.cnf 
 [mysqld]
 innodb_log_file_size=100M
 innodb_log_files_in_group=3

回滾日誌(undo log)

Undo Log 是撤消日誌的集合,提供快照技術,保存事務修改以前的數據狀態,保證了MVCC,隔離性,mysqldump的熱備。

  • 在rolback時,將數據恢復到修改以前的狀態。

  • 在實現CSR時,回滾到redo當中記錄的未提交的時候。

5.7版本,存儲在共享表空間中 (ibdata1~ibdataN

8.0版本

對常規表執行操做的事務的撤消日誌存儲在撤消表空間中(undo_001-undo_002)。 對臨時表執行操做的事務的撤消日誌存儲在全局臨時表空間中(ibtmp1)。

每一個撤消表空間和全局臨時表空間分別支持最多128個回滾段。

配置回滾段的數量

 select @@innodb_rollback_segments;

雙寫緩衝區 Double Write Buffer(DWB)

雙寫緩衝區是一個存儲區域,InnoDB先將從頁面緩衝池中刷新的頁面寫入雙寫緩衝區,而後再將頁面寫入InnoDB數據文件中。

若是在頁面寫入過程當中,發生操做系統,存儲子系統或mysqld進程的意外退出,則InnoDB能夠在崩潰恢復期間從doublewrite緩衝區中找到頁面的良好副本。

8.0.19前默認位於ibdataN中,8.0.20後就獨立出來位於#*.dblwr


預熱文件(ib_buffer_pool)

用來緩衝和緩存「熱」(常常查詢或修改)數據頁,減小物理IO。MySQL 5.7默認啓用。

當關閉數據庫的時候,緩衝和緩存會失效。5.7版本後,MySQL正常關閉時,會將內存的熱數據存放(流方式)至ib_buffer_pool。下次重啓直接讀取ib_buffer_pool加載到內存中。

查詢配置參數

指定在關閉MySQL服務器時是否記錄InnoDB 緩衝池中緩存的頁面 ,以縮短下次重啓時的預熱過程。innodb_buffer_pool_dump_pct 選項定義要轉儲的最近使用的緩衝池頁面的百分比。

 select @@innodb_buffer_pool_dump_at_shutdown;
 select @@innodb_buffer_pool_load_at_startup;

InnoDB內存結構

緩衝池 InnoDB BUFFER POOL(IBP)

緩衝池主要用來緩衝、緩存MySQL的數據頁和索引頁,還有AHI、Change buffer。MySQL中最大的、最重要的內存區域。

配置InnoDB緩衝池大小

 -- 查看緩存池大小,默認128M
 mysql> select @@innodb_buffer_pool_size;
 +---------------------------+
 | @@innodb_buffer_pool_size |
 +---------------------------+
 |                 134217728 |
 +---------------------------+

生產建議:物理內存的:50-80%

全局設置: 從新鏈接mysql生效。

 set global innodb_buffer_pool_size=268435456;

永久設置:配置文件添加參數,重啓mysql生效

 vim /etc/my.cnf 
 [mysqld]
 innodb_buffer_pool_size=256M

配置多個緩衝池實例

 -- 查詢緩衝池實例數量,默認1,最大爲64
 mysql> select @@innodb_buffer_pool_instances;
 +--------------------------------+
 | @@innodb_buffer_pool_instances |
 +--------------------------------+
 |                              1 |
 +--------------------------------+

注意:僅當您將innodb_buffer_pool_size大小設置爲1GB或更大時,此選項才生效,是全部緩衝池實例大小之和。

爲了得到最佳效率,請組合 innodb_buffer_pool_instancesinnodb_buffer_pool_size使得每一個緩衝池實例是至少爲1GB。


日誌緩衝區 InnoDB LOG BUFFER (ILB)

用於保存要寫入磁盤上的日誌文件(Redo Log)的數據。

查詢配置參數

 select @@innodb_log_buffer_size;

默認大小:16M 生產建議:innodb_log_file_size的1-N倍 永久設置:配置文件添加參數,重啓mysql生效

 vim /etc/my.cnf
 [mysqld]
 innodb_log_buffer_size=33554432

利用獨立表空間進行快速數據遷移

 源端:3306/test/t100w  -----> 目標端:3307/test/t100w
 鎖定源端t100w表
 mysql> flush tables  test.t100w with read lock ;
 mysql> show create table test.t100w;
 CREATE TABLE t100w (
  id int(11) DEFAULT NULL,
  num int(11) DEFAULT NULL,
  k1 char(2) DEFAULT NULL,
  k2 char(4) DEFAULT NULL,
  dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
 目標端建立test庫和t100w空表
 [root@db01 ~]# systemctl start mysqld3307
 [root@db01 ~]# mysql -S /tmp/mysql3307.sock
 mysql> create database test charset=utf8mb4;
 CREATE TABLE t100w (
   id int(11) DEFAULT NULL,
   num int(11) DEFAULT NULL,
   k1 char(2) DEFAULT NULL,
   k2 char(4) DEFAULT NULL,
   dt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
 單獨刪除空的表空間文件
 mysql> alter table test.t100w discard tablespace;
 拷貝源端ibd文件到目標端目錄,並設置權限
 [root@db01 test]# cp -a /data/3306/data/test/t100w.ibd     /data/3307/data/test/
 [root@db01 test]# chown -R mysql.mysql /data/*
 導入表空間
 mysql> alter table test.t100w import tablespace;
 mysql> select count(*) from test.t100w;
 +----------+
 | count(*) |
 +----------+
 |  1000000 |
 解鎖源端數據表
 mysql> unlock tables;

事務的工做流程原理(ACID特性如何保證)

 #重作日誌 (redo log)
 ib_logfile0~N   48M   , 輪詢使用
 # 日誌緩衝區
 redo log buffer : redo內存區域
 # 表空間數據文件
 ibd : 存儲數據行和索引
 # 數據緩衝區
 InnoDB buffer pool : 緩衝區池,數據和索引的緩衝
 # 日誌序列號
 LSN
 磁盤數據頁(ibd文件的page),redo log文件(ib_logfile),Innodb_buffer_pool中的數據頁,redo buffer
 MySQL 每次數據庫啓動,都會比較磁盤數據頁和redolog的LSN,必需要求二者LSN一致數據庫才能正常啓動
 #WAL : Write Ahead Log
 Redo日誌優先於數據頁寫到磁盤。
 # 髒頁: Dirty Page
 內存髒頁,內存中發生了修改,沒寫入到磁盤以前,咱們把內存頁稱之爲髒頁.
 # CheckPoint
 CKPT:檢查點,就是將髒頁刷新到磁盤的動做
 #DB_TRX_ID(6字節) 事務ID號
 InnoDB會爲每個事務生成一個事務號,伴隨着整個事務生命週期.
 #DB_ROLL_PTR(7字節) 回滾指針
 rollback 時,會使用 undo 日誌回滾已修改的數據。DB_ROLL_PTR指向了這次事務的回滾位置點,用來找到undo日誌信息。
  事務工做流程原理
 事務舉例:
 begin;
 update t1 set A=2 where A=1;
 commit;
 #redo log 重作日誌如何應用
 1.用戶發起update事務語句,將磁盤數據頁(page100,A=1,LSN=1000)加載到內存(buffer_pool)緩衝區。
 2.在內存中發生數據頁修改(A=1改爲A=2),造成髒頁,更改中數據頁的變化,記錄到redo buffer中,加入1000個字節日誌。LSN=1000+1000=2000。
 3. 當commit語句執行時,基於WAL機制,等到redo buffer中的日誌徹底落盤到ib_logfileN中,commit正式完成。
 4. ib_logfileN中記錄了一條日誌。內容:page100數據頁變化+LSN=2000。
    ##情景: 當此時,redo落地了,數據頁沒有落地,宕機了。
 5. MySQL CR(自動故障恢復)工做模式,啓動數據庫時,自動檢查redo的LSN和數據頁LSN。
 6. 若是發現redoLSN數據頁的LSN,加載原始數據頁+變化redo指定內存。使用redo重構髒頁(前滾)。
 7. 若是確認這次事務已經提交(commit標籤),當即觸發CKPT動做,將髒頁刷寫到磁盤上。
  MySQL有一種機制,批量刷寫redo的機制。會在A事務commit時,順便將redo buffer中的未提交的redo日誌也一併刷到磁盤。
  爲了區分不一樣狀態的redo,日誌記錄時,會標記是否COMMIT。
 redo保證了ACID哪些特性?
 主要是D的特性,另外A、C也有間接關聯。
 undo log 回滾日誌如何應用?
 1. 事務發生數據頁修改以前,會申請一個undo事務操做,保存事務回滾日誌(逆向操做的邏輯日誌)。
 2. undo寫完以後,事務修改數據頁頭部(會記錄DB_TRX_ID+DB_ROLL_PTR),這個信息也會被記錄的redo。
 情景1:
 當執行rollback命令時。根據數據頁的DB_TRX_ID+DB_ROLL_PTR信息,找到undo日誌,進行回滾。
 情景2:
 begin;
 update t1 set A=2 where A=1;
 宕機。
 假設: undo 有 , redo沒有
 啓動數據庫時,檢查redo和數據頁的LSN號碼。發現是一致的。
 因此不須要進行redo的前滾,此時也不須要回滾。undo信息直接被標記爲可覆蓋狀態。
 假設:undo 有,redo也有(沒有commit標籤。)
 3. MySQL CR(自動故障恢復)工做模式,啓動數據庫時,自動檢查redo的LSN和數據頁LSN。
 4. 若是發現redoLSN數據頁的LSN ,加載原始數據頁+變化redo指定內存。使用redo重構髒頁(前滾)。
 5. 若是確認這次事務沒有commit標記,當即觸發回滾操做,根據DB_TRX_ID+DB_ROLL_PTR信息,找到und回滾日誌,實現回滾。
 以上流程被稱之爲InnoDB的核心特性:自動故障恢復(Crash Recovery)。先前滾再回滾,先應用redo再應用undo。
  undo在ACID中保證了啥?
 主要保證事務的A的特性,同時C和I的特性也有關係。
 事務中的C特性怎麼保證?
 InnoDB crash recovery:數據庫意外宕機時刻,經過redo前滾+undo回滾保證數據的最終一致。
 InnoDB doublewrite buffer: 默認存儲在ibdataN中。解決數據頁寫入不完整
 mysqld process crash in the middle of a page write, InnoDB can find a good copy of the page from the doublewrite buffer during crash recovery.
 DWB一共2M。分兩次,每次1M寫入

undo log 回滾日誌

ib_buffer_pool 預熱文件

做用:

 緩衝和緩存,用來作「熱」(常常查詢或修改)數據頁,減小物理IO。
 當關閉數據庫的時候,緩衝和緩存會失效。
 5.7版本中,MySQL正常關閉時,會將內存的熱數據存放(流方式)至ib_buffer_pool。下次重啓直接讀取ib_buffer_pool加載到內存中。
 mysql> select @@innodb_buffer_pool_dump_at_shutdown;
 mysql> select @@innodb_buffer_pool_load_at_startup;

Double Write Buffer(DWB) 雙寫緩衝區 (8.0.19以前 默認在ibdataN中,8.0.20之後能夠獨立了。)

做用: MySQL,最小IO單元page(16KB),OS中最小的IO單元是block(4KB) 爲了防止出現如下問題:

 mysqld process exit in the middle of a page write, InnoDB can find a good copy of the page from the doublewrite buffer during crash recovery.
 #mysqld進程退出在頁面寫入中間過程當中,InnoDB能夠在崩潰恢復期間從雙寫緩衝區找到一個好的頁面副本。

 

 

 

內存結構

InnoDB BUFFER POOL(IBP)(介紹:內存緩衝區池,最大\最重要)

做用:

 用來緩衝、緩存,MySQL的數據頁和索引頁。MySQL中最大的、最重要的內存區域。

管理:

 查詢
 mysql> select @@innodb_buffer_pool_size;
 默認大小: 128M
 生產建議: 物理內存的:50-80%。
 OOM,全稱「Out Of Memory」,
 在線設置(256M):
 mysql> select 256*1024*1024;
 +---------------+
 | 256*1024*1024 |
 +---------------+
 |     268435456 |
 +---------------+
 mysql> set global innodb_buffer_pool_size=268435456;
 從新登陸mysql生效。
 永久設置:
 vim /etc/my.cnf
 #添加參數
 innodb_buffer_pool_size=256M
 重啓生效

InnoDB LOG BUFFER (ILB)

做用: 用來緩衝 redo log日誌信息。 管理 :

 查詢: 
 mysql> select @@innodb_log_buffer_size;
 默認大小:16M
 生產建議:和innodb_log_file_size有關,1-N倍
 設置方式 :
 vim /etc/my.cnf
 innodb_log_buffer_size=33554432
 重啓生效:
 [root@db01 data]# /etc/init.d/mysqld restart  

InnoDB核心特性--事務支持

介紹

 事務:Transaction (交易)。 伴隨着交易類的業務出現的概念(工做模式)
 交易?
 物換物,等價交換。
 貨幣換物,等價交換。
 虛擬貨幣換物(虛擬物品),等價交換。
 現實生活中怎麼保證交易「和諧」 ,法律、道德等規則約束。
 數據庫中爲了保證線上交易的「和諧」,加入了「事務」工做機制。

事務ACID特性(面試題記憶)

 A: 原子性   (atomicity)
 一個事物是一個完整總體,不可再分。
 一個事務生命週期中的DML語句,要麼全成功要麼全失敗,不能夠出現中間狀態。
 begin;
 DML1;
 DML2;
 DML3;
 commit;
 C:一致性   (consistency)
 事務發生前,中,後,數據都最終保持一致。
 只要提交成功的事務,數據保證最終一致。
 CR + double write
 I:隔離性   (isolation)
 事務操做數據行的時候,不會受到其餘時候的影響。
 D: 持久性   (durability)
 一但事務提交,保證永久生效,落盤。

事務的生命週期管理

標準(顯示)的事務控制語句

 # 開啓事務
 begin;
 # 提交事務
 commit;
 # 回滾事務
 rollback;

注意:事務生命週期中,只能使用DML語句(select、update、delete、insert)

開始事務流程

 一、檢查autocommit是否爲關閉狀態
 select @@autocommit;
 或者:
 show variables like 'autocommit';
 二、開啓事務,並結束事務
 begin
 delete from student where name='alexsb';
 update student set name='alexsb' where name='alex';
 rollback;
 begin
 delete from student where name='alexsb';
 update student set name='alexsb' where name='alex';
 commit;

事務的生命週期演示:

 mysql> use world
 mysql> begin;
 mysql> delete from city where id=1;
 mysql> update city set countrycode='CHN' where id=2;
 mysql> commit;
 mysql> begin;
 mysql> select * from city limit 10;
 mysql> update city set countrycode='AFG' where id=2;
 mysql> delete from city where id=3;
 mysql> rollback;
 MySQL的自動提交機制(autocommit)
 參數: 
 mysql> select @@autocommit;
 +--------------+
 | @@autocommit |
 +--------------+
 |            1 |
 +--------------+

做用:

 在沒有顯示的使用begin語句的時候,執行DML,會在DML前自動添加begin,並在DML執行後自動添加commit。

建議: 頻繁事務業務場景中,關閉autocommit,或者每次事務執行時都是顯示的begin和commit;

關閉autocommit方法

臨時
 mysql> set global autocommit=0;
 退出會話,從新鏈接配置生效。
永久
 [root@db01 ~]# vim /etc/my.cnf
 autocommit=0
 重啓生效。
 不進行begin操做,逐條提交。

隱式提交和回滾

 begin;
 DML1;
 DML2;
 DML3;
 commit;

隱式提交狀況

 begin 
 a
 b
 SET AUTOCOMMIT = 1
 致使提交的非事務語句:
 DDL語句: (ALTER、CREATE 和 DROP)
 DCL語句: (GRANT、REVOKE 和 SET PASSWORD)
 鎖定語句:(LOCK TABLES 和 UNLOCK TABLES)
 致使隱式提交的語句示例:
 TRUNCATE TABLE
 LOAD DATA INFILE
 SELECT FOR UPDATE

隱式回滾

 會話窗口被關閉。
 數據庫關閉 。
 出現事務衝突(死鎖)。

事務的隔離級別 (面試必問項目)

做用

 實現事務工做期間的「讀」的隔離
 讀? ----》 數據頁的讀
 級別類型
 mysql> select @@transaction_isolation;

RU : READ-UNCOMMITTED 讀未提交

 優勢:能夠讀取到事務未提交的數據。事務併發度最高。
 缺點:隔離性差,會出現髒讀(當前內存讀),不可重複讀,幻讀問題

RC : READ-COMMITTED 讀已提交(能夠用)

  優勢:防止髒讀,防止不可重複讀
  缺點:不可重複讀,幻讀

RR : REPEATABLE-READ 可重複讀(默認)

  優勢:防止髒讀,防止不可重複讀
  缺點:事務併發度通常,幻讀問題

SE : SERIALIZABLE 串行讀

 優勢:隔離性最好
 缺點:事務沒有併發

默認級別是RR級別,互聯網業務的大部分場景RC級別。

 RC  能夠減輕GAP+NextLock鎖的問題,通常在爲了讀一致性會在正常select後添加for update語句.可是,請記住執行完必定要commit不然容易出現所等待比較嚴重.
 RC 能夠減輕GAP+NextLock鎖的問題,通常在爲了讀一致性會在正常select後添加for update語句.可是,請記住執行完必定要commit不然容易出現所等待比較嚴重.
 例如:
 [world]>select * from city where id=999 for update;
 [world]>commit;
 RR 利用的是undo的快照技術+GAP(間隙鎖)+NextLock(下鍵鎖)

隔離級別參數

 select @@transaction_isolation;
 set global transaction_isolation='READ-UNCOMMITTED';
 set global transaction_isolation='READ-COMMITTED';
 set global transaction_isolation='REPEATABLE-READ';
 set global transaction_isolation='SERIALIZABLE';
 vim /etc/my.cnf
 [mysqld]
 transaction_isolation='READ-COMMITTED';

問題現象演示

 -- 建立測試庫
 create database test;
 -- 建立測試表
 create table test.t1 (
 id int not null primary key auto_increment ,
 a  int not null ,
 b  varchar(20) not null, 
 c  varchar(20) not null 
 )charset=utf8mb4 engine=innodb;
 ​
 begin;
 insert into test.t1(a,b,c) 
 values
 (1,'a','aa'),
 (2,'c','ab'),
 (3,'d','ae'),
 (4,'e','ag'),
 (5,'f','at');
 commit;
 -- 關閉自動提交
 set global autocommit=0;
 -- 打開兩個會話窗口:
 -- sessionA: 
 -- sessionB: 

InnoDB 事務的ACID如何保證

一些概念

 redo log ---> 重作日誌 ib_logfile0~1   50M   , 輪詢使用
 redo log buffer ---> redo內存區域
 ibd     ----> 存儲 數據行和索引
 buffer pool --->緩衝區池,數據和索引的緩衝
 LSN : 日誌序列號
 磁盤數據頁,redo文件,buffer pool,redo buffer
 MySQL 每次數據庫啓動,都會比較磁盤數據頁和redolog的LSN,必需要求二者LSN一致數據庫才能正常啓動
 WAL : write ahead log 日誌優先寫的方式實現持久化
 髒頁: 內存髒頁,內存中發生了修改,沒寫入到磁盤以前,咱們把內存頁稱之爲髒頁.
 CKPT:Checkpoint,檢查點,就是將髒頁刷寫到磁盤的動做
 TXID: 事務號,InnoDB會爲每個事務生成一個事務號,伴隨着整個事務.

髒讀

髒讀又稱無效數據的讀出,當前內存讀,能夠讀取到別人未提交的數據。

例如:事務T1修改某一值,未提交,可是事務T2卻能讀取該值,此後T1由於某種緣由撤銷對該值的修改,這就致使了T2所讀取到的數據是無效的。注意,髒讀通常是針對於update操做。

 -- RU級別下不可重讀現象演示:
 -- 第一步:設置隔離級別,從新鏈接數據庫
 mysql> set global transaction_isolation='READ-UNCOMMITTED';
 mysql> exit
 -- 第二步:檢查隔離級別
 -- sessionA: 
 mysql> select @@transaction_isolation;
 +-------------------------+
 | @@transaction_isolation |
 +-------------------------+
 | READ-UNCOMMITTED        |
 +-------------------------+
 -- sessionB: 
 mysql> select @@transaction_isolation;
 +-------------------------+
 | @@transaction_isolation |
 +-------------------------+
 | READ-UNCOMMITTED        |
 +-------------------------+
 -- 第三步:開啓事務
 -- sessionA: 
 mysql> begin;
 -- sessionB: 
 mysql> begin;
 -- 第四步:查看當前表數據
 -- sessionA: 
 mysql> select * from test.t1 where id=2;
 +----+---+---+----+
 | id | a | b | c  |
 +----+---+---+----+
 |  2 | 2 | c | ab |
 +----+---+---+----+
 -- sessionB: 
 mysql> select * from test.t1 where id=2;
 +----+---+---+----+
 | id | a | b | c  |
 +----+---+---+----+
 |  2 | 2 | c | ab |
 +----+---+---+----+
 -- 第五步:
 -- sessionA: 執行DML語句
 mysql> update test.t1 set a=8 where id=2;
 -- 第六步:
 -- sessionB:查看當前表數據發現數據變化,髒讀
 mysql> select * from test.t1 where id=2;
 +----+---+---+----+
 | id | a | b | c  |
 +----+---+---+----+
 |  2 | 8 | c | ab |
 +----+---+---+----+
 -- 第七步:
 -- sessionA: 回滾
 mysql> rollback;
 -- 第八步:
 -- sessionB:查看當前表數據發現數據變化,不可重複讀
 mysql> select * from test.t1 where id=2;
 +----+---+---+----+
 | id | a | b | c  |
 +----+---+---+----+
 |  2 | 2 | c | ab |
 +----+---+---+----+

 

 

不可重複讀

不可重複讀,指一個事務範圍內兩個相同的查詢卻返回了不一樣數據。

這是因爲查詢時系統中其餘事務修改的提交而引發的。好比事務T1讀取某一數據,事務T2讀取並修改了該數據,T1爲了對讀取值進行檢驗而再次讀取該數據,便獲得了不一樣的結果。

 -- RC級別下不可重讀現象演示:
 
-- 第一步:設置隔離級別,從新鏈接數據庫
 mysql> set global transaction_isolation='READ-COMMITTED';
 mysql> exit
 -- 第二步:檢查隔離級別
 -- sessionA: 
 mysql> select @@transaction_isolation;
 +-------------------------+
 | @@transaction_isolation |
 +-------------------------+
 | READ-COMMITTED          |
 +-------------------------+
 -- sessionB: 
 mysql> select @@transaction_isolation;
 +-------------------------+
 | @@transaction_isolation |
 +-------------------------+
 | READ-COMMITTED          |
 +-------------------------+
 -- 第三步:開啓事務
 -- sessionA: 
 mysql> begin;
 -- sessionB: 
 mysql> begin;
 -- 第四步:查看當前表數據
 -- sessionA: 
 mysql> select * from test.t1 where id=1;
 +----+---+---+----+
 | id | a | b | c  |
 +----+---+---+----+
 |  1 | 1 | a | aa |
 +----+---+---+----+
 -- sessionB: 
 mysql> select * from test.t1 where id=1;
 +----+---+---+----+
 | id | a | b | c  |
 +----+---+---+----+
 |  1 | 1 | a | aa |
 +----+---+---+----+
 -- 第五步:
 -- sessionA: 執行DML語句並提交事務
 mysql> update test.t1 set a=6 where id=1;
 mysql> commit;
 -- 第六步:
 -- sessionB:查看當前表數據發現數據變化
 mysql> select * from test.t1 where id=1;
 +----+---+---+----+
 | id | a | b | c  |
 +----+---+---+----+
 |  1 | 6 | a | aa |
 +----+---+---+----+

幻讀

幻讀 ,指同一查詢在不一樣時間產生不一樣的行集。

例如:第一個事務對一個表中的數據進行了修改,好比這種修改涉及到表中的「所有數據行」。同時,第二個事務也修改這個表中的數據,這種修改是向表中插入「一行新數據」。那麼,就會發生操做第一個事務的用戶發現表中還存在沒有修改的數據行,就好象發生了幻覺同樣。

通常解決幻讀的方法是增長範圍鎖RangeS,鎖定檢索範圍爲只讀,這樣就避免了幻讀。

 
-- RC級別下幻讀現象演示:
 -- 第一步:設置隔離級別,從新鏈接數據庫
 mysql> set global transaction_isolation='READ-COMMITTED';
 mysql> exit
 -- 第二步:檢查隔離級別
 -- sessionA: 
 mysql> select @@transaction_isolation;
 +-------------------------+
 | @@transaction_isolation |
 +-------------------------+
 | READ-COMMITTED          |
 +-------------------------+
 -- sessionB: 
 mysql> select @@transaction_isolation;
 +-------------------------+
 | @@transaction_isolation |
 +-------------------------+
 | READ-COMMITTED          |
 +-------------------------+
 -- 第三步:開啓事務
 -- sessionA: 
 mysql> begin;
 -- sessionB: 
 mysql> begin;
 -- 第四步:查看當前表數據
 -- sessionA: 
 mysql> select * from test.t1;
 +----+---+---+----+
 | id | a | b | c  |
 +----+---+---+----+
 |  1 | 6 | a | aa |
 |  2 | 2 | c | ab |
 |  3 | 3 | d | ae |
 |  4 | 4 | e | ag |
 |  5 | 5 | f | at |
 +----+---+---+----+
 -- sessionB: 
 mysql> select * from test.t1;
 +----+---+---+----+
 | id | a | b | c  |
 +----+---+---+----+
 |  1 | 6 | a | aa |
 |  2 | 2 | c | ab |
 |  3 | 3 | d | ae |
 |  4 | 4 | e | ag |
 |  5 | 5 | f | at |
 +----+---+---+----+
 -- 第五步:
 -- sessionA:執行DML語句,所有數據行修改
 mysql> update test.t1 set a=10 where a<10;
 -- 第六步:
 -- sessionB:執行DML語句,插入一行數據,提交事務
 mysql> insert into test.t1(a,b,c) values (1,'z','az');
 mysql> commit;
 -- 第七步: 
 -- sessionA:提交事務
 mysql> commit;
 -- 第八步:
 -- sessionA:查看當前表數據,好像發生了幻覺
 mysql> select * from test.t1;
 +----+----+---+----+
 | id | a  | b | c  |
 +----+----+---+----+
 |  1 | 10 | a | aa |
 |  2 | 10 | c | ab |
 |  3 | 10 | d | ae |
 |  4 | 10 | e | ag |
 |  5 | 10 | f | at |
 |  6 |  1 | z | az |
 +----+----+---+----+

存儲引擎進階

名詞介紹

重作日誌(redo log)

 磁盤  ib_logfile0~N   innodb_log_file_size
                      innodb_log_files_in_group
 內存  innodb_log_buffer   innodb_log_buffer_size
 表數據     
 磁盤  獨立表空間    
 xxxibd   
 segment  
 extents  
 pages
 內存   innodb_buffer_pool     innodb_buffer_pool_size
 日誌序列號LSN   
 一個順序遞增的數字
 記錄數據頁變化的版本
 redo日誌的變化量(字節) 
 哪些對象有LSN
 redo buffer  Log  sequence number 180973874
 redo log     Log  flushed  up to  180973874
 數據頁        Last  checkpoint at 180973874
 查詢方式
 show  engine innodb  status \G
 write ahead log(WAL)
 日誌先行
 commit提交事務時,保證日誌先寫磁盤,數據後寫
 髒頁:Dirty Page       在內存中修改的數據頁,沒寫到磁盤的叫作髒頁
 檢查點:CheckPoint     將髒頁刷新到磁盤的動做
 DB_ROLL_TR      回滾指針   存儲在數據頁頭部   

重作日誌 (redo log)

 ib_logfile0~N   48M   , 輪詢使用

日誌緩衝區

 redo log buffer : redo內存區域

表空間數據文件

 ibd: 存儲數據行和索引 

數據緩衝區

 InnoDB buffer pool : 緩衝區池,數據和索引的緩衝

日誌序列號

LSN

 磁盤數據頁(ibd文件的page),redo log文件(ib_logfile),Innodb_buffer_pool中的數據頁,redo buffer
 MySQL 每次數據庫啓動,都會比較磁盤數據頁和redolog的LSN,必需要求二者LSN一致數據庫才能正常啓動
 #WAL : Write Ahead Log
 Redo日誌優先於數據頁寫到磁盤。

髒頁: Dirty Page

內存髒頁,內存中發生了修改,沒寫入到磁盤以前,咱們把內存頁稱之爲髒頁.

CheckPoint

 CKPT:檢查點,就是將髒頁刷新到磁盤的動做
 #DB_TRX_ID(6字節) 事務ID號
 InnoDB會爲每個事務生成一個事務號,伴隨着整個事務生命週期.
 #DB_ROLL_PTR(7字節) 回滾指針
 rollback 時,會使用 undo 日誌回滾已修改的數據。DB_ROLL_PTR指向了這次事務的回滾位置點,用來找到undo日誌信息。
  事務工做流程原理
 事務舉例:
 begin;
 update t1 set A=2 where A=1;
 commit;
 #redo log 重作日誌如何應用
 用戶發起update事務語句,將磁盤數據頁(page100,A=1,LSN=1000)加載到內存(buffer_pool)緩衝區。
 在內存中發生數據頁修改(A=1改爲A=2),造成髒頁,更改中數據頁的變化,記錄到redo buffer中,加入1000個字節日誌。LSN=1000+1000=2000。
 當commit語句執行時,基於WAL機制,等到redo buffer中的日誌徹底落盤到ib_logfileN中,commit正式完成。
 ib_logfileN中記錄了一條日誌。內容:page100數據頁變化+LSN=2000。
 ##情景: 當此時,redo落地了,數據頁沒有落地,宕機了。
 MySQL CR(自動故障恢復)工做模式,啓動數據庫時,自動檢查redo的LSN和數據頁LSN。
 若是發現redoLSN>數據頁的LSN ,加載原始數據頁+變化redo指定內存。使用redo重構髒頁(前滾)。
 若是確認這次事務已經提交(commit標籤),當即觸發CKPT動做,將髒頁刷寫到磁盤上。

補充:

 MySQL有一種機制,批量刷寫redo的機制。會在A事務commit時,順便將redo buffer中的未提交的redo日誌也一併刷到磁盤。
 爲了區分不一樣狀態的redo,日誌記錄時,會標記是否COMMIT。

redo保證了ACID哪些特性

主要是D的特性,另外A、C也有間接關聯。

undo log 回滾日誌應用

 1.事務發生數據頁修改以前,會申請一個undo事務操做,保存事務回滾日誌(逆向操做的邏輯日誌)。
 2.undo寫完以後,事務修改數據頁頭部(會記錄DB_TRX_ID+DB_ROLL_PTR),這個信息也會被記錄的redo。
 情景1:
 當執行rollback命令時。根據數據頁的DB_TRX_ID+DB_ROLL_PTR信息,找到undo日誌,進行回滾。
 情景2:
 begin;
 update t1 set A=2 where A=1;
 宕機。
 假設: undo 有 , redo沒有
 啓動數據庫時,檢查redo和數據頁的LSN號碼。發現是一致的。
 因此不須要進行redo的前滾,此時也不須要回滾。undo信息直接被標記爲可覆蓋狀態。
 假設:undo 有,redo也有(沒有commit標籤。)
 3.MySQL CR(自動故障恢復)工做模式,啓動數據庫時,自動檢查redo的LSN和數據頁LSN。
 4.若是發現redoLSN>數據頁的LSN ,加載原始數據頁+變化redo指定內存。使用redo重構髒頁(前滾)。
 5.若是確認這次事務沒有commit標記,當即觸發回滾操做,根據DB_TRX_ID+DB_ROLL_PTR信息,找到und回滾日誌,實現回滾。
 以上流程被稱之爲InnoDB的核心特性:自動故障恢復(Crash Recovery)。先前滾再回滾,先應用redo再應用undo。

undo在ACID中的保證

主要保證事務的A的特性,同時C和I的特性也有關係。 事務中的C特性怎麼保證?

 InnoDB crash recovery:數據庫意外宕機時刻,經過redo前滾+undo回滾保證數據的最終一致。
 InnoDB doublewrite buffer: 默認存儲在ibdataN中。解決數據頁寫入不完整
 mysqld process crash in the middle of a page write, InnoDB can find a good copy of the page from the doublewrite buffer during crash recovery.
 DWB一共2M。分兩次,每次1M寫入

事務中的I的特性怎麼保證?

隔離級別:讀隔離性

 RU: 髒讀 、 不可重複讀 、幻讀
 RC: 不可重複讀、幻讀
 RR:有可能會出現幻讀。
 SR(SE):事務串行工做。

鎖機制:寫的隔離

做用:保護併發訪問資源。

保護的資源分類:

 latch(閂鎖):rwlock、mutex,主要保護內存資源
 MDL: Metadata_lock,元數據(DDL操做)
 table_lock: 表級別
 lock table t1 read ;
 mysqldump、XBK(PBK):備份非InnoDB數據時,觸發FTWRL全局鎖表(Global)。

行鎖升級爲表鎖。

 row lock:InnoDB 默認鎖粒度,加鎖方式都是在索引加鎖的。
 record lock:記錄鎖,在聚簇索引鎖定。RC級別只有record lock。
 gap lock:間隙鎖,在輔助索引間隙加鎖。RR級別存在。防止幻讀。
 next lock:下一鍵鎖,GAP+Record。   RR級別存在。防止幻讀。 (,]

如何監控行鎖問題?

 mysql> select * from sys.innodb_lock_waits\G

功能性上:

 IS: select * from t1 lock in shared mode;
 S : 讀鎖。            
 IX: 意向排他鎖。表上添加的。 select * from t1 for update;
 X : 排他鎖,寫鎖。

MVCC : 多版本併發控制

 樂觀鎖: 樂觀。
 悲觀鎖: 悲觀。
 每一個事務操做都要經歷兩個階段:
 讀: 樂觀鎖。
 MVCC利用樂觀鎖機制,實現非鎖定讀取。
 read view:RV,版本號集合。
 trx1 :
 begin;
 dml1  ---> 在作第一個查詢的時候,當前事務,獲取系統最新的:RV1 版本快照。
 dml2  ---> 生成 RV2 版本快照。
 select 查詢 RV2 快照數據
 commit; ----> RV2 快照數據 ----》系統最新快照。

RC

 trx1: Rv1  Rv2  commit;
 trx2 RVV1 RVV1 RV2

RR

 trx1 : 第一個查詢時, 生成global consitence snapshot  RV-CS1(10:00) ,一直伴隨着事務生命週期結束。
 trx2 : 第一個查詢時,生成global consitence snapshot RV-CS2(10:01) ,一直伴隨着事務生命週期結束。
 快照技術由undo log來提供。
 寫: 悲觀鎖 X

總結:

 1.MVCC採用樂觀鎖機制,實現非鎖定讀取。
 2.在RC級別下,事務中能夠當即讀取到其餘事務commit過的readview
 3.在RR級別下,事務中從第一次查詢開始,生成一個一致性readview,直到事務結束。
相關文章
相關標籤/搜索