30.6. MySQL併發控制,加鎖和事務,隔離級別,日誌等

併發控制

鎖粒度:
表級鎖
行級鎖
鎖:
讀鎖:共享鎖,只讀不可寫(包括自己當前用戶和當前事務) ,多個讀互不阻塞
寫鎖:獨佔鎖,排它鎖,寫鎖會阻塞其它事務(不包括當前事務)的讀和它鎖
實現
存儲引擎:自行實現其鎖策略和鎖粒度
服務器級:實現了鎖,表級鎖,用戶可顯式請求
分類:
隱式鎖:由存儲引擎自動施加鎖
顯式鎖:用戶手動請求

鎖策略:在鎖粒度及數據安全性尋求的平衡機制
顯式使用鎖

  1. LOCK TABLES 加鎖
    lock tables tbl_name [[AS] alias] lock_type
    [, tbl_name [[AS] alias] lock_type] ...
    lock_type: READ ,WRITE
  2. UNLOCK TABLES 解鎖
  3. FLUSH TABLES [tb_name[,...]] [WITH READ LOCK]
    關閉所有正在打開的表,同時清除掉查詢緩存以及準備好的語句緩存,
    如果加上with read lock 選項的話,它代表關閉所有正在打開的表並加上全局鎖(不清除緩存了),通常在備份前加全局讀鎖
  4. SELECT clause [FOR UPDATE | LOCK IN SHARE MODE]
    查詢時加寫或讀鎖

注意點1(加鎖):

  1. 注意,讀鎖加到表上之後,此表將只能讀,不能進行其他任何操作。而且加了讀鎖的這個session終端也不能對它進行讀read以外其他操作了,必須要unlock table命令之後纔可以進行其他操作。
    • 但是如果在此session上加了一個read鎖之後,再在此session上對其他的一個或多個表再繼續加read鎖(沒有unlock tables),則經過測試以及查看lock的幫助得知,之前加的這個鎖將會被清除掉,只有最後一個鎖會生效。
    • 對於其他的session終端來說,只有最後一個表相當於被加了read鎖,之前在原來的session被加鎖的表的鎖都會消失掉,和沒加鎖效果一樣。而對於原來的session來說也是這樣,只不過經過測試,如果想要訪問之前被加過鎖但是掉鎖的表的時候,它會報錯(不論是select命令還是其他命令都會報錯,其他終端session上不會報錯)
    • 此時在原session上只有unlock table 之後纔可繼續訪問之前的被加鎖但是掉鎖的表而不報錯。
    • 經過測試 lock table TABNAME write 的結果和上面read的效果相同,只不過它倆限制的內容不一樣而已,上面說的現象都是lock tables這個命令的效果,和加讀鎖read還是寫鎖write無關;
    • 還有一點要注意的是,如果在當前session中對錶加了鎖之後,當前session終端上退出了mysql的連接(注意不是關閉了終端,就是當前終端上退出數據庫的連接,當然關閉終端更加暴力),則這個鎖就立即失效了,其他的終端session上就可以繼續進行正常訪問數據庫了。(同時加鎖也是立即生效的,而且這個鎖都是全局立即生效的,注意這個效果和之前的global變量不同:global變量雖然也是全局的,但它必須重連session才能生效,這個加鎖解鎖都是命令一執行則所有連接的session不用重新連接就全局直接立即生效了。
  2. flush tables with read lock:這個命令和lock table還是有區別的,而且有一點非常重要
    • 當在一個session中,先用了lock table命令鎖了自己想要鎖定的表,此時如果在再當前session開啓事務,則注意,這些用命令lock table命令被鎖的表上面的鎖全部都會被釋放,就相當於執行了unlock tables,同時,其他session之前不能執行的操作或者正在執行但是阻塞的操作(比如這邊session開了讀鎖,另外的session要插入數據但是卡住,而此時這邊的session又開了事務,則另外的session中的這個插入命令就直接執行併成功了,而且能夠瞬間被這邊的事務看到,事務級別爲可重複讀的級別。估計是因爲測試用的插入命令就一條執行很快,如果插入修改等命令佔用時間過多的話可能在這邊事務中就看不到那麼多的內容了)
    • 而flush tables with read lock命令則不會受到事務開啓的影響,因爲它不含table lock。開啓事務之後,用這個命令執行的全局鎖仍然會生效。不過當然,如果當前連接到數據庫的session退出,這個全局鎖也會失效。也可以手動unlock tables取消這個全局鎖。
    • 更詳細的說明查看官方文檔
      https://dev.mysql.com/doc/refman/5.7/en/flush.html#flush-tables-with-read-lock
    • 下面三段話的解釋說明,(結合示例):
FLUSH TABLES WITH READ LOCK acquires a global read lock rather than table locks, so it is not subject to the same behavior as LOCK TABLES and UNLOCK TABLES with respect to table locking and implicit commits:

UNLOCK TABLES implicitly commits any active transaction only if any tables currently have been locked with LOCK TABLES. The commit does not occur for UNLOCK TABLES following FLUSH TABLES WITH READ LOCK because the latter statement does not acquire table locks.

Beginning a transaction causes table locks acquired with LOCK TABLES to be released, as though you had executed UNLOCK TABLES. Beginning a transaction does not release a global read lock acquired with FLUSH TABLES WITH READ LOCK.

注意:unlock tables 命令只能解當前session加的鎖

  1. flush不受到lock命令的影響,前者得到的鎖和後者得到的鎖不一樣:
  2. unlock tables命令會把當前session中的事務給提交commit了,因爲它隱性的執行了commit命令,注意不過有個前提就是數據庫中有表被lock tables 命令鎖住了,此時執行unlock tables纔會commit,如果沒有表被鎖,執行unlock tables命令相當於無效,沒有執行任何操作。
    • 注意上面已經說過了,開啓事務會把開始事務之前的table lock給解開,因此1中的情況指的是開啓事務之後,再次進行了lock table的命令進行了加鎖(不論是給哪個表加鎖都一樣),此時如果事務沒有完成但是進行了unlock tables命令操作,則它相當於即進行了解鎖,但會也把當前事務給提交commit了,(注意unlock提交之後再進行的操作就不在之前的事務內了)這點及其要注意。
    • 附加:如果對錶加了寫鎖,若沒有對錶進行更改,其他的session還是能用select查看此表的,因爲用了緩存,緩存驗證表滅有改變,其他session還能查看。但是如果當前session對錶進行修改了,則緩存驗證不通過,其他session就會卡住無法對錶進行包括查看之內的任何命令了。
  3. 對於flush tables加的鎖,則unlock tables 雖然能夠解鎖,但是不會把事務給提交了。
    • 注意 lock tables和flush tables加鎖經過測試不能夠同時存在
    • 比如先flush tables(不論是事務前還是事務後),因爲它會加了全局讀鎖之後就無法再用lock table命令給表加鎖了。
    • 再比如,先lock tables之後,然後再flush tables加全局鎖,它會提示Can't execute the given command because you have active locked tables or an active transaction。
  4. 一些結果的例子:
    • 先lock鎖再開啓事務,則lock鎖直接無效
    • 先事務,再開啓lock鎖,鎖有效,但是如果在當前的客戶端中解鎖,則會一併把事務給提交commit了(注意不是回滾),事務就結束了。
    • flush的加鎖全局有效,此時開啓事務沒有意義(只能讀),它的功能最好之久就是僅僅用於備份
    • 根據上面操作得知,要對錶進行獨佔操作,先開啓事務,再開啓寫鎖,順序不能反
  5. 還有一點就是,在session的事務中,一旦session斷開(quit數據庫或直接關閉窗口),如果加了lock鎖 則相當於此session的執行了unlock table命令,但因爲unlock tables它會隱性的執行commit命令 ,會將鎖全部解開並自動提交事務,因此會讓這個session斷開之間的事務的已經進行的操作給commit了,這點要注意小心
    但是如果此session並未加鎖,則session斷開則相當於當前事務執行了rollback操作。

事務

事務Transactions:一組原子性的SQL語句,或一個獨立工作單元
事務日誌:記錄事務信息,實現undo,redo等故障恢復功能

  • ACID特性:
    1. A:atomicity原子性;整個事務中的所有操作要麼全部成功執行,要麼全部失敗後回滾
    2. C:consistency一致性;數據庫總是從一個一致性狀態轉換爲另一個一致性狀態
    3. I:Isolation隔離性;一個事務所做出的操作在提交之前,是不能爲其它事務所見;隔離有多種隔離級別,實現併發
    4. D:durability持久性;一旦事務提交,其所做的修改會永久保存於數據庫中

事務的生命週期

image

事務隔離級別

  • 事務隔離級別:從上至下嚴格程度遞增
    1. READ UNCOMMITTED 可讀取到未提交數據,產生髒讀
    2. READ COMMITTED 可讀取到提交數據,但未提交數據不可讀,產生不可重複讀,即可讀取到多個提交數據,導致每次讀取數據不一致
    3. REPEATABLE READ 可重複讀,多次讀取數據都一致,產生幻讀,即讀取過程中,即使有其它提交的事務修改數據,仍只能讀取到未修改前的舊數據。此爲MySQL默認設置
    4. SERIALIZABILE 可串行化,未提交的讀事務阻塞修改事務,或者未提交的修改事務阻塞讀事務。導致併發性能差
  • MVCC: 多版本併發控制,和事務級別相關

image

事務的相關操作

  1. 啓動事務:
    BEGIN;
    BEGIN WORK;
    START TRANSACTION;
  2. 結束事務:
    COMMIT:提交
    ROLLBACK: 回滾
    注意:只有事務型存儲引擎中的DML語句方能支持此類操作,比如innodb。(myisam不支持)
  3. 自動提交:set autocommit={1|0}
    默認爲1,爲0時設爲非自動提交
    建議:顯式請求和提交事務,而不要使用“自動提交”功能
  4. 事務支持保存點:savepoint
    SAVEPOINT identifier;
    ROLLBACK [WORK] TO [SAVEPOINT] identifier;
    RELEASE SAVEPOINT identifier;

指定事務隔離級別:

服務器變量tx_isolation指定,默認爲REPEATABLE-READ,可在GLOBAL和
SESSION級進行設置

SET tx_isolation=''
READ-UNCOMMITTED
READ-COMMITTED
REPEATABLE-READ
SERIALIZABLE
也可在服務器選項中指定,重啓服務自動生效
vim /etc/my.cnf
[mysqld]
transaction-isolation=SERIALIZABLE

  • 注:mysql默認的事務級別(可重複讀,也叫幻讀)很符合生產要求,一般不需要修改

併發控制

  1. 死鎖:
    兩個或多個事務在同一資源相互佔用,並請求鎖定對方佔用的資源的狀態
  2. 事務日誌:
    事務日誌的寫入類型爲“追加”,因此其操作爲“順序IO” ;通常也被稱爲:預寫式日誌 write ahead logging
    事務日誌文件: ib_logfile0, ib_logfile1

注意點2(事務):

  1. 事務對於數據庫中的數據操作過程:開啓事務之後,首先將事務讀入內存之中,然後對它進行的各種操作會同步寫入事務日誌中,此時因爲並未寫入真正的數據存放文件位置內,因此可以進行回滾等操作。等到事務內的所有操作全部完成並提交之後,數據此時才真正的修改了。
    • 如果在這個過程中出現故障或者其他問題,則服務器下次正常重啓(也有可能是客戶端斷開了未提交事務)的時候,事務日誌發現上一次進行的事務並未完成,則它就會對這個事務已經進行的操作undo,進行rollback回滾操作。
    • 而如果重啓之後事務日誌發現上一次的事務的操作已經在內存中處理完畢了(事務完整並且提交結束了),但是對於數據的修改還未真正進行或者說僅僅執行了一部分,則事務日誌就會對這個事務進行redo操作,重新執行事務。
  2. 事務的原子性和一致性:一個或者多個事務如果在執行過程中中斷,則下次機器或者服務正常啓動的時候,會先對事務日誌進行查詢處於執行一半過程中的事務的操作將會回滾被撤銷掉,而那些之前完成的事務(並未寫入磁盤)則將會寫入到磁盤上,這樣就能保證事務操作的事件的原子性,要麼做完事務內的這一系列操作,要麼這些操作一個都不做。
  3. 注意,Mysql數據庫默認就自動提交,每一條單獨命令都是一個獨立的事務,每執行一條語句,就相當於執行了 ‘語句 ;commit;’ 的隱性2個操作(DDL語句更特殊,它不是相當於執行,它就是隱性跟着commit語句並執行它,下面有介紹)。
    • 由此原因,在之前進行的各種測試中,因爲沒有使用顯式事務(手動開啓事務),因此對數據庫進行的何種操作都相當於單條命令事務,只要一執行就相當於提交了會立即生效就能夠在其他的session中看到結果(比如創建數據庫,創建表這些DDL語言,以及DML語言增加刪除記錄等等,都會立即生效被其他session看到)
    • 可以通過修改配置或變量autocommit爲off來關閉自動提交事務
    • 但如果手動開啓了顯式事務,則在這個顯式事務內的命令就不會自動提交了,必須手動commit提交(除了DDL語句之外)。
    • 但是注意了DDL語言(create,drop,alter),就算是在事務中,比如說手動begin開啓事務或者說把autocommit設置爲off之後,它的結果也會立即生效,感覺就像直接就commit了DDL語句,即使當前是在事務內;而且感覺真的沒錯,經過測試,只要進行了DDL語句的命令,它後面就隱性地真的執行了commit命令並結束了當前事務,後面的命令就不在當前事務內了(如果mysql按照默認的配置自動提交,則此時後面執行DML的操作也就是單命令事務了,也就會立即生效並在其他session可見了),
    • 由上面DDL的特殊性可知,在事務中前面執行的DML語句就算沒有commit或者rollback,只要後面再跟上一個DDL語句就會把這個整個事務給提交了,很容易造成前面不想提交的DML語句被提交併且永久生效了。因此在事務中不能混用DML和DDL。
    • 還有一點就是手動開啓事務begin會把之前加的table鎖解開,但是autocommit模式下,如果不手動開啓事務,雖然執行一條命令自動提交了(看起來像執行了'begin 命令 commit'),但是這個隱性的begin事務不像顯式的手動begin開啓事務,它並不會把lock table的鎖給解開,所以放心使用
  4. 一個事務的結束用commit或者rollback命令,不論是這兩個的哪一個命令被執行了,都會結束當前事務;
    • 注意執行了上面兩個命令之後,當前事務就結束了,在mysql的默認配置下(自動提交),如果之後沒有再手動開啓事務的話,則再進行的操作就相當於自動提交的單條事務了(相當於重新連接到mysql數據庫時的初始狀態,沒有手動開啓任何顯式事務),而這樣就會在其他session中可見到立即生效的結果。
    • 還有要注意,如果在事務中用了savepoint 保存點, 利用rollback to savepoint_name命令,並沒有結束當前事務,後面的命令仍然在事務中執行,只有最後遇到commit或者rollback命令纔會結束事務並提交或回滾。注意它和rollback的區別:後者直接結束當前事務。
  5. 注意事務的隔離級別的作用範圍是兩個不同的事務之間,比如兩個新開的session都手動開啓了begin顯式事務,這兩個事務處於隔離級別中;而如果沒有開啓begin顯式事務的話,比如新開兩個session但沒有輸入begin,則不屬於隔離級別作用範圍。
    • 注意事務隔離級別的變量和服務器選項不相同。
    • 注意在事務之內的查詢select命令不會被緩存到查詢緩存中,只會增加not_cache中的數量(如果能存進去則其他session和事務就可以查詢到了,就起不到隔離級別的效果了)
  6. 在可重複讀的隔離級別中:一個session的一個事務提交commit並結束之後,然後利用了select 命令查看了一下。此時如果在另外的事務中用相同的select命令查看,則這一次查看經過對查詢緩存的status變量分析得知:
    - 第一點:首先我們要知道,查詢緩存的緩存內容,在連接到數據庫的任何一個session中,不論是在開啓的顯性事務內還是沒有開啓事務,只要對錶進行了DML語句(經測試DDL語句不影響),則此mysql數據庫之前的所有緩存過的SQL語句queries_in_cache都會被清空爲0(不修改不會清空)(不過那些統計的變量不會被清空)。
    • 還有一點就是,在一個事務內的進行的select語句查詢操作,如果在所有的session開啓事務之後,所有的session都沒有進行過DML語句,這在這個事務內的select的SQL語句也會被記錄到查詢緩存中,不過這個查詢緩存是相對於總的數據庫來說的,和這個事務無關。可以這樣說,標準狀況下,一個事務內的select查詢語句本來是不會被記錄到查詢緩存中的,事務內的select語句只會增加not-cached的數量,但在這裏會被記錄到查詢緩存queries_in_cache中是因爲這個事務雖然begin手動開啓了,但是並沒有執行修改數據的DML操作,也就相當於沒有開啓事務。執行的查詢操作被系統認爲在事務外,所以會被記錄到查詢緩存中
    • 在以上兩點的前提下,回到之前的問題,在開啓的顯性事務中第一個的select查看命令(不論在事務之前是否有完全一樣的SQL語句),即使在可重複讀隔離級別下,這整個事務的第一次的select查看語句都能夠查看到當前表的真實數據狀態。
    • 可以這樣理解,也就是說,這一個事務,雖然begin開啓的時間很早,但只要你沒有使用selec命令進行查看,則這個事務就相當於沒有開啓,只有在事務內使用了查看select命令的時候,這個可重複讀的事務才相當於剛剛開啓,之前的其他事務即使是在這個事務的begin命令之後的操作,也能夠被這第一個select命令查看到。
    • 根據上面的原因,當可重複讀事務內執行select命令之後,才能把它看做是真正的開啓了事務,之後的select纔有幻讀的隔離效果,此時如果其他的session再開啓事務,然後進行操作,即使它提交併進行了查看命令,也不會再影響這個事務的查看了。這裏絕不能把begin看做開啓事務的起點時間。經過了各種測試,確實是這樣的結果。
    • 注意可重複讀級別中,上面這裏的事務中的第一次select語句雖然看到當前數據庫表中真正的數據,但這裏真正的數據指的是已經被其他事務commit修改後的永久保存後的結果,如果其他事務還沒有提交併修改數據爲永久的(也就是說其他事務能夠回滾),則這個select是看不到的,一定要分清楚哪些能看到,哪些不能看到。還要區分可重複讀和不可重複讀以及上面提到的這一點坑的區別
    • 上面是經多次測試總結下來,目前就是這樣結果,更多內容更多以後再研究。
    • 如果利用事務進行備份而又不想影響到其他客戶進行修改或者查詢操作等,不能用flush tables的方式(當然在這裏其他客戶不能進行DDL操作),則就用幻讀的隔離級別事務來進行這種備份操作,進去之後一定要先執行一次select命令真正開啓事務再進行備份。
  7. Innodb存儲引擎,事務開啓之後,各個事務則會對錶中正在修改(DML)的行record進行加鎖操作,此時如果有兩個事務想要修改已經被對方事務加鎖的row記錄的話(也就是對方事務之前已經對這些行進行了DML操作了,加了鎖,注意這些行可以是數據庫中同一個表的不同行,也可是不同表的不同行),則就會產生死鎖現象(如果僅僅是一個事務想要修改被另外的事務中加鎖的表中的行row的話,只是會卡住而已)。
    • 此時如果服務器發現死鎖現象存在,就會將其中的一個事務進行rollback操作,僅讓一個事務生效。
  8. 如果出現了一個表長期被鎖導致其他用戶無法操作,可用show processlist以及和kill命令來剔除鎖表的用戶和命令(主要是sleep的用戶要踢掉,他可能就是鎖表的用戶,沒有進行操作還連着數據庫)
  9. 事務日誌是比數據寫入磁盤中要先寫的,也被稱爲預寫式日誌。它是按照追加的方式寫入默認的兩個事務日誌文件中的,這兩個事務日誌文件默認就在存放數據庫數據的目錄中(可修改)。這兩個(或多個)事務日誌文件一個文件寫滿了就會寫另外一個,另外一個也寫滿了就會重新返回來寫這個文件,把之前的日誌給覆蓋掉。

日誌

事務日誌 transaction log
錯誤日誌 error log
通用日誌 general log
慢查詢日誌 slow query log
二進制日誌 binary log
中繼日誌 reley log

事務日誌

事務日誌:transaction log
事務型存儲引擎自行管理和使用,建議和數據文件分開存放
redo log
undo log
Innodb事務日誌相關配置:
show variables like '%innodb_log%';
innodb_log_file_size 5242880 每個日誌文件大小
innodb_log_files_in_group 2 日誌組成員個數
innodb_log_group_home_dir ./ 事務文件路徑(可以寫絕對路徑)
innodb_flush_log_at_trx_commit 默認爲1

  1. innodb_flush_log_at_trx_commit
    說明:設置爲1,同時sync_binlog = 1表示最高級別的容錯
    innodb_use_global_flush_log_at_trx_commit的值確定是否可以使用SET語句重置此變量
    • 1默認情況下,日誌緩衝區將寫入日誌文件,並在每次事務後執行刷新到磁盤。這是完全遵守ACID特性
    • 0提交時沒有任何操作; 而是每秒執行一次日誌緩衝區寫入和刷新。 這樣可以提供更好的性能,但服務器崩潰可以清除最後一秒的事務
    • 2每次提交後都會寫入日誌緩衝區,但每秒都會進行一次刷新。 性能比0略好一些,但操作系統或停電可能導致最後一秒的交易丟失
    • 3模擬MariaDB 5.5組提交(每組提交3個同步),此項MariaDB 10.0支持

事務日誌優化:

image

錯誤日誌

  1. 錯誤日誌
    mysqld啓動和關閉過程中輸出的事件信息
    mysqld運行中產生的錯誤信息
    event scheduler運行一個event時產生的日誌信息
    在主從複製架構中的從服務器上啓動從服務器線程時產生的信息
  2. 錯誤日誌相關配置
    SHOW GLOBAL VARIABLES LIKE 'log_error'
    錯誤文件路徑
    log_error=/PATH/TO/LOG_ERROR_FILE
    是否記錄警告信息至錯誤日誌文件
    log_warnings=1|0 默認值1

通用日誌

  1. 通用日誌:記錄對數據庫的通用操作,包括錯誤的SQL語句
    文件:file,默認值
    表:table
  2. 通用日誌相關設置
    general_log=ON|OFF
    general_log_file=HOSTNAME.log
    log_output=TABLE|FILE|NONE

慢查詢日誌

慢查詢日誌:記錄執行查詢時長超出指定時長的操作
slow_query_log=ON|OFF 開啓或關閉慢查詢
long_query_time=N 慢查詢的閥值,單位秒
slow_query_log_file=HOSTNAME-slow.log 慢查詢日誌文件
log_slow_filter = admin,filesort,filesort_on_disk,full_join,full_scan,
query_cache,query_cache_miss,tmp_table,tmp_table_on_disk
上述查詢類型且查詢時長超過long_query_time,則記錄日誌
log_queries_not_using_indexes=ON 不使用索引或使用全索引掃描,不論
是否達到慢查詢閥值的語句是否記錄日誌,默認OFF,即不記錄
log_slow_rate_limit = 1 多少次查詢才記錄,mariadb特有
log_slow_verbosity= Query_plan,explain 記錄內容
log_slow_queries = OFF 同slow_query_log 新版已廢棄

二進制日誌

  1. 二進制日誌
    記錄導致數據改變或潛在導致數據改變的SQL語句
    記錄已提交的日誌
    不依賴於存儲引擎類型
    功能:通過“重放”日誌文件中的事件來生成數據副本
    注意:建議二進制日誌和數據文件分開存放
  2. 中繼日誌:relay log
    主從複製架構中,從服務器用於保存從主服務器的二進制日誌中讀取的事件

  3. 二進制日誌記錄格式
    • 二進制日誌記錄三種格式
      基於“語句”記錄:statement,記錄語句,默認模式
      基於“行”記錄:row,記錄數據,日誌量較大
      混合模式:mixed, 讓系統自行判定該基於哪種方式進行
  4. 格式配置
    show variables like ‘binlog_format';
  5. 二進制日誌文件的構成
    有兩類文件
    日誌文件:mysql|mariadb-bin.文件名後綴,二進制格式
    如: mariadb-bin.000001
    索引文件:mysql|mariadb-bin.index,文本格式
  6. 二進制日誌相關的服務器變量:
    • sql_log_bin=ON|OFF:是否記錄二進制日誌,默認ON
    • log_bin=/PATH/BIN_LOG_FILE:指定文件位置;默認OFF,表示不啓用二進制日誌功能,上述兩項都開啓纔可
    • binlog_format=STATEMENT|ROW|MIXED:二進制日誌記錄的格式,默認STATEMENT
    • max_binlog_size=1073741824:單個二進制日誌文件的最大體積,到達最大值會自動滾動,默認爲1G
      說明:文件達到上限時的大小未必爲指定的精確值
    • sync_binlog=1|0:設定是否啓動二進制日誌即時同步磁盤功能,默認0,由操作系統負責同步日誌到磁盤,1的話是立即寫入磁盤,雖然更加安全但是IO效率佔用多,影響服務器效率
    • expire_logs_days=N:二進制日誌可以自動刪除的天數。 默認爲0,即不自動刪除
  7. 二進制日誌相關配置
    • 查看mariadb自行管理使用中的二進制日誌文件列表,及大小
      SHOW {BINARY | MASTER} LOGS
    • 查看使用中的二進制日誌文件
      SHOW MASTER STATUS
    • 查看二進制文件中的指定內容
      SHOW BINLOG EVENTS [IN 'log_name'] [FROM pos] [LIMIT [offset,] row_count]
      show binlog events in ‘mysql-bin.000001' from 6516 limit 2,3

二進制日誌工具

mysqlbinlog:二進制日誌的客戶端命令工具

  1. 命令格式:
    mysqlbinlog [OPTIONS] log_file…
    --start-position=# 指定開始位置
    --stop-position=#
    --start-datetime=
    --stop-datetime=
    時間格式:YYYY-MM-DD hh:mm:ss
    --base64-output[=name]
    -v -vvv :查看詳細信息
    示例:
    mysqlbinlog --start-position=6787 --stop-position=7527 /var/lib/mysql/mariadb-bin.000003 -v
    mysqlbinlog --start-datetime="2018-01-30 20:30:10" --stop-datetime="2018-01-30 20:35:22" mariadb-bin.000003 -vvv

  2. 二進制日誌事件的格式:
    #at 328
    #151105 16:31:40 server id 1 end_log_pos 431 Query thread_id=1 exec_time=0 error_code=0
    use mydb/!/;
    SET TIMESTAMP=1446712300/!/;
    CREATE TABLE tb1 (id int, name char(30))
    /!/;

事件發生的日期和時間:151105 16:31:40
事件發生的服務器標識:server id 1
事件的結束位置:end_log_pos 431
事件的類型:Query
事件發生時所在服務器執行此事件的線程的ID:thread_id=1
語句的時間戳與將其寫入二進制文件中的時間差:exec_time=0
錯誤代碼:error_code=0
事件內容:
GTID:Global Transaction ID,mysql5.6以mariadb10以上版本專屬屬性:GTID

  1. 清除指定二進制日誌:
    PURGE { BINARY | MASTER } LOGS { TO 'log_name' | BEFORE datetime_expr }
    示例:
    PURGE BINARY LOGS TO ‘mariadb-bin.000003’;刪除3之前的日誌
    PURGE BINARY LOGS BEFORE '2017-01-23';
    PURGE BINARY LOGS BEFORE '2017-03-22 09:25:30';
    • 刪除所有二進制日誌,index文件重新記數:
      RESET MASTER [TO #];
      刪除所有二進制日誌文件,並重新生成日誌文件,文件名從#開始記數,默認從1開始,一般是master主機第一次啓動時執行,
      MariaDB10.1.6開始支持TO #
    • 切換日誌文件:
      FLUSH LOGS;
      注意它會Closes and reopens any log file to which the server is writing.
      因此雖然它也能生成新的二級制文件,不過最好還是單獨用命令flush binary logs;

注意點3(日誌)

  1. 各種日誌的位置,大小,啓用的功能等等參數都有相應的變量以及服務器選項配置用於修改。
  2. 事務日誌可用innodb_log_group_home_dir 選項在配置文件中修改其存放的位置,最好是把它和數據庫目錄分開存放,這樣可以提高數據庫的讀寫IO速度,減少IO佔用。
    • 事務日誌有多種把最終的修改後的事務數據寫入磁盤的方式,可以用變量(選項)innodb_flush_log_at_trx_commit的值來控制。如果爲提高效率,可以把它修改爲2.不過注意可能有丟失事務操作的數據的風險(不過一般這種風險比較小,因爲斷電都有額外供電電源,只有CPU或者mysql崩了纔會出現問題)
  3. 通用日誌默認不啓用,可以直接global變量修改general_log=on啓動,默認位置也和數據庫放在一起,也可以修改默認位置和名字 general_log_file
    • 通用日誌除了上面的以文件方式存放,也可以以表的形式存放,修改選項log_output的值即可.如果以表的形式存放,則這個表的位置就在mysql的database下,名字就叫general_log;
  4. 慢查詢日誌可以查看哪些任務的查詢速度很慢,再用explain重新執行這些命令,查看是否沒有利用索引等,以及再根據其他的操作來判斷爲何這個操作速度慢(命令不合理還是數據庫的表設計不合理等,或者說沒有創建索引,用於甩鍋,慢查詢因此很重要)
    • 除了打開慢查詢,調整一下時間長度,設置文件位置,還有一個選項log_queries_not_using_indexes打開,則可以讓查詢只要沒有用索引的記錄都用慢查詢記錄下來。以上幾項比較常用。
  5. 二進制的日誌只記錄已經提交的事務或者說是已經完成的數據操作,而事務日誌不論提交與否都會記錄下來事務中進行的各種操作。
    • 二進制日誌只記錄數據的增刪改操作,至於查詢以及對服務器的配置操作(修改變量和選項等,目前所知這種操作它不記錄的,因爲沒有對數據進行操作,可以查看官方文檔來詳細分析,因爲這個修改配置可能會影響後面的主從服務器的同步問題,但這裏暫時當做全部都不記錄)都不記錄。注意它會記錄授權以及創建用戶的操作等等,因爲這些操作相當於是對information和mysql數據庫的內容進行了修改。
    • 二進制日誌主要用於恢復上次全備份之後到服務器崩潰之間這一段時間內的數據操作,也用來進行主從備份。因此二進制日誌很重要,同時它不要和原來的數據庫文件放在一起,要分開硬盤存放,也要把它進行遠程主機的備份。
  6. 二級制日誌基於語句形式的話會造成根據時間狀況的命令執行的錯誤(比如now())函數等等),因此最好用基於row的方式記錄二進制日誌,不過這樣的話也會造成二進制日誌所佔空間的增長速度比數據庫還要快,它要比數據庫的文件還要大。也可以用mixd模式來進行記錄減少一點影響。不過最好是用row的格式來記錄。
  7. 二進制日誌有兩項控制變量必須都開啓纔可,其中sql_log_bin是爲了在mysql連接之後的session命令行中直接控制二進制是否記錄用的,在某些情況下(比如主從備份,還原數據的時候等等,因爲數據變了但是不想讓它記錄到二進制日誌中)會把它關閉來進行一些設置之後再開啓。
    • 另外一個log_bin選項,如果在配置文件中直接寫入,它會生成二進制文件在數據庫中,也可以用它自己指定二進制文件的路徑位置。生產中最好就是用這個選項把二進制日誌文件放到其他目錄中,但是注意別忘了二進制文件目錄的屬主(其他日誌改位置也是)。
    • 只要重啓mysql服務,就會生成一個新的二進制文件(重啓的時候相當於執行了flush binary logs命令)
  8. 利用mysqlbinlog查看的二進制文件的標準輸出(加上-v或者不加都行,重定向導出的時候最好不加),它的結果也可以直接導出到文件中,比如起個名字叫bin.sql 這個文件也可以直接導入到數據庫中進行數據的恢復操作。
    • 用這種方式的好處就是可以選取二進制文件中記錄的部分操作(利用start,stop等選開始結束點,不寫結束點的話就是到最後),然後進行恢復。當然也可以直接把這整個二進制文件進行導入數據庫的操作,和sql的導入方式是一樣的,mysql數據庫也支持。
    • 到這裏我們發現已經有4種文件可以直接導入了,一個是我們自己寫的sql文件,裏面就是各個sql語句;一個是二進制文件直接導入;一個是二進制文件部分導入,先利用mysqlbinlog查看的結果生成一個文件然後把它重定向導入;還有一個是後面的mysqldump命令生成的sql文件,它類似於自己寫的sql文件,不過它是數據庫的邏輯全盤備份文件,後面博客詳細介紹。
  9. 二進制文件的清理操作:
    • 在進行清理操作之前可用flush binary logs新建二進制文件,然後再用purge命令清除之前的二進制文件(可以指定日期等)
    • 利用reset master [to #] 可以清除所有的binarylogs並且從數字#開始,不寫默認爲1,它和flush的不同之處就是它會刪除掉所有的二進制文件,慎用。
    • 在不連接數據庫的情況下用mysqladmin flush-binary-logs也可以切換新的binlog.

下面是配置文件中,可能要配置的設置,先放在這裏以後完善詳細設置:

[服務器端]
注意各個日誌文件夾的屬主

datadir
socket
port
safe update 開啓
per table 開啓
sql_mode 嚴格模式
服務器字符集設置utf8mb4
慢查詢,時間閾值,位置,利用索引記錄打開
通用日誌,位置
事務日誌,大小,數量,位置,開啓2模式等
查詢緩存開啓,大小,每個字節設置等等,優化
pid-log
log-err
二進制日誌,位置

[客戶端]
字符集utf8mb4
socket
datadir
prompt="Date:\D Count:\c \n\U[\d] > "


下面是測試用sql語句文件,可寫入文件後直接導入數據庫。

create table testlog (id int auto_increment primary key,name char(10),age int default 20);

delimiter $$

create procedure  pro_testlog() 
begin  
declare i int;
set i = 1; 
while i < 100000 
do  insert into testlog(name,age) values (concat('wang',i),i); 
set i = i +1; 
end while; 
end$$

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