BIO即background I/O service,後臺I/O服務,是redis的aof持久化後臺服務。
redis把阻塞的同步I/O操做交給後臺I/O服務來完成:close和fsync。
close加入BIO的緣由
1.若是fd是特定文件描述符的最後一份拷貝,那麼文件描述符相關的資源會被釋放。
2.若是fd是最後一個引用文件描述符的,而且文件描述符以前已經使用unlink進行刪除,那麼文件會被刪除。
資源釋放和文件刪除是很是慢的,會阻塞服務器
fsync加入BIO的緣由
把內存中修改的文件數據同步到磁盤。調用者將被阻塞至磁盤報告同步完成。
BIO的設計
目前有兩種任務:fsync和close。每種任務一個隊列和一個線程。
// 存放工做的隊列
static list *bio_jobs[REDIS_BIO_NUM_OPS];
// 記錄每種類型 job 隊列裏有多少 job 等待執行
static unsigned long long bio_pending[REDIS_BIO_NUM_OPS];
//初始化後臺服務,啓動後臺線程
//建立後臺任務
void bioCreateBackgroundJob(int type, void *arg1, void *arg2, void *arg3)
//處理後臺任務,後臺線程啓動函數
void *bioProcessBackgroundJobs(void *arg)
{
//當任務數爲0的時候,調用pthread_cond_wait等待通知
//任務數不爲0的時候,從隊列取任務並處理
// 執行任務,type即線程啓動的入參,每一個後臺線程處理一種任務
if (type == REDIS_BIO_CLOSE_FILE) {
close((long)job->arg1);
} else if (type == REDIS_BIO_AOF_FSYNC) {
aof_fsync((long)job->arg1);
} else {
redisPanic("Wrong job type in bioProcessBackgroundJobs().");
}
}
BIO的結構
BIO技術應用和優化
aof有三種持久化方案
1。內核同步,交給內核去緩存的數據到磁盤,大約30s一次。
2.每秒同步,這是做者推薦的同步方案,和內核同步幾乎同樣快。
3.每次都同步,性能極差,做者都巴不得刪掉這個功能。
fsync放在單獨線程處理存在的問題
fsync線程的使用和主線程的write存在衝突,在fsync進行的時候,write將被阻塞,fsync期間write不能執行,直到fsync完成。
redis中實現了一種優化
部分代碼
void flushAppendOnlyFile(int force) {
// 策略爲每秒 FSYNC
if (server.aof_fsync == AOF_FSYNC_EVERYSEC)
// 是否有 SYNC 正在後臺進行?
sync_in_progress = bioPendingJobsOfType(REDIS_BIO_AOF_FSYNC) != 0;
// 每秒 fsync ,而且強制寫入爲假
if (server.aof_fsync == AOF_FSYNC_EVERYSEC && !force) {
* 若是後臺仍在執行 FSYNC ,那麼咱們能夠延遲寫操做一兩秒
* (若是強制執行 write 的話,服務器主線程將阻塞在 write 上面)
*/
if (sync_in_progress) {
// 有 fsync 正在後臺進行 。。。
* 若是後臺還有 fsync 在執行,而且 write 已經推遲 >= 2 秒
* 那麼執行寫操做(write 將被阻塞)
*/
}
//write寫入緩衝區
nwritten = write(server.aof_fd,server.aof_buf,sdslen(server.aof_buf));
優化方案
當aof同步方式爲每秒同步時,主線程的write進行優化,若是執行write時fsync服務正在同步中,那麼推遲write的時間,最多推遲兩秒,若是等待兩秒後fsync仍在同步,執行強制write操做,主線程將阻塞等待write完成。
fsync服務線程和bgsave以及bgrewriteaof子進程的衝突
這時候不該該執行fsync
if (server.aof_no_fsync_on_rewrite &&
(server.aof_child_pid != -1 || server.rdb_child_pid != -1))
return;
做者關於fsync服務線程的博客