mysql expire_logs_days是怎麼生效和計算出來的

chenfeng發表於2018-11-16

mysql主備複製是透過binlog完成的。在開啟binlog的情況下,expire_logs_days引數可以讓mysql自動清理若干天前的binlog。

那麼expire_logs_days是在什麼時候生效的呢?初步猜想實在每次產生一個新的binlog的時候去判斷一次。查了一下具體的實現,確實是這樣的:

原始碼(5.1.58, log.cc)



int MYSQL_BIN_LOG::rotate_and_purge(uint flags)  

{  

...  

  if (!error && check_purge && expire_logs_days)  

  {  

    time_t purge_time= my_time(0) - expire_logs_days*24*60*60;  

    if (purge_time >= 0)  

      purge_logs_before_date(purge_time);  

  }  

...  

}  


如果設定了expire_logs_days,每次binlog rotate的時候都去計算一下purge_time(當前時間-expire_logs_days;

這個計算似乎是可以省去的: expire_logs_days是以天為單位,範圍是0~99, 0表示不會清理,自然不會進入if塊內:),

以99來計算,my_time(0) - 99*24*60*60>=0也是恆真的了),呼叫purge_logs_before_date(purge_time), 

purge_logs_before_date會從log index檔案中的第一個binlog檔案開始迴圈: 比較檔案的最後修改時間,如果小於purge_time,

就放入陣列to_log中。然後呼叫purge_logs,清理所有滿足條件的binlog。


int MYSQL_BIN_LOG::purge_logs_before_date(time_t purge_time)  

{  

...  

  MY_STAT stat_area;  

...  

  pthread_mutex_lock(&LOCK_index);  

  to_log[0]= 0;  

  if ((error=find_log_pos(&log_info, NullS, 0 /*no mutex*/)))  

    goto err;  

  

  while (strcmp(log_file_name, log_info.log_file_name) &&  

     !is_active(log_info.log_file_name) &&  

         !log_in_use(log_info.log_file_name))  

  {  

...  

      if (stat_area.st_mtime < purge_time)   

        strmake(to_log,   

                log_info.log_file_name,   

                sizeof(log_info.log_file_name) - 1);  

      else  

        break;  

...  

   }  

   

下面看一下purge_logs的實現:

int MYSQL_BIN_LOG::purge_logs(const char *to_log,   

                          bool included,  

                          bool need_mutex,   

                          bool need_update_threads,   

                          ulonglong *decrease_log_space)  

{  

...  

  while ((strcmp(to_log,log_info.log_file_name) || (exit_loop=included)) &&  

         !is_active(log_info.log_file_name) &&  

         !log_in_use(log_info.log_file_name))  

  {  

    if ((error= register_purge_index_entry(log_info.log_file_name)))  

...  

  }  

...  

  /* We know how many files to delete. Update index file. */  

  if ((error=update_log_index(&log_info, need_update_threads)))  

...  

  /* Read each entry from purge_index_file and delete the file. */  

  if (is_inited_purge_index_file() &&  

      (error= purge_index_entry(thd, decrease_log_space, FALSE)))  

...  

 


        purge_logs會將需要清理的binlog檔名放入purge_index_file(IO_CACHE)中,然後更新index檔案,最後再呼叫purge_index_entry刪除binlog檔案:


int MYSQL_BIN_LOG::purge_index_entry(THD *thd, ulonglong *decrease_log_space,  

                                     bool need_mutex)  

{  

...  

  for (;;)  

  {  

...  

        if (!my_delete(log_info.log_file_name, MYF(0)))  

        {  

          if (decrease_log_space)  

            *decrease_log_space-= s.st_size;  

        }  

...  

}  

       my_delete呼叫unlink()刪除binlog檔案。至此,完成了自動清理binlog的過程。另外當mysql啟動的時候,mysql也會執行purge_logs_before_date(purge_time)的過程(其它的操作,如果會引起binlog rotate,自然也會觸發這個過程,如flush logs)。


       梳理一下整個過程,不難發現,在壓力比較大的mysql上或生產環境,我們不應該啟動這個引數(my.cnf中不顯式設定該引數或設定expire_logs_days=0):mysql每天產生十幾個或更多的binlog檔案,啟用這個引數後,一次清理這麼多檔案,必定會導致磁碟io被佔滿,mysql出現抖動或hang住。因此建議自己編寫指令碼,每次purge完一個binlog後,sleep幾秒。


注意:

expire_logs_days設定之後不會立即清除,觸發條件是:

binlog大小超過max_binlog_size

手動執行flush logs

重新啟動時


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/15498/viewspace-2220393/,如需轉載,請註明出處,否則將追究法律責任。

相關文章