MySQL運維之神奇的引數
MySQL運維之神奇的引數
sql_safe_updates
http://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_sql_safe_updates
背景(why)
主要是針對大表的誤操作。
如果只是更改了幾條記錄,那麼說不定業務方可以很容易的根據日誌進行恢復。即便沒有,也可以通過找binlog,進行逆向操作恢復。
如果被誤操作的表非常小,其實問題也不大,全備+binlog恢復 or 閃回 都可以進行很好的恢復。
But,如果你要恢復的表非常大,比如:100G,100T,對於這型別的誤操作,恐怕神仙都難救。
所以,我們這裡通過這個神奇的引數,可以避免掉80%的誤操作場景。 PS: 不能避免100% ,下面的實戰會告訴大家如何破解。
生產環境的誤操作案例分享
update xx set url_desc=`防不勝防` WHERE 4918=4918 AND SLEEP(5)-- xYpp` where id=7046
這種表,線上有500G,一次誤操作,要恢復500G的資料,會中斷服務很長時間。
如果設定了sql_safe_updates,此類事故就可以很華麗的避免掉了。
原理和實戰
- 表結構
dba:lc> show create table tb;
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| tb | CREATE TABLE `tb` (
`id` int(11) NOT NULL,
`id_2` int(11) DEFAULT NULL COMMENT `lc22222233333`,
`id_3` text,
PRIMARY KEY (`id`),
KEY `idx_2` (`id_2`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
- update 相關測試
UPDATE statements must have a WHERE clause that uses a key or a LIMIT clause, or both.
* 不帶where 條件
dba:lc> update tb set id_2=2 ;
ERROR 1175 (HY000): You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column
* where 條件有索引,但是沒有limit
dba:lc> update tb set id_3 = `bb` where id > 0;
^C^C -- query aborted
ERROR 1317 (70100): Query execution was interrupted
* where 條件無索引,也沒有limit
dba:lc> update tb set id_3 = `bb` where id_3 = `0`;
ERROR 1175 (HY000): You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column
* where 條件有索引,有limit
dba:lc> update tb set id_3 = `bb` where id > 0 limit 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
* where 條件無索引,有limit
dba:lc> update tb set id_3 = `bb` where id_3 > 0 limit 1;
Query OK, 0 rows affected (0.26 sec)
Rows matched: 0 Changed: 0 Warnings: 0
結論: 對於update,只有兩種場景會被限制
- 無索引,無limit的情況
- 無where條件, 無limit的情況
- delete相關測試
DELETE statements must have both
* 不帶where 條件
dba:lc> delete from tb ;
ERROR 1175 (HY000): You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column
* where 條件有索引,但是沒有limit
dba:lc> delete from tb where id = 0 ;
Query OK, 0 rows affected (0.00 sec)
dba:lc> delete from tb where id > 0 ;
^C^C -- query aborted
ERROR 1317 (70100): Query execution was interrupted
dba:lc> explain select * from tb where id_2 > 0;
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------------+
| 1 | SIMPLE | tb | NULL | ALL | idx_2,idx_3 | NULL | NULL | NULL | 245204 | 50.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+--------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
dba:lc> delete from tb where id_2 > 0 ;
^C^C -- query aborted
^C^C -- query aborted
ERROR 1317 (70100): Query execution was interrupted
* where 條件無索引,也沒有limit
dba:lc> delete from tb where id_3 = `a` ;
ERROR 1175 (HY000): You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column
* where 條件有索引,有limit
dba:lc> delete from tb where id = 205 limit 1 ;
Query OK, 1 row affected (0.00 sec)
* where 條件無索引,有limit
dba:lc> delete from tb where id_3 = `aaaaa` limit 1 ;
Query OK, 1 row affected (0.00 sec)
測試結果證明: 關於delete相關,官方文件描述有誤。
結論: 對於delete,只有兩種場景會被限制
- 無索引,無limit的情況
- 無where條件, 無limit的情況
綜上所述:不管是update,還是delete ,被限制的前提只有兩個
1. 無索引,無limit的情況
2. 無where條件, 無limit的情況
好了,通過以上的知識,大家都應該很瞭解,接下來就是實施的問題了。
對於新業務,新DB,直接設定這樣的引數就好了,再測試環境也設定,這樣開發在測試環境就能發現問題,不會在新業務上產生這樣危險的語句。
對於老業務,怎麼辦呢?
我們的做法:因為我們的MySQL是5.6,所以另外一個神奇的功能就是P_S(performance schema), 通過P_S,我們可以獲取哪些query語句是沒有使用索引的。
這裡又會引發另外一個問題,可能是Performance schema的bug,它竟然無法統計dml 是否使用索引
經過我們大量的測試後證明:events_statements_summary_by_digest 表裡面的SUM_NO_INDEX_USED,SUM_NO_GOOD_INDEX_USED ,對dml無效。
既然如此,我們所幸對dml語句自己進行分析,將dml轉換成對應的select語句。
比如: update tb set id = S where id = S; 轉換成 select * from tb where id = `1` 。。。。
然後根據select語句,進行explain分析,如果type=ALL表示沒有使用索引,這樣的語句就是我們認為的全表dml語句了。
然而,理想很豐滿,現實很骨感。這樣的做法很快就出現了問題, 因為這裡需要自己構造真實的SQL,由於資料分佈以及構造的語句不可能真實,所以得到的執行計劃謬之千里,type=None。
所以,以上方法很可能導致全表的dml沒有被抓取出來,so 我們開始想其他辦法。
說來也簡單,sql_safe_udpates 只針對兩種場景是不允許的,那就是:
1. where條件後面 無索引,無limit的情況
2. 無where條件的情況 , 無limit的情況
那麼我們就獲取dml語句後面的欄位和關鍵字,用來構造我們的全表dml
1. 檢查dml 是否是帶有limit的語句
如果有,允許通過 -- ( 有limit , 肯定可以執行 )
如果沒有,則往下繼續判斷
2. 判斷dml SQL有無where條件
如果沒有, 則直接拒絕 -- (沒有where,沒有limit,肯定是全表掃描的更新,直接拒絕 )
如果有,則繼續往下判斷
3. 判斷where後面的欄位是否符合索引字首原理
如果符合,則允許執行 -- ( where條件後面欄位有索引,無limit, 允許通過 )
如果不符合,則拒絕 -- ( where條件後面欄位無索引,無limit,直接拒絕 )
恩,這樣分析下來,是不是感覺很完美了? 還是那句話,理想和現實總有差距,那麼來幾條牛逼的漏網之魚看看唄
1. 型別轉換導致的問題
update tb set id=2 where id_change = 1; -- 注意:欄位id_change是varchar型別。
2. 函式
UPDATE pay_log_id SET id=LAST_INSERT_ID(id + 1)
至少以上兩種型別是抓不到的,所以,還是有問題,那麼繼續找方法。
重新分析下我們的初心,我們的目的是啥?沒錯,我們的目的就是要先找到沒有使用索引的dml,突然腦海中飄來一句話,MySQL自身是否可以列印出沒有使用索引的語句呢?
果然,去官方文件上一搜index關鍵字,結果log_queries_not_using_indexes就是我們迫切需要的,但是它會將select也列印出來,不過沒關係,我們將select過濾掉即可。
so,最後的終極解決方案就是:在測試環境加上log_queries_not_using_indexes=1(long_query_time=1000,這樣可以不用混淆),然後測試環境跑一個月,將沒有使用索引的dml語句統統抓住來解決掉,這樣就可以安心的上線sql_safe_updates=1 了。
注意:
當log_queries_not_using_indexes=1 和 sql_safe_updates=1 同時設定的時候:
1) delete from tb_1 ; --會被sql_safe_updates拒絕,不會記錄到slow log中
2) update tb_1 set id = 1; --會被sql_safe_updates拒絕,同時也會被記錄到slow log中
以上就是兩者的區別,善用
總結
如果線上設定sql_safe_updates = 1 後,業務還有零星的dml被拒絕,業務方可以考慮如下解決方案:
1)如果你確保你的SQL語句沒有任何問題,可以這樣: set sql_safe_updates=0; 但是開發必須考慮到這樣做的後果。
2) 可以改寫SQL語句,讓其使用上索引欄位。
3)為什麼這邊沒有讓大家使用limit呢?因為在大多數場景下,dml + limit = 不確定的SQL 。 很可能導致主從不一致。 ( dml + limit 的方式,是線上禁止的)
各位看官,以上神器請大家慢慢享用。 關於PS和sys,如果大家有更加新奇的想法,可以一起討論研究。
相關文章
- Redis日常運維-引數詳解Redis運維
- 【MySQL】MySQL 5.6 引數之 extra_portMySql
- mysql運維案例MySql運維
- IT運維之自動化運維運維
- MySQL自動化運維之安裝篇MySql運維
- 【純乾貨】神奇的Ctrl鍵,Linux運維常用快捷鍵!Linux運維
- Mysql 引數MySql
- mysql之 CentOS系統針對mysql引數優化MySqlCentOS優化
- MySQL基礎運維——percona-toolkit運維工具MySql運維
- 【勝通 】mysql的引數變數MySql變數
- Linux運維必會的100道MySql面試題之(三)Linux運維MySql面試題
- MySQL運維之binlog_gtid_simple_recovery(GTID)MySql運維
- MySQL 效能優化之快取引數優化MySql優化快取
- Mysql重要配置引數的整理MySql
- MySQL運維5-Mycat配置MySql運維
- EMQX Cloud 更新:日誌分析增加更多引數,監控運維更省心MQCloud運維
- MySQL運維12-Mycat分庫分表之按天分片MySql運維
- MySQL引數調整MySql
- mysql 常用引數整理MySql
- MySQL引數說明MySql
- mysql 引數調優MySql
- MySQL運維的一款利器sys schemaMySql運維
- 美創運維日記|SQL Server安裝後,需立即修改的三個引數運維SQLServer
- mysql引數之innodb_buffer_pool_size大小設定MySql
- 大規模MySQL運維陷阱之基於MyCat的偽分散式架構MySql運維分散式架構
- mysql儲存過程的引數MySql儲存過程
- MySQL運維4-Mycat入門MySql運維
- mysql-inception自動化運維MySql運維
- MySQL 資料庫日常運維文件MySql資料庫運維
- 【MySQL】檢視MySQL Server引數方法MySqlServer
- C#引數傳遞之值引數C#
- mysql運維利器percona-toolkit工具之pt-query-digestMySql運維
- MySQL運維11-Mycat分庫分表之應用指定分片MySql運維
- Linux運維之程式管理①Linux運維
- 雲端計算運維學習---Nginx服務中rewrite引數的死迴圈問題運維Nginx
- MySQL針對Swap分割槽的運維注意點MySql運維
- MySQL效能相關引數MySql
- MySQL資料庫引數MySql資料庫