聊聊數據庫~6.SQL運維中篇


上篇回顧:https://www.cnblogs.com/dotnetcrazy/p/10810798.html#top

1.6.5.MySQL日志相關

本文的測試環境:MySQL5.7.26MariaDB5.5.60MySQL8.0.16

PS:版本查詢select version();

1.MySQL常用日志

服務器層日志(存儲引擎層有自己的日志)

日志類型 描述
error_log(錯誤日志) 記錄MySQL啟動、運行或停止時出現的問題
general_log(常規日志) 記錄所有發送給MySQL的請求(耗性能)
slow_query_log(慢查日志) 記錄符合條件的查詢(eg:超過10s、沒有使用索引等)
binary_log(二進制日志) 記錄全部有效的數據修改日志(老版本數據庫不會開啟)
relay_log(中繼日志) 用於主從復制,臨時存儲主從同步的二進制日志(增量復制)

知識擴展:https://blog.csdn.net/zhang123456456/article/details/72811875

實時查看文件:tail -f /var/log/mysqld.log

tail -f 用於監視文件增長(默認是末尾10行)

2.error_log(錯誤日志)

一般記錄MySQL運行錯誤和和未授權的訪問

  • 老版:log_error + log_warnings
  • 常用:log_error + log_error_verbosity
  • 新版:log_error + log_error_verbosity + log_error_services

查詢MySQL配置:show variables like '%log_error%';

SQL查詢可以這么干:

-- Ubuntu下默認是:`/var/log/mysql/error.log`
-- CentOS下默認是:`/var/log/mysqld.log` | `/var/log/mariadb/mariadb.log`
select @@log_error; -- 盡可能和Data分開存儲

-- 0:不記錄警告信息,1:告警信息寫入錯誤日志,2:各類告警信息都寫入(eg:網絡故障和重連信息)
select @@log_warnings; -- MySQL8中已經移除(MySQL5.7默認是2,MariaDB5.5.60默認是1)

-- 錯誤級別(1:Error,2:Error、Warning,3:Error、Warning、Info
select @@log_error_verbosity; -- MySQL8默認是2,MySQL5.7默認是3
-- PS:從MySQL 5.7.2開始,首選`log_error_verbosity`系統變量

-- 默認是`log_filter_internal; log_sink_internal`
select @@log_error_services; -- MySQL8.0中新增

PS:其實MySQL在市面上有很多成熟解決方案(基本上都是基於5.6、5.7的)

這也是為什么我開篇主推MySQL5.7系列和MariaDB5.5.60(很多時候不是不用最新DB,而是架構依賴做不到啊)

知識拓展:https://www.cnblogs.com/kerrycode/p/8973285.html

PS:SQLServer的ErrorLog也是差不多的

類比MSSQL的Errorlog.png

MySQL8.0新增參數:log_error_services

日志服務組件

日志服務組件名 描述
log_sink_internal 默認的日志輸出組件(依賴log_error
log_filter_internal 默認的日志過濾組件(依賴log_error_verbosity
log_sink_json 將錯誤日志輸出到json文件
log_sink_syseventlog 將錯誤日志輸出到系統日志文件

PS:log_filter_internal:過濾錯誤信息(達不到級別的不記錄

日記格式一般是這樣的UTC時間戳 進程id [日志級別] [錯誤代碼] [由什么產生的日志(Server or Client)] 詳細信息

eg:2019-05-19T09:54:11.590474Z 8 [Warning] [MY-010055] [Server] IP address '192.168.36.144' could not be resolved: Name or service not known

一般log_sink_json用的比較多

官方文檔參考:https://dev.mysql.com/doc/refman/8.0/en/error-log-json.html

PS:第一次使用需要安裝一下json組件:install component 'file://component_log_sink_json';

常用設置:set persist log_error_services='log_filter_internal;log_sink_json';

時間戳相關的小知識點

上面的時間默認是UTC的時間戳,和我們是有時差的,這個時間戳可以通過設置log_timestamps來本地化:

-- 查詢
select @@log_timestamps; -- MySQL5.7新增

-- 從8開始,可通過SET PERSIST命令將全局變量的修改持久化到配置文件中
set persist log_timestamps='SYSTEM'; -- 需要root權限

PS:set persist生成的配置文件路徑在:/var/lib/mysql/mysqld-auto.cnf

autoconfig.png

3.general_log(常規日志)

以前開發調試的時候基本上都是會開啟的,上線后關閉(系統V1初期的時候也會開啟一段時間)

現在開發可以使用go-sniffer來抓包查看客戶端執行的SQL

-- 是否打開常規日志(0不打開,1打開)
-- 一般不打開(性能)
select @@general_log; -- 默認為0

-- Ubuntu默認:/var/lib/mysql/ubuntuserver.log
-- CentOS默認:/var/lib/mysql/localhost.log
select @@general_log_file; -- 常規日志的路徑

-- 日志的存儲方式(FILE | TABLE | NONE)
select @@log_output; -- 默認是文件存儲

簡單看一下常規日志在數據庫中的結構:

2.常規日志.png

臨時開啟參考

# 開啟
set global general_log = 1;

# set [global | persist] general_log_file = '日志路徑';

set global log_output = 'TABLE';

4.slow_query_log(慢查詢日志)

這個是最常用的,把符合條件的查詢語句記錄在日志中,一般都是些需要優化的SQL

PS:出現性能瓶頸的時候,或者為了優化SQL會開啟一段時間(小項目推薦直接開啟)

先看下默認值:show variables like '%slow%';show variables like 'long%';

2.慢查詢.png

SQL查詢

-- 是否開啟
select @@slow_query_log; -- 默認是關閉

-- CentOS:/var/lib/mysql/localhost-slow.log
-- Ubuntu:/var/lib/mysql/ubuntuserver-slow.log
select @@slow_query_log_file;

-- 條件:設置超過多少秒為慢查詢(一般設置1s)
select @@long_query_time; -- 默認是10s(支持小數:0.003)

-- PS:設置為0就會記錄所有SQL(不推薦這么干)

-- 條件:沒有使用索引的查詢記錄到日志中
select @@log_queries_not_using_indexes; -- 默認是0(不開啟)

-- 記錄optimize table、analyze table和alter table的管理語句
select @@log_slow_admin_statements; -- 默認是0(不開啟)

-- 記錄由Slave所產生的慢查詢
select @@log_slow_slave_statements;

常用設置

PS:高並發下的互聯網項目,對SQL執行時間的容忍度一般都是低於300~500ms的(long_query_time=0.05

# 常用如下:(需要MySQL的root權限)
set global slow_query_log = 1; # 開啟慢查詢日志
set global long_query_time = 1; # 記錄大於1s的SQL
set global log_slow_admin_statements = 1; # 記錄管理語句
set global log_queries_not_using_indexes = 1; # 記錄沒有使用索引的SQL
# set [global | persist] slow_query_log_file = '路徑'; # 設置log路徑

設置long_query_time時,需要重新連接才能生效(不需要重啟DB)

PS:當前會話不生效,之后的會話就生效了(不想重連可以再設置下當前會話的long_query_time

知識拓展:(chown mysql:mysql /work/log/xxx.log

擴展:慢查詢工具

先簡單分析下慢查詢日志:

# Time: 2019-05-22T21:16:28.759491+08:00
# User@Host: root[root] @ localhost []  Id:    11
# Query_time: 0.000818  Lock_time: 0.000449 Rows_sent: 5  Rows_examined: 5
SET timestamp=1558530988;
select * from mysql.user order by host; # SQL語句
  1. Time:查詢的執行時間start_time
  2. User@Host: root[root] @ localhost [] Id:11:執行 sql 的主機信息
  3. Query_time:SQL查詢
  4. Lock_time鎖定時間
  5. Rows_sent:所發送的行數
  6. Rows_examined鎖掃描的行數
  7. SET timestamp=1558530988;:SQL執行時間

現在可以說說工具了,推薦兩款:

  1. 自帶的慢日志分析工具:mysqldumpslow
  2. MySQL工具箱(percona-toolkit)中的pt-query-digest
mysqldumpslow(精簡)

查詢最慢的10條SQL:mysqldumpslow -s t -t 10 /var/lib/mysql/localhost-slow.log

-s 按照那種方式排序
    t: 查詢時間
    c:訪問計數
    l:鎖定時間
    r:返回記錄
    al:平均鎖定時間
    ar:平均訪問記錄數
    at:平均查詢時間
-t 返回多少條數據(可以理解為top n)
-g 可以跟上正則匹配模式,大小寫不敏感。

PS:使用mysqldumpslow的分析結果不會顯示具體完整的sql語句:

  1. 翻頁sql不一樣,性能也是不一樣的,越往后的頁數越容易出現慢查詢,而mysqldumpslow把所有翻頁sql當成一個sql了
  2. eg:select * from tb_table where uid=20 group by createtime limit 10000, 1000; ==> select * from tb_table where uid=N group by createtime limit N, N;
    • 不管你uid和limit怎么變,mysqldumpslow認為是一樣的
pt-query-digest(推薦)

官方文檔:https://www.percona.com/doc/percona-toolkit/3.0/pt-query-digest.html

分析慢查詢日志:pt-query-digest /var/lib/mysql/localhost-slow.log

  1. 使用tcppdump捕獲MySQL協議數據,然后報告最慢的查詢:
    • tcpdump -s 65535 -x -nn -q -tttt -i any -c 1000 port 3306 > mysql.tcp.txt
    • pt-query-digest --type tcpdump mysql.tcp.txt
  2. 查看來自遠程進程列表上最慢的查詢:
    • pt-query-digest --processlist h=ip

安裝可以參考:https://github.com/lotapp/awesome-tools/blob/master/README.md#4運維

PS:percona-toolkit的常用工具我也在里面簡單說了下,對應文檔也貼了

other

PS:還有一款mysqlsla我沒用過,所以貼個參考文章,感興趣的同志自己研究下

https://www.cnblogs.com/fengchi/p/6187099.html

知識拓展:https://www.cnblogs.com/fengchi/p/6187099.html


5.binary_log(二進制日志)

上節主要說了通用日志和慢查日志,今天說下二進制日志:

二進制日志算是最常用的了,主要就是記錄對數據庫的修改,然后就是主從復制用的比較多(比如增量備份)

PS:記錄了修改操作,那么衍生出的場景就是:增量備份和恢復(基於時間點的備份和恢復)

PS:MySQL日志主要分為這兩類:(互不干擾)

  1. 服務層日志(和使用存儲引擎無關)
    • 通用日志、慢查詢日志、二進制日志
  2. 存儲引擎層日志
    • eg:innodb的重做日志(redo log)和回滾日志(undo log)

Q:那什么樣的修改會記錄下來呢?

A:記錄所有對MySQL數據庫的修改事件(包括增刪改查事件和對表結構修改的事件),而且只記錄已經成功執行的事件(失敗的不會記錄)

這么說可能有點抽象,熟悉SQLServer的同志看個圖就秒懂:

3.二進制日志.png

5.1.二進制日志格式
參數 說明
STATEMENT 基於段的格式,記錄執行數據修改時候所執行的SQL語句
ROW 基於行的格式,記錄增刪改查操作所修改行的信息(每修改一行就會有一條信息)
MIXED 基於行和端的混合格式,根據SQL語句由系統決定是基於段還是基於行的日志格式記錄

查看方式:show variables like 'binlog_format';

  1. binlog_format=statement:基於段的記錄格式(老版本的默認值)
    1. 優點:記錄量較小,節約磁盤和網絡IO(單條操作Row更節約)
    2. 缺點:必須記錄上下文信息來保證語句在從服務器上執行結果與主服務器相同
      • 但是如果使用了uuid()user()等結果非確定的函數,可能會造成MySQL主從不一致
    3. 日志查看mysqlbinlog /var/lib/mysql/binlog.0000xx | more(不用指定參數)
  2. binlog_format=row:基於行的記錄格式(5.7以后的默認值)
    1. 優點:可以避免MySQL復制中出現的主從不一致的問題(主從更安全)
      • PS:沒有備份的時候可以通過分析row格式的二進制日志來反向恢復
    2. 缺點:記錄日志量較大(順序寫入)
      • 現在增加了新參數來優化binlog_row_image=[full|minimal|noblob]
    3. 日志查看mysqlbinlog -vv /var/lib/mysql/binlog.0000xx | more
  3. binlog_format=mixed:基於行和端的混合格式(推薦
    • PS:數據量大小由所執行的SQL決定(非確定性函數越多,行數越多)

PS:DDL操作(create、drop、alter)的時候都是基於段方式來記錄log

如果一條一條記錄,表有上億數據,我就修改某列的狀態值,那不得瘋?

binlog_row_image=[FULL|MINIMAL|NOBLOB]的補充說明

PS:查看方式:show variables like 'binlog_row_image'

  1. 默認是full:完整
    • 記錄修改行的全部內容
  2. noblob:就是在full記錄的基礎上對大文本列的優化
    • 沒有對text或者blob列修改就不記錄該列
  3. minimal:簡單記錄,只記錄修改的那一列
    • PS:這個要特別注意一點,雖然容量小了,但是一旦誤操作,很難恢復的(不知道原來內容)
推薦使用

一般使用binlog_format=mixed混合格式 or binlog_format=row + binlog_row_image=minimal

PS:如果對安全性要求特別高,推薦使用binlog_format=row + binlog_row_image=full(不怕誤操作)

這個和SQLServer的日志恢復模式有點類似,我貼下圖你們可以對比參考:

3.容量.png

5.2.二進制日志配置

上面雖然說完了二進制日志的常用3種格式,但老版本默認都是不啟用二進制日志的,咋辦?

PS:如果是MariaDB可以去示例配置中查看:ls /usr/share/mysql/ |grep .cnf(CentOS)

驗證下:

MySQL8之前:cat /etc/mysql/mysql.conf.d/mysqld.cnf(UbuntuServer)

3.binlog.png

MySQL8:cat /etc/my.cnf |grep log(CentOS)
3.binlog2.png


Q:有些人可能疑惑了,為什么用show variables like 'log_bin';查詢出來的結果和配置文件中不大一樣啊?

PS:一般配置項中的參數都可以使用show variables like 'xx'來查詢對應的值

3.log_bin.png

A:那是因為5.7之后版本分成了兩個參數:log_binlog_bin_basename

PS:配置文件的log_bin=xxx相當於命令中的log_binlog_bin_basename

mysql> show variables like 'log_bin%';
+---------------------------------+-----------------------------+
| Variable_name                   | Value                       |
+---------------------------------+-----------------------------+
| log_bin                         | ON                          |
| log_bin_basename                | /var/lib/mysql/binlog       |
| log_bin_index                   | /var/lib/mysql/binlog.index |
| log_bin_trust_function_creators | OFF                         |
| log_bin_use_v1_row_events       | OFF                         |
+---------------------------------+-----------------------------+
5 rows in set (0.00 sec)
開啟演示

MariaDB開啟binlog圖示:(CentOS)

4.MariaDB開啟binlog.jpg

MySQL5.7演示:(UbuntuServer)

4.UbuntuServer下MySQL5.7演示.jpg

配置文件中修改:(show variables like 'binlog_format';:查看當前binlog基於什么格式

# 服務器標識
server-id=1 # 單機MariaDB可不開啟

# 開啟binlog並設置路徑
# 不指定路徑則默認在數據目錄下
log_bin=binlog # 這個代表以binlog開頭的文件

# binlog采用ROW|MIXED格式
# binlog_format=MIXED # 5.7默認是ROW

先看下文件前綴(log_bin=binlog)的概念,一張圖就懂:

4.文件前綴.png

PS:如果log_bin只是指定一個名字,那么默認路徑一般都是在數據文件的文件夾中

配置文件一般都會寫,eg:datadir=/var/lib/mysql,或者通過show variables like 'datadir';也可以查詢到

雖然和SQLServer文件組不是一個概念,但有些相似 ==> log可以多個也可以動態調整
3.多日志文件.png

5.3.ROW模式下記錄SQL

Q:雖然ROW記錄能保證主從數據安全,但我們排查問題的時候往往需要知道SQL,而用段的記錄方式又不合適,咋辦?

A:有個新參數可以解決:binlog_rows_query_log_events,開啟后就可以記錄sql了

查看方式:show variables like 'binlog_row%';

mysql> show variables like 'binlog_row%';
+------------------------------+-------+
| Variable_name                | Value |
+------------------------------+-------+
| binlog_row_image             | FULL  |
| binlog_rows_query_log_events | OFF   |
+------------------------------+-------+
2 rows in set (0.01 sec)
binlog演示

顯示binlog列表:show binary logs;

刷新一份新的binlog:flush logs;(現在開始的二進制日志就記錄在這個新文件中)

5.新建binlog.jpg

binlog現在是空的:(-vv:把二進制格式的日志顯示為能讀懂的字符串)

mysqlbinlog --no-defaults -vv --base64-output=DECODE-ROWS /var/lib/mysql/binlog.000006

5.新建binlog2.png

現在簡單摸擬幾個SQL操作,然后看看binlog:

6.sql.png

查看下binlog日志:(線上開發一般都是FULL模式,主要是防止程序員修改SQL的時候不加條件等誤操作)

FULL模式就是這樣,該行數據全部記錄(修改部分其實就綠色框的地方)

6.binlog.jpg

想要binlog中記錄SQL就開啟binlog_rows_query_log_events

PS:像這種操作,如果模式選混合模式,binlog中會記錄SQL的

臨時開啟下binlog_rows_query_log_events(如果你有需要可以配置文件設置一下)

PS:MySQL8可通過set persist命令將全局變量的修改持久化到配置文件中

6.sql2.png

效果如下:

6.binlog2.png

5.4.二進制日志的清除
  1. 自動清除
    • 配置文件中設置時間:expire_logs_days = 30
  2. 手動清除
    • 刪除指定編號之前的日志:purge binary logs to 'binlog.000006';
    • 刪除指定時間之前的日志:purge binary logs before '2019-06-15 14:14:00';

已經23:23了,我們快速演示下:

MySQL命令行中執行命令:

7.刪除.png

文件列表:

7.文件.png

5.5.二進制日志與主從

這個把運維篇講完會繼續說,運維篇結束后會有個高級篇(架構),這邊就簡單提下二進制格式對主從復制的影響

  1. 基於SQL語句的復制(SBR)
    • 二進制日志格式使用的是statement格式(5.7前的默認)
  2. 基於行的復制(RBR)
    • 二進制日志格式使用的是基於行的日志格式
  3. 混合模式
    • 根據實際在上面兩者中切換

貼個課后拓展文章:https://www.cnblogs.com/gujianzhe/p/9371682.html

下級預估:備份與恢復、監控


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM