1. 引言mysql
雲原生數據庫跟分佈式mpp數據庫是有差別的,雖然二者都是計算與存儲分離,可是在資源的佔用上有所不一樣。雲原生數據庫是shard everything架構,其依賴的存儲資源、內存資源、事務資源在雲中都是共享、彈性伸縮的。由分佈式文件系統提供按需分配、租戶隔離的塊存儲,由分佈式內存池提供buffer pool佔用的大塊內存。分佈式mpp數據庫則是shard nothing架構,其依賴的存儲資源、內存資源、事務資源是單個物理節點上的資源,在SQL計算層進行了分佈式計算邏輯的分發。linux
本文重點介紹共享存儲,若是分佈式文件系統的iops、每秒刷盤數可以比單個物理節點上的性能線性提高,那麼雲原生數據庫mysql的tps也會隨之提高,並且mysql的原生SQL語法都是支持的,包括嵌套子查詢、存儲過程、函數等。分佈式mpp在SQL計算層作分佈式計算邏輯的分發,這些可能會被裁減掉。ios
單機mysql的事務型存儲引擎innodb的表空間數據存儲依賴於單個linux節點的VFS提供的posix標準的文件操做接口。VFS做爲各個具體文件系統(ext4、xfs、ext3等)的抽象層,屏蔽了各個具體文件系統的實現差別,對應用層提供了統1、標準的posix文件操做接口。相似於阿里雲的polardb對polarfs的依賴,其實polardb就是mysql的內核源碼的二次開發而成的。本文重點羅列雲原生數據庫mysql在各個場景下對posix文件操做接口需求。sql
分佈式彈性文件系統要具體地實現這些接口,各個接口的語義要徹底符合posix標準。並提供mount掛載的功能,將其實現的具體接口註冊到VFS內部,mysql將表空間放置到掛載了分佈式彈性文件系統的路徑下,innodb內部對錶空間文件操做時候,實際上就調用了分佈式文件系統提供的文件操做的api。數據庫
innodb的表空間有用戶表空間、系統表空間、Redo日誌、Undo表空間。本文重點分析用戶表空間對文件操做接口的需求,應該也涵蓋了其他的表空間對文件操做接口的需求。api
用戶對用戶表空間的操做主要有兩類,一類是表空間數據的讀寫,另外一類是表空間DDL操做,對應到posix標準的文件操做接口,一類是文件數據讀寫IO,另外一類是文件的元數據操做。安全
2. 表空間數據的讀寫操做架構
2.1 同步IO 併發
同步IO會阻塞調用線程,直到IO完成,調用線程才返回,pwrite/pread函數是線程安全的同步IO,lseek+read/write函數非線程安全,須要加互斥鎖,併發量大的時候,對性能有必定的影響。如下幾種場景下,會使用同步IO。mvc
場景一. 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 雜項
其他文件屬性、權限類的操做,就不一一列舉了。