📄 log0log.c
字号:
/******************************************************Database log(c) 1995-1997 Innobase OyCreated 12/9/1995 Heikki Tuuri*******************************************************/#include "log0log.h"#ifdef UNIV_NONINL#include "log0log.ic"#endif#include "mem0mem.h"#include "buf0buf.h"#include "buf0flu.h"#include "srv0srv.h"#include "log0recv.h"#include "fil0fil.h"#include "dict0boot.h"#include "srv0srv.h"#include "srv0start.h"#include "trx0sys.h"#include "trx0trx.h"/*General philosophy of InnoDB redo-logs:1) Every change to a contents of a data page must be donethrough mtr, which in mtr_commit() writes log recordsto the InnoDB redo log.2) Normally these changes are performed using a mlog_write_ulint()or similar function.3) In some page level operations only a code number of a c-function and its parameters are written to the log to reduce the size of the log. 3a) You should not add parameters to these kind of functions (e.g. trx_undo_header_create(), trx_undo_insert_header_reuse()) 3b) You should not add such functionality which either change working when compared with the old or are dependent on data outside of the page. These kind of functions should implement self-contained page transformation and it should be unchanged if you don't have very essential reasons to change log semantics or format.*//* Current free limit of space 0; protected by the log sys mutex; 0 meansuninitialized */ulint log_fsp_current_free_limit = 0;/* Global log system variable */log_t* log_sys = NULL;#ifdef UNIV_DEBUGibool log_do_write = TRUE;ibool log_debug_writes = FALSE;#endif /* UNIV_DEBUG *//* These control how often we print warnings if the last checkpoint is tooold */ibool log_has_printed_chkp_warning = FALSE;time_t log_last_warning_time;#ifdef UNIV_LOG_ARCHIVE/* Pointer to this variable is used as the i/o-message when we do i/o to anarchive */byte log_archive_io;#endif /* UNIV_LOG_ARCHIVE *//* A margin for free space in the log buffer before a log entry is catenated */#define LOG_BUF_WRITE_MARGIN (4 * OS_FILE_LOG_BLOCK_SIZE)/* Margins for free space in the log buffer after a log entry is catenated */#define LOG_BUF_FLUSH_RATIO 2#define LOG_BUF_FLUSH_MARGIN (LOG_BUF_WRITE_MARGIN + 4 * UNIV_PAGE_SIZE)/* Margin for the free space in the smallest log group, before a new querystep which modifies the database, is started */#define LOG_CHECKPOINT_FREE_PER_THREAD (4 * UNIV_PAGE_SIZE)#define LOG_CHECKPOINT_EXTRA_FREE (8 * UNIV_PAGE_SIZE)/* This parameter controls asynchronous making of a new checkpoint; the valueshould be bigger than LOG_POOL_PREFLUSH_RATIO_SYNC */#define LOG_POOL_CHECKPOINT_RATIO_ASYNC 32/* This parameter controls synchronous preflushing of modified buffer pages */#define LOG_POOL_PREFLUSH_RATIO_SYNC 16/* The same ratio for asynchronous preflushing; this value should be less thanthe previous */#define LOG_POOL_PREFLUSH_RATIO_ASYNC 8/* Extra margin, in addition to one log file, used in archiving */#define LOG_ARCHIVE_EXTRA_MARGIN (4 * UNIV_PAGE_SIZE)/* This parameter controls asynchronous writing to the archive */#define LOG_ARCHIVE_RATIO_ASYNC 16/* Codes used in unlocking flush latches */#define LOG_UNLOCK_NONE_FLUSHED_LOCK 1#define LOG_UNLOCK_FLUSH_LOCK 2/* States of an archiving operation */#define LOG_ARCHIVE_READ 1#define LOG_ARCHIVE_WRITE 2/**********************************************************Completes a checkpoint write i/o to a log file. */staticvoidlog_io_complete_checkpoint(void);/*============================*/#ifdef UNIV_LOG_ARCHIVE/**********************************************************Completes an archiving i/o. */staticvoidlog_io_complete_archive(void);/*=========================*/#endif /* UNIV_LOG_ARCHIVE *//********************************************************************Sets the global variable log_fsp_current_free_limit. Also makes a checkpoint,so that we know that the limit has been written to a log checkpoint fieldon disk. */voidlog_fsp_current_free_limit_set_and_checkpoint(/*==========================================*/ ulint limit) /* in: limit to set */{ ibool success; mutex_enter(&(log_sys->mutex)); log_fsp_current_free_limit = limit; mutex_exit(&(log_sys->mutex)); /* Try to make a synchronous checkpoint */ success = FALSE; while (!success) { success = log_checkpoint(TRUE, TRUE); }}/********************************************************************Returns the oldest modified block lsn in the pool, or log_sys->lsn if noneexists. */staticdulintlog_buf_pool_get_oldest_modification(void)/*======================================*/{ dulint lsn;#ifdef UNIV_SYNC_DEBUG ut_ad(mutex_own(&(log_sys->mutex)));#endif /* UNIV_SYNC_DEBUG */ lsn = buf_pool_get_oldest_modification(); if (ut_dulint_is_zero(lsn)) { lsn = log_sys->lsn; } return(lsn);}/****************************************************************Opens the log for log_write_low. The log must be closed with log_close andreleased with log_release. */dulintlog_reserve_and_open(/*=================*/ /* out: start lsn of the log record */ ulint len) /* in: length of data to be catenated */{ log_t* log = log_sys; ulint len_upper_limit;#ifdef UNIV_LOG_ARCHIVE ulint archived_lsn_age; ulint dummy;#endif /* UNIV_LOG_ARCHIVE */#ifdef UNIV_DEBUG ulint count = 0;#endif /* UNIV_DEBUG */ ut_a(len < log->buf_size / 2);loop: mutex_enter(&(log->mutex)); /* Calculate an upper limit for the space the string may take in the log buffer */ len_upper_limit = LOG_BUF_WRITE_MARGIN + (5 * len) / 4; if (log->buf_free + len_upper_limit > log->buf_size) { mutex_exit(&(log->mutex)); /* Not enough free space, do a syncronous flush of the log buffer */ log_buffer_flush_to_disk(); srv_log_waits++; ut_ad(++count < 50); goto loop; }#ifdef UNIV_LOG_ARCHIVE if (log->archiving_state != LOG_ARCH_OFF) { archived_lsn_age = ut_dulint_minus(log->lsn, log->archived_lsn); if (archived_lsn_age + len_upper_limit > log->max_archived_lsn_age) { /* Not enough free archived space in log groups: do a synchronous archive write batch: */ mutex_exit(&(log->mutex)); ut_ad(len_upper_limit <= log->max_archived_lsn_age); log_archive_do(TRUE, &dummy); ut_ad(++count < 50); goto loop; } }#endif /* UNIV_LOG_ARCHIVE */#ifdef UNIV_LOG_DEBUG log->old_buf_free = log->buf_free; log->old_lsn = log->lsn;#endif return(log->lsn);}/****************************************************************Writes to the log the string given. It is assumed that the caller holds thelog mutex. */voidlog_write_low(/*==========*/ byte* str, /* in: string */ ulint str_len) /* in: string length */{ log_t* log = log_sys; ulint len; ulint data_len; byte* log_block;#ifdef UNIV_SYNC_DEBUG ut_ad(mutex_own(&(log->mutex)));#endif /* UNIV_SYNC_DEBUG */part_loop: /* Calculate a part length */ data_len = (log->buf_free % OS_FILE_LOG_BLOCK_SIZE) + str_len; if (data_len <= OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_TRL_SIZE) { /* The string fits within the current log block */ len = str_len; } else { data_len = OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_TRL_SIZE; len = OS_FILE_LOG_BLOCK_SIZE - (log->buf_free % OS_FILE_LOG_BLOCK_SIZE) - LOG_BLOCK_TRL_SIZE; } ut_memcpy(log->buf + log->buf_free, str, len); str_len -= len; str = str + len; log_block = ut_align_down(log->buf + log->buf_free, OS_FILE_LOG_BLOCK_SIZE); log_block_set_data_len(log_block, data_len); if (data_len == OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_TRL_SIZE) { /* This block became full */ log_block_set_data_len(log_block, OS_FILE_LOG_BLOCK_SIZE); log_block_set_checkpoint_no(log_block, log_sys->next_checkpoint_no); len += LOG_BLOCK_HDR_SIZE + LOG_BLOCK_TRL_SIZE; log->lsn = ut_dulint_add(log->lsn, len); /* Initialize the next block header */ log_block_init(log_block + OS_FILE_LOG_BLOCK_SIZE, log->lsn); } else { log->lsn = ut_dulint_add(log->lsn, len); } log->buf_free += len; ut_ad(log->buf_free <= log->buf_size); if (str_len > 0) { goto part_loop; } srv_log_write_requests++;}/****************************************************************Closes the log. */dulintlog_close(void)/*===========*/ /* out: lsn */{ byte* log_block; ulint first_rec_group; dulint oldest_lsn; dulint lsn; log_t* log = log_sys; ulint checkpoint_age;#ifdef UNIV_SYNC_DEBUG ut_ad(mutex_own(&(log->mutex)));#endif /* UNIV_SYNC_DEBUG */ lsn = log->lsn; log_block = ut_align_down(log->buf + log->buf_free, OS_FILE_LOG_BLOCK_SIZE); first_rec_group = log_block_get_first_rec_group(log_block); if (first_rec_group == 0) { /* We initialized a new log block which was not written full by the current mtr: the next mtr log record group will start within this block at the offset data_len */ log_block_set_first_rec_group(log_block, log_block_get_data_len(log_block)); } if (log->buf_free > log->max_buf_free) { log->check_flush_or_checkpoint = TRUE; } checkpoint_age = ut_dulint_minus(lsn, log->last_checkpoint_lsn); if (checkpoint_age >= log->log_group_capacity) { /* TODO: split btr_store_big_rec_extern_fields() into small steps so that we can release all latches in the middle, and call log_free_check() to ensure we never write over log written after the latest checkpoint. In principle, we should split all big_rec operations, but other operations are smaller. */ if (!log_has_printed_chkp_warning || difftime(time(NULL), log_last_warning_time) > 15) { log_has_printed_chkp_warning = TRUE; log_last_warning_time = time(NULL); ut_print_timestamp(stderr); fprintf(stderr," InnoDB: ERROR: the age of the last checkpoint is %lu,\n""InnoDB: which exceeds the log group capacity %lu.\n""InnoDB: If you are using big BLOB or TEXT rows, you must set the\n""InnoDB: combined size of log files at least 10 times bigger than the\n""InnoDB: largest such row.\n", (ulong) checkpoint_age, (ulong) log->log_group_capacity); } } if (checkpoint_age <= log->max_modified_age_async) { goto function_exit; } oldest_lsn = buf_pool_get_oldest_modification(); if (ut_dulint_is_zero(oldest_lsn) || (ut_dulint_minus(lsn, oldest_lsn) > log->max_modified_age_async) || checkpoint_age > log->max_checkpoint_age_async) { log->check_flush_or_checkpoint = TRUE; }function_exit:#ifdef UNIV_LOG_DEBUG log_check_log_recs(log->buf + log->old_buf_free, log->buf_free - log->old_buf_free, log->old_lsn);#endif return(lsn);}#ifdef UNIV_LOG_ARCHIVE/**********************************************************Pads the current log block full with dummy log records. Used in producingconsistent archived log files. */staticvoidlog_pad_current_log_block(void)/*===========================*/{ byte b = MLOG_DUMMY_RECORD; ulint pad_length; ulint i; dulint lsn; /* We retrieve lsn only because otherwise gcc crashed on HP-UX */ lsn = log_reserve_and_open(OS_FILE_LOG_BLOCK_SIZE); pad_length = OS_FILE_LOG_BLOCK_SIZE - (log_sys->buf_free % OS_FILE_LOG_BLOCK_SIZE) - LOG_BLOCK_TRL_SIZE; for (i = 0; i < pad_length; i++) { log_write_low(&b, 1); } lsn = log_sys->lsn; log_close(); log_release(); ut_a((ut_dulint_get_low(lsn) % OS_FILE_LOG_BLOCK_SIZE) == LOG_BLOCK_HDR_SIZE);}#endif /* UNIV_LOG_ARCHIVE *//**********************************************************Calculates the data capacity of a log group, when the log file headers are notincluded. */ulintlog_group_get_capacity(/*===================*/ /* out: capacity in bytes */ log_group_t* group) /* in: log group */{#ifdef UNIV_SYNC_DEBUG ut_ad(mutex_own(&(log_sys->mutex)));#endif /* UNIV_SYNC_DEBUG */ return((group->file_size - LOG_FILE_HDR_SIZE) * group->n_files); }/**********************************************************Calculates the offset within a log group, when the log file headers are notincluded. */UNIV_INLINEulintlog_group_calc_size_offset(/*=======================*/ /* out: size offset (<= offset) */ ulint offset, /* in: real offset within the log group */ log_group_t* group) /* in: log group */{#ifdef UNIV_SYNC_DEBUG ut_ad(mutex_own(&(log_sys->mutex)));#endif /* UNIV_SYNC_DEBUG */ return(offset - LOG_FILE_HDR_SIZE * (1 + offset / group->file_size));}/**********************************************************Calculates the offset within a log group, when the log file headers areincluded. */UNIV_INLINEulintlog_group_calc_real_offset(/*=======================*/ /* out: real offset (>= offset) */ ulint offset, /* in: size offset within the log group */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -