Table of Contents
前言
AOF流程
源码
Home Database Mysql Tutorial Redis源码学习-AOF

Redis源码学习-AOF

Jun 07, 2016 pm 04:02 PM
redis Preface study Source code network

前言 网络上也有许多介绍redis的AOF机制的文章,但是从宏观上介绍aof的流程,没有具体分析在AOF过程中涉及到的数据结构和控制机制。昨晚特别看了2.8源码,感觉源码中的许多细节是值得细细深究的。特别是list *aof_rewrite_buf_blocks结构。仔细看源码,会发

前言

网络上也有许多介绍redis的AOF机制的文章,但是从宏观上介绍aof的流程,没有具体分析在AOF过程中涉及到的数据结构和控制机制。昨晚特别看了2.8源码,感觉源码中的许多细节是值得细细深究的。特别是list *aof_rewrite_buf_blocks结构。仔细看源码,会发现原来看网络文章多的到的领会是片面的,最好的学习还是得自己动手...

原文链接: http://blog.csdn.net/ordeder/article/details/39271543

作者提及的AOF简化的流程为:
* 1) The user calls BGREWRITEAOF
* 2) Redis calls this function, that forks():
* 2a) the child rewrite the append only file in a temp file.
* 2b) the parent accumulates differences in server.aof_rewrite_buf.
* 3) When the child finished '2a' exists.
* 4) The parent will trap the exit code, if it's OK, will append the
* data accumulated into server.aof_rewrite_buf into the temp file, and
* finally will rename(2) the temp file in the actual file name.
* The the new file is reopened as the new append only file. Profit!

AOF流程

依据源码,AOF总体有一下操作:

主要函数:
//函数1:将command写入aof_buff
void feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv, int argc);
//函数2:启动子进程,子进程用于刷一遍redis中的数据
int rewriteAppendOnlyFileBackground(void);
//函数3:刷一遍server.db[16],依次将对象写入磁盘临时文件tmpfile
int rewriteAppendOnlyFile(char *filename);
//函数4:将aof_buff内容持久化
void flushAppendOnlyFile(int force);
//函数5:将server.aof_rewrite_buf_blocks中的内容写入tmpfile,并替换aof文件

void backgroundRewriteDoneHandler(exitcode,bysignal);

1 AOF日常命令append:
1.1. Redis执行文件事件:执行用户命令,并将该命令缓存于Server.aof_buf中{函数1}
1.2. Redis执行时间时间的ServerCron:依据参数server.aof_flush_postponed_start,{函数4}
1.2.1. 将redisServer.aof_buf写入文件Server.aof_fd。
1.2.2. 该文件何时fsync到磁盘有三种机制:
AOF_FSYNC_EVERYSEC 每秒调用fsync
AOF_FSYNC_ALWAYS 写文件后立即调用fsync
其他 听系统的

2 AOF日志简化操作:
2.1. Redis执行时间时间的ServerCron:{函数2-3}
2.1.1. 开启后台AOF进程,依据redis内存数据(redis.db[16]),生成可重建数据库的命令集,并写入tmpfile临时文件
2.2. Redis执行文件事件:
执行用户命令时,{函数1}
2.2.1. 将该命令缓存于redisServer.aof_buf;
2.2.2. 同时将该命令缓存于server.aof_rewrite_buf_blocks
2.3. Redis执行时间时间的ServerCron:
2.3.1 {函数4}在aof子进程还未结束期间,步骤 1.2 照常执行,将aof_buf写入aof_fd(该干嘛干嘛)
2.3.2 wait3发现aof子进程结束,那么:{函数5}
2.3.2.1 将server.aof_rewrite_buf_blocks中的内容写入tmpfile中
2.3.2.2 用tmpfile替换原有aof文件,并重置Server.aof_fd

函数和数据间关系如下图所示:

\

源码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

378

379

380

381

382

383

384

385

386

387

388

389

390

391

392

393

394

395

396

397

398

399

400

401

402

403

404

405

406

407

408

409

410

411

412

413

414

415

416

417

418

419

420

421

422

423

424

425

426

427

428

429

430

431

432

433

434

435

436

437

438

439

440

441

442

443

444

445

446

447

448

449

450

451

452

453

454

455

456

457

458

459

460

461

462

463

464

465

466

467

468

469

470

471

472

473

474

475

476

477

478

479

480

481

482

483

484

485

486

487

struct redisServer{

    ...

    /* AOF persistence */

    int aof_state;                  /* REDIS_AOF_(ON|OFF|WAIT_REWRITE) */

    int aof_fsync;                  /* Kind of fsync() policy (每个操作|每秒|缓冲区满)*/

    char *aof_filename;             /* Name of the AOF file */

    int aof_no_fsync_on_rewrite;    /* Don't fsync if a rewrite is in prog. */

    int aof_rewrite_perc;           /* Rewrite AOF if % growth is > M and... */

    off_t aof_rewrite_min_size;     /* the AOF file is at least N bytes. */

    off_t aof_rewrite_base_size;    /* AOF size on latest startup or rewrite. */

    off_t aof_current_size;         /* AOF current size. */

    int aof_rewrite_scheduled;      /* Rewrite once BGSAVE terminates. 是否需要开启后台aof子进程*/

    pid_t aof_child_pid;            /* PID if rewriting process */

    list *aof_rewrite_buf_blocks;   /* Hold changes during an AOF rewrite. 在aof bgsave期间redis执行的命令将存储到aof_rewrite_buf_blocks,当然aof_buf还是要照常使用的,二者不冲突*/

    sds aof_buf;      /* AOF buffer, written before entering the event loop */

    int aof_fd;       /* File descriptor of currently selected AOF file */

    int aof_selected_db; /* Currently selected DB in AOF */

    time_t aof_flush_postponed_start; /* UNIX time of postponed AOF flush */

    time_t aof_last_fsync;            /* UNIX time of last fsync() */

    time_t aof_rewrite_time_last;   /* Time used by last AOF rewrite run. */

    time_t aof_rewrite_time_start;  /* Current AOF rewrite start time. */

    int aof_lastbgrewrite_status;   /* REDIS_OK or REDIS_ERR */

    unsigned long aof_delayed_fsync;  /* delayed AOF fsync() counter */

    int aof_rewrite_incremental_fsync;/* fsync incrementally while rewriting? */

    ...

}

 

/////////////////////////////////////////////////////////////////////////////////

/* Call() is the core of Redis execution of a command */

void call(redisClient *c, int flags) {

    long long dirty, start = ustime(), duration;

    int client_old_flags = c->flags;

     

    ...

     

     /* 执行用户命令 */

    c->flags &= ~(REDIS_FORCE_AOF|REDIS_FORCE_REPL);

    redisOpArrayInit(&server.also_propagate);

    dirty = server.dirty;

    c->cmd->proc(c);

    dirty = server.dirty-dirty;

    duration = ustime()-start;

     

    ...

     

    /* 将用户命令进行AOF备份 */

    if (flags & REDIS_CALL_PROPAGATE) {

        int flags = REDIS_PROPAGATE_NONE;

 

        if (c->flags & REDIS_FORCE_REPL) flags |= REDIS_PROPAGATE_REPL;

        if (c->flags & REDIS_FORCE_AOF) flags |= REDIS_PROPAGATE_AOF;

        if (dirty)

            flags |= (REDIS_PROPAGATE_REPL | REDIS_PROPAGATE_AOF);

        if (flags != REDIS_PROPAGATE_NONE)

            propagate(c->cmd,c->db->id,c->argv,c->argc,flags);

    }

}

 

void propagate(struct redisCommand *cmd, int dbid, robj **argv, int argc,

               int flags)

{

    if (server.aof_state != REDIS_AOF_OFF && flags & REDIS_PROPAGATE_AOF)

        feedAppendOnlyFile(cmd,dbid,argv,argc);

    if (flags & REDIS_PROPAGATE_REPL)

        replicationFeedSlaves(server.slaves,dbid,argv,argc);

}

 

void feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv, int argc) {

    sds buf = sdsempty();

    robj *tmpargv[3];

 

    /* 如果当前操作的dict和前一次操作的dict不同,

    那么redis要在aof中添加一条:select命令,选择当前dict */

    if (dictid != server.aof_selected_db) {

        char seldb[64];

 

        snprintf(seldb,sizeof(seldb),"%d",dictid);

        buf = sdscatprintf(buf,"*2\r\n$6\r\nSELECT\r\n$%lu\r\n%s\r\n",

            (unsigned long)strlen(seldb),seldb);

        server.aof_selected_db = dictid;

    }

    //依据不同的命令,进行字符画处理,并将结果写入临时的buff中

    if (cmd->proc == expireCommand || cmd->proc == pexpireCommand ||

        cmd->proc == expireatCommand) {

        /* Translate EXPIRE/PEXPIRE/EXPIREAT into PEXPIREAT */

        buf = catAppendOnlyExpireAtCommand(buf,cmd,argv[1],argv[2]);

    } else if (cmd->proc == setexCommand || cmd->proc == psetexCommand) {

        /* Translate SETEX/PSETEX to SET and PEXPIREAT */

        tmpargv[0] = createStringObject("SET",3);

        tmpargv[1] = argv[1];

        tmpargv[2] = argv[3];

        buf = catAppendOnlyGenericCommand(buf,3,tmpargv);

        decrRefCount(tmpargv[0]);

        buf = catAppendOnlyExpireAtCommand(buf,cmd,argv[1],argv[2]);

    } else {

        /* All the other commands don't need translation or need the

         * same translation already operated in the command vector

         * for the replication itself. */

        buf = catAppendOnlyGenericCommand(buf,argc,argv);

    }

 

    /* Append to the AOF buffer. This will be flushed on disk just before

     * of re-entering the event loop, so before the client will get a

     * positive reply about the operation performed. */

    //如果用户开启的AOF,那么将当前命令的buff Append到server.aof_buf缓冲的尾部

    if (server.aof_state == REDIS_AOF_ON)

        server.aof_buf = sdscatlen(server.aof_buf,buf,sdslen(buf));

 

    /* If a background append only file rewriting is in progress we want to

     * accumulate the differences between the child DB and the current one

     * in a buffer, so that when the child process will do its work we

     * can append the differences to the new append only file. */

     //如果当前有子进程正在进行AOF日志的重构(即扫描redis数据库,依据数据构建日志)

     //那么将当前命令的buff添加到server.aof_rewrite_buf_blocks内存中(该部分内存

     //专门记录在重构AOF期间redis处理的操作)

    if (server.aof_child_pid != -1)

        aofRewriteBufferAppend((unsigned char*)buf,sdslen(buf));

 

    sdsfree(buf);

}

 

////////////////////////////////////////////////////////////////////////////////////////

 

int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {

    int j;

    REDIS_NOTUSED(eventLoop);

    REDIS_NOTUSED(id);

    REDIS_NOTUSED(clientData);

 

    /* Software watchdog: deliver the SIGALRM that will reach the signal

     * handler if we don't return here fast enough. */

    if (server.watchdog_period) watchdogScheduleSignal(server.watchdog_period);

 

    /* We take a cached value of the unix time in the global state because

     * with virtual memory and aging there is to store the current time

     * in objects at every object access, and accuracy is not needed.

     * To access a global var is faster than calling time(NULL) */

     //缓存系统时间...

    server.unixtime = time(NULL);

    server.mstime = mstime();

 

    ...

 

    /* Start a scheduled AOF rewrite if this was requested by the user while

     * a BGSAVE was in progress. */

     //开启AOF日志重建的子进程(简化日志)

     //后台AOF子进程通过扫描redis.db[16]数据,生成可重建当前数据库的命令,

     //并写入临时文件tmpfile

    if (server.rdb_child_pid == -1 && server.aof_child_pid == -1 &&

        server.aof_rewrite_scheduled)

    {

        //AOF

        rewriteAppendOnlyFileBackground();

    }

 

    /* Check if a background saving or AOF rewrite in progress terminated. */

    //后台AOF进程结束:将在后台AOF子进程构建AOF日志期间redis执行的新命令

    //(记录于server.aof_rewrite_buf_blocks)append 到后台子进程构建的tmpfile中

    //最后将tmpfile重名为server.aof_filename 替换原有AOF

    if (server.rdb_child_pid != -1 || server.aof_child_pid != -1) {

        int statloc;

        pid_t pid;

 

        if ((pid = wait3(&statloc,WNOHANG,NULL)) != 0) {

            int exitcode = WEXITSTATUS(statloc);

            int bysignal = 0;

             

            if (WIFSIGNALED(statloc)) bysignal = WTERMSIG(statloc);

 

            if (pid == server.rdb_child_pid) {

                backgroundSaveDoneHandler(exitcode,bysignal);

            } else if (pid == server.aof_child_pid) {

                backgroundRewriteDoneHandler(exitcode,bysignal);

            } else {

                redisLog(REDIS_WARNING,

                    "Warning, detected child with unmatched pid: %ld",

                    (long)pid);

            }

            updateDictResizePolicy();

        }

    } else {

        /* If there is not a background saving/rewrite in progress check if

         * we have to save/rewrite now */

         //没有后台子进程在跑,那么检查是否要开启一个AOF或者RDB的子进程。。。

         ...

    }

 

    /* If we postponed an AOF buffer flush, let's try to do it every time the

     * cron function is called. */

     //将server.aof_buf(缓存redis最近执行过的命名)flush到磁盘AOF文件中

     //flush的策略有如下:

     //每个操作,调用fync将命令持久化

     //间隔1秒,调用fync将aof_buf持久化

     //从不调用fync,由系统自行安排时机

    if (server.aof_flush_postponed_start) flushAppendOnlyFile(0);

 

    ...

 

    server.cronloops++;

    return 1000/server.hz;

}

 

/* This is how rewriting of the append only file in background works:

 *

 * 1) The user calls BGREWRITEAOF

 * 2) Redis calls this function, that forks():

 *    2a) the child rewrite the append only file in a temp file.

 *    2b) the parent accumulates differences in server.aof_rewrite_buf.

 * 3) When the child finished '2a' exists.

 * 4) The parent will trap the exit code, if it's OK, will append the

 *    data accumulated into server.aof_rewrite_buf into the temp file, and

 *    finally will rename(2) the temp file in the actual file name.

 *    The the new file is reopened as the new append only file. Profit!

 */

int rewriteAppendOnlyFileBackground(void) {

    pid_t childpid;

    long long start;

 

    if (server.aof_child_pid != -1) return REDIS_ERR;

    start = ustime();

    if ((childpid = fork()) == 0) {

        char tmpfile[256];

 

        /* Child */

        closeListeningSockets(0);

        redisSetProcTitle("redis-aof-rewrite");

        snprintf(tmpfile,256,"temp-rewriteaof-bg-%d.aof", (int) getpid());

        if (rewriteAppendOnlyFile(tmpfile) == REDIS_OK) {

            size_t private_dirty = zmalloc_get_private_dirty();

 

            if (private_dirty) {

                redisLog(REDIS_NOTICE,

                    "AOF rewrite: %zu MB of memory used by copy-on-write",

                    private_dirty/(1024*1024));

            }

            exitFromChild(0);

        } else {

            exitFromChild(1);

        }

    } else {

        /* Parent */

        server.stat_fork_time = ustime()-start;

        if (childpid == -1) {

            redisLog(REDIS_WARNING,

                "Can't rewrite append only file in background: fork: %s",

                strerror(errno));

            return REDIS_ERR;

        }

        redisLog(REDIS_NOTICE,

            "Background append only file rewriting started by pid %d",childpid);

        server.aof_rewrite_scheduled = 0;

        server.aof_rewrite_time_start = time(NULL);

        server.aof_child_pid = childpid;

        updateDictResizePolicy();

        /* We set appendseldb to -1 in order to force the next call to the

         * feedAppendOnlyFile() to issue a SELECT command, so the differences

         * accumulated by the parent into server.aof_rewrite_buf will start

         * with a SELECT statement and it will be safe to merge. */

        server.aof_selected_db = -1;

        replicationScriptCacheFlush();

        return REDIS_OK;

    }

    return REDIS_OK; /* unreached */

}

 

/* Write a sequence of commands able to fully rebuild the dataset into

 * "filename". Used both by REWRITEAOF and BGREWRITEAOF.

 *

 * In order to minimize the number of commands needed in the rewritten

 * log Redis uses variadic commands when possible, such as RPUSH, SADD

 * and ZADD. However at max REDIS_AOF_REWRITE_ITEMS_PER_CMD items per time

 * are inserted using a single command. */

int rewriteAppendOnlyFile(char *filename) {

    dictIterator *di = NULL;

    dictEntry *de;

    rio aof;

    FILE *fp;

    char tmpfile[256];

    int j;

    long long now = mstime();

 

    /* Note that we have to use a different temp name here compared to the

     * one used by rewriteAppendOnlyFileBackground() function. */

    snprintf(tmpfile,256,"temp-rewriteaof-%d.aof", (int) getpid());

    fp = fopen(tmpfile,"w");

    if (!fp) {

        redisLog(REDIS_WARNING, "Opening the temp file for AOF rewrite in rewriteAppendOnlyFile(): %s", strerror(errno));

        return REDIS_ERR;

    }

 

    rioInitWithFile(&aof,fp);

    if (server.aof_rewrite_incremental_fsync)

        rioSetAutoSync(&aof,REDIS_AOF_AUTOSYNC_BYTES);

    for (j = 0; j < server.dbnum; j++) {

        //添加一条定位dict的命令

        char selectcmd[] = "*2\r\n$6\r\nSELECT\r\n";

        redisDb *db = server.db+j;

        dict *d = db->dict;

        if (dictSize(d) == 0) continue;

        di = dictGetSafeIterator(d);

        if (!di) {

            fclose(fp);

            return REDIS_ERR;

        }

 

        /* SELECT the new DB */

        if (rioWrite(&aof,selectcmd,sizeof(selectcmd)-1) == 0) goto werr;

        if (rioWriteBulkLongLong(&aof,j) == 0) goto werr;

 

        /* Iterate this DB writing every entry */

        while((de = dictNext(di)) != NULL) {

            sds keystr;

            robj key, *o;

            long long expiretime;

 

            keystr = dictGetKey(de);

            o = dictGetVal(de);

            initStaticStringObject(key,keystr);

 

            expiretime = getExpire(db,&key);

 

            /* If this key is already expired skip it */

            if (expiretime != -1 && expiretime < now) continue;

 

            /* Save the key and associated value */

            if (o->type == REDIS_STRING) {

                /* Emit a SET command */

                char cmd[]="*3\r\n$3\r\nSET\r\n";

                if (rioWrite(&aof,cmd,sizeof(cmd)-1) == 0) goto werr;

                /* Key and value */

                if (rioWriteBulkObject(&aof,&key) == 0) goto werr;

                if (rioWriteBulkObject(&aof,o) == 0) goto werr;

            } else if (o->type == REDIS_LIST) {

                if (rewriteListObject(&aof,&key,o) == 0) goto werr;

            } else if (o->type == REDIS_SET) {

                if (rewriteSetObject(&aof,&key,o) == 0) goto werr;

            } else if (o->type == REDIS_ZSET) {

                if (rewriteSortedSetObject(&aof,&key,o) == 0) goto werr;

            } else if (o->type == REDIS_HASH) {

                if (rewriteHashObject(&aof,&key,o) == 0) goto werr;

            } else {

                redisPanic("Unknown object type");

            }

            /* Save the expire time */

            if (expiretime != -1) {

                char cmd[]="*3\r\n$9\r\nPEXPIREAT\r\n";

                if (rioWrite(&aof,cmd,sizeof(cmd)-1) == 0) goto werr;

                if (rioWriteBulkObject(&aof,&key) == 0) goto werr;

                if (rioWriteBulkLongLong(&aof,expiretime) == 0) goto werr;

            }

        }

        dictReleaseIterator(di);

    }

 

    /* Make sure data will not remain on the OS&#39;s output buffers */

    fflush(fp);

    aof_fsync(fileno(fp));

    fclose(fp);

 

    /* Use RENAME to make sure the DB file is changed atomically only

     * if the generate DB file is ok. */

    if (rename(tmpfile,filename) == -1) {

        redisLog(REDIS_WARNING,"Error moving temp append only file on the final destination: %s", strerror(errno));

        unlink(tmpfile);

        return REDIS_ERR;

    }

    redisLog(REDIS_NOTICE,"SYNC append only file rewrite performed");

    return REDIS_OK;

 

werr:

    fclose(fp);

    unlink(tmpfile);

    redisLog(REDIS_WARNING,"Write error writing append only file on disk: %s", strerror(errno));

    if (di) dictReleaseIterator(di);

    return REDIS_ERR;

}

 

/* Write the append only file buffer on disk.

 *

 * Since we are required to write the AOF before replying to the client,

 * and the only way the client socket can get a write is entering when the

 * the event loop, we accumulate all the AOF writes in a memory

 * buffer and write it on disk using this function just before entering

 * the event loop again.

 *

 * About the &#39;force&#39; argument:

 *

 * When the fsync policy is set to &#39;everysec&#39; we may delay the flush if there

 * is still an fsync() going on in the background thread, since for instance

 * on Linux write(2) will be blocked by the background fsync anyway.

 * When this happens we remember that there is some aof buffer to be

 * flushed ASAP, and will try to do that in the serverCron() function.

 *

 * However if force is set to 1 we&#39;ll write regardless of the background

 * fsync. */

void flushAppendOnlyFile(int force) {

    ssize_t nwritten;

    int sync_in_progress = 0;

 

    if (sdslen(server.aof_buf) == 0) return;

 

    if (server.aof_fsync == AOF_FSYNC_EVERYSEC)

        sync_in_progress = bioPendingJobsOfType(REDIS_BIO_AOF_FSYNC) != 0;

 

    //判定是否该开始将server.aof_buff中缓存的命令flush到server.aof_fd文件的写缓冲中

    if (server.aof_fsync == AOF_FSYNC_EVERYSEC && !force) {

        /* With this append fsync policy we do background fsyncing.

         * If the fsync is still in progress we can try to delay

         * the write for a couple of seconds. */

        if (sync_in_progress) {

            if (server.aof_flush_postponed_start == 0) {

                /* No previous write postponinig, remember that we are

                 * postponing the flush and return. */

                server.aof_flush_postponed_start = server.unixtime;

                return;

            } else if (server.unixtime - server.aof_flush_postponed_start < 2) {

                /* We were already waiting for fsync to finish, but for less

                 * than two seconds this is still ok. Postpone again. */

                return;

            }

            /* Otherwise fall trough, and go write since we can&#39;t wait

             * over two seconds. */

            server.aof_delayed_fsync++;

            redisLog(REDIS_NOTICE,"Asynchronous AOF fsync is taking too long (disk is busy?). Writing the AOF buffer without waiting for fsync to complete, this may slow down Redis.");

        }

    }

    /* If you are following this code path, then we are going to write so

     * set reset the postponed flush sentinel to zero. */

    server.aof_flush_postponed_start = 0;  

 

    /* We want to perform a single write. This should be guaranteed atomic

     * at least if the filesystem we are writing is a real physical one.

     * While this will save us against the server being killed I don&#39;t think

     * there is much to do about the whole server stopping for power problems

     * or alike */

     //将redis最近执行的一些命令(存于server.aof_buf)写入文件(server.aof_fd)

     //注意,写入文件并不能保证马上写入磁盘,因为这是带缓冲的写。关于何时将

     //文件写缓冲中的命令fync到磁盘,这就要看用户的设置:(见下文)

    nwritten = write(server.aof_fd,server.aof_buf,sdslen(server.aof_buf));

    if (nwritten != (signed)sdslen(server.aof_buf)) {

        /* Ooops, we are in troubles. The best thing to do for now is

         * aborting instead of giving the illusion that everything is

         * working as expected. */

        ...

        exit(1);

    }

    server.aof_current_size += nwritten;   

 

    /* Re-use AOF buffer when it is small enough. The maximum comes from the

     * arena size of 4k minus some overhead (but is otherwise arbitrary). */

    if ((sdslen(server.aof_buf)+sdsavail(server.aof_buf)) < 4000) {

        sdsclear(server.aof_buf);

    } else {

        sdsfree(server.aof_buf);

        server.aof_buf = sdsempty();

    }

 

     //aof_no_fsync_on_rewrite : 该标志位表示当有aof或rdb子进程时,不进行fsync操作

    if (server.aof_no_fsync_on_rewrite &&

        (server.aof_child_pid != -1 || server.rdb_child_pid != -1))

            return;

 

    //fsync...

    //每个操作,调用fync将命令持久化 [1]

    //间隔1秒,调用fync将aof_buf持久化 [2]

    //从不调用fync,由系统自行安排时机(fd的写缓冲区满了)[3]

     

    //【1】

    //每个操作都需要将文件缓冲区的写 buff sync到磁盘。从而保证每个redis操作在

    //被redis执行后,都能马上持久化,安全性很高,就是磁盘写的系统开销有点大大

    if (server.aof_fsync == AOF_FSYNC_ALWAYS) {

        /* aof_fsync is defined as fdatasync() for Linux in order to avoid

         * flushing metadata. */

        aof_fsync(server.aof_fd); /* Let&#39;s try to get this data on the disk */

        server.aof_last_fsync = server.unixtime;

    }

    //【2】

    //每隔1s将文件缓冲区的写缓冲区sync到磁盘

    else if ((server.aof_fsync == AOF_FSYNC_EVERYSEC &&

                server.unixtime > server.aof_last_fsync)) {

        if (!sync_in_progress) aof_background_fsync(server.aof_fd);

        server.aof_last_fsync = server.unixtime;

    }

     

    //【3】

    //else fd的写缓冲满后会由系统安排执行(听天由命)

}

Copy after login
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

AI Hentai Generator

AI Hentai Generator

Generate AI Hentai for free.

Hot Article

R.E.P.O. Energy Crystals Explained and What They Do (Yellow Crystal)
2 weeks ago By 尊渡假赌尊渡假赌尊渡假赌
Repo: How To Revive Teammates
4 weeks ago By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island Adventure: How To Get Giant Seeds
4 weeks ago By 尊渡假赌尊渡假赌尊渡假赌

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

Solution to 0x80242008 error when installing Windows 11 10.0.22000.100 Solution to 0x80242008 error when installing Windows 11 10.0.22000.100 May 08, 2024 pm 03:50 PM

1. Start the [Start] menu, enter [cmd], right-click [Command Prompt], and select Run as [Administrator]. 2. Enter the following commands in sequence (copy and paste carefully): SCconfigwuauservstart=auto, press Enter SCconfigbitsstart=auto, press Enter SCconfigcryptsvcstart=auto, press Enter SCconfigtrustedinstallerstart=auto, press Enter SCconfigwuauservtype=share, press Enter netstopwuauserv , press enter netstopcryptS

Golang API caching strategy and optimization Golang API caching strategy and optimization May 07, 2024 pm 02:12 PM

The caching strategy in GolangAPI can improve performance and reduce server load. Commonly used strategies are: LRU, LFU, FIFO and TTL. Optimization techniques include selecting appropriate cache storage, hierarchical caching, invalidation management, and monitoring and tuning. In the practical case, the LRU cache is used to optimize the API for obtaining user information from the database. The data can be quickly retrieved from the cache. Otherwise, the cache can be updated after obtaining it from the database.

Caching mechanism and application practice in PHP development Caching mechanism and application practice in PHP development May 09, 2024 pm 01:30 PM

In PHP development, the caching mechanism improves performance by temporarily storing frequently accessed data in memory or disk, thereby reducing the number of database accesses. Cache types mainly include memory, file and database cache. Caching can be implemented in PHP using built-in functions or third-party libraries, such as cache_get() and Memcache. Common practical applications include caching database query results to optimize query performance and caching page output to speed up rendering. The caching mechanism effectively improves website response speed, enhances user experience and reduces server load.

How to use Redis cache in PHP array pagination? How to use Redis cache in PHP array pagination? May 01, 2024 am 10:48 AM

Using Redis cache can greatly optimize the performance of PHP array paging. This can be achieved through the following steps: Install the Redis client. Connect to the Redis server. Create cache data and store each page of data into a Redis hash with the key "page:{page_number}". Get data from cache and avoid expensive operations on large arrays.

How to upgrade Win11 English 21996 to Simplified Chinese 22000_How to upgrade Win11 English 21996 to Simplified Chinese 22000 How to upgrade Win11 English 21996 to Simplified Chinese 22000_How to upgrade Win11 English 21996 to Simplified Chinese 22000 May 08, 2024 pm 05:10 PM

First you need to set the system language to Simplified Chinese display and restart. Of course, if you have changed the display language to Simplified Chinese before, you can just skip this step. Next, start operating the registry, regedit.exe, directly navigate to HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlNlsLanguage in the left navigation bar or the upper address bar, and then modify the InstallLanguage key value and Default key value to 0804 (if you want to change it to English en-us, you need First set the system display language to en-us, restart the system and then change everything to 0409) You must restart the system at this point.

A comprehensive guide to learning and applying golang framework source code A comprehensive guide to learning and applying golang framework source code Jun 01, 2024 pm 10:31 PM

By understanding the Golang framework source code, developers can master the essence of the language and expand the framework's functions. First, get the source code and become familiar with its directory structure. Second, read the code, trace the execution flow, and understand dependencies. Practical examples show how to apply this knowledge: create custom middleware and extend the routing system. Best practices include learning step-by-step, avoiding mindless copy-pasting, utilizing tools, and referring to online resources.

How to find the update file downloaded by Win11_Share the location of the update file downloaded by Win11 How to find the update file downloaded by Win11_Share the location of the update file downloaded by Win11 May 08, 2024 am 10:34 AM

1. First, double-click the [This PC] icon on the desktop to open it. 2. Then double-click the left mouse button to enter [C drive]. System files will generally be automatically stored in C drive. 3. Then find the [windows] folder in the C drive and double-click to enter. 4. After entering the [windows] folder, find the [SoftwareDistribution] folder. 5. After entering, find the [download] folder, which contains all win11 download and update files. 6. If we want to delete these files, just delete them directly in this folder.

Golang framework source code analysis and optimization Golang framework source code analysis and optimization Jun 02, 2024 pm 04:54 PM

This article explores the source code analysis and optimization of the Go framework. The source code structure includes four main packages, involving core framework logic, request context, data binding and response rendering. Optimization techniques include: 1. Use routing trees to optimize route processing to significantly increase route lookup speed. 2. Use middleware for caching and compression to reduce server load and response time. 3. Avoid performing time-consuming operations in callbacks to maintain high responsiveness. 4. Enable logging and analyze slow requests to identify performance bottlenecks. 5. Update framework versions regularly to take advantage of the latest performance improvements.

See all articles