1. 程式人生 > >雲原生資料庫mysql對共享儲存分散式檔案系統的介面需求分析【原創】

雲原生資料庫mysql對共享儲存分散式檔案系統的介面需求分析【原創】

1. 引言

  雲原生資料庫跟分散式mpp資料庫是有差異的,雖然兩者都是計算與儲存分離,但是在資源的佔用上有所不同。雲原生資料庫是shard everything架構,其依賴的儲存資源、記憶體資源、事務資源在雲中都是共享、彈性伸縮的。由分散式檔案系統提供按需分配、租戶隔離的塊儲存,由分散式記憶體池提供buffer pool佔用的大塊記憶體。分散式mpp資料庫則是shard nothing架構,其依賴的儲存資源、記憶體資源、事務資源是單個物理節點上的資源,在SQL計算層進行了分散式計算邏輯的分發。

  本文重點介紹共享儲存,如果分散式檔案系統的iops、每秒刷盤數能夠比單個物理節點上的效能線性提升,那麼雲原生資料庫mysql的tps也會隨之提升,而且mysql的原生SQL語法都是支援的,包括巢狀子查詢、儲存過程、函式等。分散式mpp在SQL計算層做分散式計算邏輯的分發,這些可能會被裁減掉。

  單機mysql的事務型儲存引擎innodb的表空間資料儲存依賴於單個linux節點的VFS提供的posix標準的檔案操作介面。VFS作為各個具體檔案系統(ext4、xfs、ext3等)的抽象層,遮蔽了各個具體檔案系統的實現差異,對應用層提供了統一、標準的posix檔案操作介面。類似於阿里雲的polardb對polarfs的依賴,其實polardb就是mysql的核心原始碼的二次開發而成的。本文重點羅列雲原生資料庫mysql在各個場景下對posix檔案操作介面需求。

  分散式彈性檔案系統要具體地實現這些介面,各個介面的語義要完全符合posix標準。並提供mount掛載的功能,將其實現的具體介面註冊到VFS內部,mysql將表空間放置到掛載了分散式彈性檔案系統的路徑下,innodb內部對錶空間檔案操作時候,實際上就呼叫了分散式檔案系統提供的檔案操作的api。

  innodb的表空間有使用者表空間、系統表空間、Redo日誌、Undo表空間。本文重點分析使用者表空間對檔案操作介面的需求,應該也涵蓋了其餘的表空間對檔案操作介面的需求。

  使用者對使用者表空間的操作主要有兩類,一類是表空間資料的讀寫,另一類是表空間DDL操作,對應到posix標準的檔案操作介面,一類是檔案資料讀寫IO,另一類是檔案的元資料操作。

 

2. 表空間資料的讀寫操作

2.1 同步IO 

  同步IO會阻塞呼叫執行緒,直到IO完成,呼叫執行緒才返回,pwrite/pread函式是執行緒安全的同步IO,lseek+read/write函式非執行緒安全,需要加互斥鎖,併發量大的時候,對效能有一定的影響。以下幾種場景下,會使用同步IO。

場景一. linux不支援native aio, Page cleaner執行緒刷髒,同步寫

  從buffer pool中刷髒頁時候,如果不支援native aio,則通過simulated aio模擬非同步寫進行dirty page的寫入表空間操作,本質上是同步寫

   呼叫棧:

1 buf_flush_page_cleaner_worker → pc_flush_slot → buf_flush_do_batch →  buf_do_flush_list_batch → buf_flush_page_and_try_neighbors → buf_flush_try_neighbors → buf_flush_page  → buf_flush_write_block_low → fil_io(sync=false) → os_aio → os_aio_func →AIO::wake_simulated_handler_thread

場景二. 刷髒時,如果double write buffer寫滿,將double write buffer中資料寫入系統表空間ibdata檔案,同步寫

  呼叫棧:

buf_flush_page → buf_dblwr_flush_buffered_writes → fil_io(sync=true) 

場景三. 事務buffer中資料寫入Redo log file,同步寫

  呼叫棧:

1 innobase_commit_low → trx_commit_for_mysql → trx_commit → trx_commit_in_memory → trx_flush_log_if_needed_low → log_write_up_to → log_group_write_buf → log_group_write_buf → fil_io(sync=true) 

場景四,使用者執行緒觸發的資料塊請求讀寫,同步讀寫

  呼叫棧:

1 ha_innobase::index_read → row_search_mvcc →  row_sel_get_clust_rec_for_mysql→  buf_page_get_gen → buf_read_page → buf_read_page_low → fil_io(sync=true) 

 

2.2非同步IO

  非同步IO不會阻塞呼叫執行緒,提交IO請求後,呼叫執行緒就返回,可以做其餘的操作,後臺執行緒會輪詢IO的完成情況,如果執行完成可以呼叫相關的回撥函式。

在支援native aio的情況下,innodb的後臺 Page cleaner執行緒刷髒,預讀走的就是非同步IO流程,主要以下兩個場景。

場景一. linux支援native aio ,Page cleaner執行緒刷髒,非同步寫

  從buffer pool中刷髒頁時候,如果支援native aio,則通過 io_submit非同步io介面進行dirty page的表空間寫入操作。

1 buf_flush_page_cleaner_worker → pc_flush_slot → buf_flush_do_batch →  buf_do_flush_list_batch → buf_flush_page_and_try_neighbors → buf_flush_try_neighbors →buf_flush_page  → buf_flush_write_block_low → fil_io(sync=false)→ os_aio → os_aio_func → AIO::linux_dispatch  →  io_submit

場景二. 線性或者邏輯預讀,非同步讀

邏輯預讀呼叫棧: 

1 ha_innobase::index_read → row_search_mvcc → row_sel_get_clust_rec_for_mysql → buf_page_get_gen → buf_read_ahead_random → fil_io(sync=false)

線性預讀呼叫棧:

1 ha_innobase::index_read → row_search_mvcc → row_sel_get_clust_rec_for_mysql → buf_page_get_gen → buf_read_ahead_linear→ fil_io(sync=false)

2.3 刷盤

如果innodb_flush_method設定了O_DSYNC,日誌檔案(ib_logfileXXX)使用O_SYNC開啟,因此寫完資料不需要呼叫函式fsync刷盤,資料檔案(ibd)使用default模式開啟,因此寫完資料需要呼叫fsync刷盤。

如果innodb_flush_method設定了fsync或者不設定,資料檔案和日誌檔案都使用default模式開啟,寫完資料都需要使用fsync來刷盤。

如果innodb_flush_method設定了O_DIRECT,日誌檔案(ib_logfileXXX)使用default模式開啟,寫完資料需要呼叫fsync函式刷盤,資料檔案(ibd)使用O_DIRECT模式開啟,寫完資料需要呼叫fsync刷盤。

如果innodb_flush_method設定為O_DIRECT_NO_FSYNC,檔案開啟方式與O_DIRECT模式類似,區別是,資料檔案寫完後,不呼叫fsync來刷盤,主要針對O_DIRECT能保證檔案的元資料也落盤的FS

如果使用linux native aio,innodb_flush_method一定要配置成O_DIRECT,否則會退化成同步IO。

 

3. 表空間DDL操作 

3.1 create table

建立表時候呼叫,呼叫流程如下:

1 ha_innobase::create → dict_build_tablespace_for_table → fil_idb_create 

依次依賴於 os_file_create 、os_file_flush、os_file_set_size、os_file_close、os_file_delete, 這些函式依次依賴於open\ fsync\lseek\close\unlink posix檔案標準介面。

3.2 drop table

刪除表的時候呼叫,呼叫棧如下。

1 ha_innobase::delete_table → row_drop_table_for_mysql → row_drop_single_table_tablespace → fil_delete_tablespace → unlink

3.3 rename table

重命名錶的時候呼叫,呼叫棧如下。

1 ha_innobase::rename_table → row_rename_table_for_mysql → row_rename_table_for_mysql → dict_table_rename_in_cache→ fil_rename_tablespace → rename

3.4 truncate table

截斷表時候呼叫,預設表空間截留4個page的大小。呼叫棧如下。

1 ha_innobase::truncate → row_truncate_table_for_mysql → row_truncate_complete → truncate_t::truncate → os_file_truncate_posix → ftruncate

3.5 extend  tablespace

innodb表空間檔案大小是動態擴充套件的,如果表空間中的資料頁不夠,則需要對錶空間檔案進行預擴充套件,比如往聚集索引中大量插入資料的時候。呼叫棧如下

1 row_ins_clust_index_entry_low → btr_cur_pessimistic_insert → fsp_try_extend_data_file → fsp_try_extend_data_file → fil_space_extend → posix_fallocate

4.posix標準的檔案操作介面列表

4.1 檔案元資料操作 

1 open(const char *__file, int __oflag, …)
2 close (int __fd);
3 rename (const char *__old, const char *__new)
4 fcntl(int __fd, int __cmd, ...)
5 unlink(const char *__name)
6 mkdir(const char *__path)
7 rmdir(const char *__path)
8 ftruncate(int __fd, __off64_t __length)
9 posix_fallocate(int __fd, __off64_t __offset,__off64_t __len) 

4.2 同步IO介面

1 lseek(int __fd, __off64_t __offset, int __whence)
2 read(int __fd, void *__buf, size_t __nbytes)
3 write(int __fd, const void *__buf, size_t __n)
4 pread(int __fd, void *__buf, size_t __nbytes, __off64_t __offset)
5 pwrite(int __fd, const void *__buf, size_t __nbytes, __off64_t __offset)
6 fsync(int __fd)

4.3 非同步IO介面

1 io_setup(int maxevents, io_context_t *ctxp);
2 io_destroy(io_context_t ctx);
3 io_submit(io_context_t ctx, long nr, struct iocb *ios[]);
4 io_cancel(io_context_t ctx, struct iocb *iocb, struct io_event *evt);
5 io_getevents(io_context_t ctx, long min_nr, long nr, struct io_event *events, struct timespec *timeout);

4.4 掛載

mount
umount 

4.5 雜項

  其餘檔案屬性、許可權類的操作,就不一一列舉了。

&n