初識 undo類型日誌系統

來源:互聯網
上載者:User

初識 undo類型日誌系統

日誌系統是保證資料庫管理系統正確執行事務的基本機制。根據作用的不同,日誌系統分為undo和redo兩種,本文對undo類型日誌的原理進行簡單類比說明。

1 UNDO日誌要求
  • 日誌記錄了資料修改之前的舊值;
  • 資料刷盤之前,把日誌刷盤;(一致性)
  • 資料刷盤之後,把日誌COMMIT刷盤。(持久性)
2 UNDO日誌缺陷

UNDO日誌提供了足夠的資訊可以保證事務的一致性和持久性。但是,為了保持一致性,採取的是被動保守的策略,即:用舊值覆蓋不能確保成功的事務。未成功的事務不能重新執行,只能恢複到事務之前的一致狀態。

2 類比代碼

只是類比了資料寫入的過程,沒有類比資料恢複過程,待以後有時間補充。

/* UNDO 類型日誌基本流程類比 */#include <stdio.h>#include <string.h>#include <stdlib.h>#include <err.h>/* 記錄檔,基本格式: * T1_START * A=100 * B=100 * T1_COMMIT */#define LOG_FILE "test.log"/* 資料檔案,基本格式: 每行一個索引值對,長度固定為1024,右側用空格填充。  * A=100     (padding) * B=100     (padding) */#define DATA_FILE "test.data"#define LINE_MAX 1024/* 索引值對 */typedef struct KV KV;struct KV{    char* key;    char* value;};/* 從硬碟讀取名為k的資料 */static int fread_kv(char* k, char* v, FILE* fp){    rewind(fp);    char line[LINE_MAX+1]={0};    int lineNo = 0;    while(fread(line, 1, LINE_MAX, fp)==LINE_MAX){        int i=0;        while(k[i] && line[i]!='='){            if(k[i] != line[i]){                break;            }            i++;        }        if(line[i] == '='){            strcpy(v, &line[i+1]);            return lineNo;                                                                                                                                                                    1,1           Top        }        lineNo ++;    }    return -1;}/* 把資料刷入硬碟 */static void fwrite_kv( char* k,  char* v, FILE* fp){    int lineNo = -1;    char newLine[LINE_MAX];    sprintf(newLine, "%s=%s", k, v);    int offset = 0;    if( (lineNo=fread_kv(k,v,fp))<0)  /* insert */    {        offset = fseek(fp, 0, SEEK_END);    }else{  /* update */        offset = fseek(fp, LINE_MAX * lineNo, 0);    }    fprintf(fp, "%-1023s\n", newLine);    fflush(fp);}int main(int argc, char** argv){    FILE* fpLog = fopen(LOG_FILE, "r+");    FILE* fpData = fopen(DATA_FILE, "r+");    if(!(fpLog && fpData)){        perror(NULL);    }    /* 開始一個事務 */    char log[1024] = "T1 START\n";    /* 在記憶體中執行事務操作 */    char v[LINE_MAX];    fread_kv("A", v, fpData);    int A = atoi(v);    fread_kv("B", v, fpData);    int B = atoi(v);    char logA[1024];    sprintf(logA, "T1:A=%d\n", A); /*在日誌中記錄舊值*/    strcat(log, logA);    A -= 50;    char logB[1024];    sprintf(logB, "T1:B=%d\n", B); /*在日誌中記錄舊值*/    strcat(log, logB);    B += 50;    /************** 如果此時發生故障,日誌和資料均尚未寫出到硬碟上, 事務丟失,但保持資料庫一致性.*************/    /* 資料刷盤之前,先把日誌刷入硬碟 */    fputs(log, fpLog);    fflush(fpLog);    /************* 如果此時發生故障,日誌舊值已經被寫出到硬碟上,資料尚未寫入,恢複時需要把舊值恢複.(隨然資料未刷盤,但並不可知)********/    /* 把資料新值刷入硬碟 */    sprintf(v, "%d", A);    fwrite_kv("A", v, fpData);    /************* 如果此時發生故障,日誌舊值已經被寫出到硬碟上,資料尚未寫入,恢複時需要把舊值恢複.********/    abort(); /* 類比故障 */    sprintf(v, "%d", B);    fwrite_kv("B", v, fpData);    /************* 如果此時發生故障,日誌舊值已經被寫出到硬碟上,資料已經寫入硬碟,但是COMMIT日誌未寫出,也需要恢複舊值.(雖然資料已完整刷盤,但並不可知)********/    /* 資料刷盤之後,把提交日誌刷入硬碟*/    fputs("T1 COMMIT\n", fpLog);    fflush(fpLog);    /************* 如果此時發生故障,日誌COMMIT已刷盤,能夠確保資料也已經刷盤成功。無需恢複********/    fclose(fpLog);    fclose(fpData);    return 0;}

代碼執行之前:
資料檔案內容如下:

A=100B=100

上述代碼執行後:
資料檔案內容如下:

A=50B=100

記錄檔內容如下:

T1 STARTT1:A=100T1:B=100

根據記錄檔,由於沒有找到T1 COMMIT,所以斷定事務T1未能成功,資料可能處於不一致狀態,需要資料恢複。進而根據記錄檔,可以得到事務T1執行之前的資料舊值A=100,B=100,恢複也就很容易了,只要把A,B的值都更新為其對應的舊值就可以了。

恢複之後的資料檔案:

A=100B=100

相關文章

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.