最近,在一個系統的慢查詢日志里發現有個insert操作很慢,達到秒級,并且是比較簡單的SQL語句,把語句拿出來到mysql中直接執行,速度卻很快。
這種問題一般不是SQL語句本身的問題,而是在具體的應用環境中,由于并發等原因導致的。最可懷疑的地方就是在等待表級鎖。
加上監控的日志來看,很多SQL是在同一時間完成的,下面的第三列是結束時間,第四列是開始時間:
14:27:30 bizId30905 1355812050 1355812045 14:27:30 bizId28907 1355812050 1355812043 14:27:30 bizId30905 1355812050 1355812047 14:27:30 bizId17388 1355812050 1355812040 14:27:30 bizId40563 1355812050 1355812044 14:27:30 bizId15477 1355812050 1355812048 14:27:30 bizId32588 1355812050 1355812048
但是通過應用的分析來看,并不存在表級鎖的地方,而insert自身的操作也只是對要插入的記錄本身加鎖,不會影響其他并發的insert操作。
沒有更好的辦法,只能在MySQL寫入磁盤的性能上考慮,MySQL有個innodb_flush_log_at_trx_commit參數,用來配置flush log到磁盤的時機,具體點說,是從log buffer寫到log file,并寫入到磁盤上的時機。這個參數的默認值是1,即每次事務提交的時候會把日志刷到磁盤,而頻繁的insert操作就會引起flush log操作的不斷積累,進而引發性能問題。在應用數據可接受的前提下,可以把這個值改成0,就是每秒才操作一次。修改后潛在的問題是,在事務已經提交的情況下,如果尚未寫入磁盤的時候發生故障,可能丟失數據。
MySQL官網對此參數的描述如下:
If the value of?innodb_flush_log_at_trx_commit
?is 0, the log buffer is written out to the log file once per second and the flush to disk operation is performed on the log file, but nothing is done at a transaction commit. When the value is 1 (the default), the log buffer is written out to the log file at each transaction commit and the flush to disk operation is performed on the log file. When the value is 2, the log buffer is written out to the file at each commit, but the flush to disk operation is not performed on it. However, the flushing on the log file takes place once per second also when the value is 2. Note that the once-per-second flushing is not 100% guaranteed to happen every second, due to process scheduling issues.
The default value of 1 is required for full ACID compliance. You can achieve better performance by setting the value different from 1, but then you can lose up to one second worth of transactions in a crash. With a value of 0, any?mysqld?process crash can erase the last second of transactions. With a value of 2, only an operating system crash or a power outage can erase the last second of transactions.?InnoDB
‘s?crash recovery?works regardless of the value.
其他角度的優化辦法:
如果是MyISAM存儲引擎,可以使用insert delay的方式來提高性能,其原理是MySQL自身會在內存中維護一個insert隊列,在實際表空閑的時候insert數據。
從應用的角度,批量提交也是解決問題的辦法,當然要在應用場景許可的前提下。
?
參考:
http://www.banping.com/2012/12/19/innodb_flush_log_at_trx_commit/