洞悉MySQL底層架構:遊走在緩衝與磁盤之間_台中搬家

※台中搬家公司費用怎麼算?

擁有20年純熟搬遷經驗,提供免費估價且流程透明更是5星評價的搬家公司

提起MySQL,其實網上已經有一大把教程了,為什麼我還要寫這篇文章呢,大概是因為網上很多網站都是比較零散,而且描述不夠直觀,不能系統對MySQL相關知識有一個系統的學習,導致不能形成知識體系。為此我撰寫了這篇文章,試圖讓這些底層架構相關知識更加直觀易懂:

  • 盡量以圖文的方式描述技術原理;
  • 涉及到關鍵的技術,附加官網或者技術書籍來源,方便大家進一步擴展學習;
  • 涉及到的背景知識盡可能做一個交代,比如討論到log buffer的刷盤方式,延伸一下IO寫磁盤相關知識點。

好了,MySQL從不會到精通系列馬上就要開始了(看完之後還是不會的話..請忽略這句話)。

可能會有同學問:為啥不直接學更加先進的TiDB,或者是強大的OceanBase。

其實,MySQL作為老牌的應用場景廣泛的關係型開源數據庫,其底層架構是很值得我們學習的,吸收其設計精華,那麼我們在平時的方案設計工作中也可以借鑒,如果項目中用的是MySQL,那麼就能夠把數據庫用的更好了,了解了MySQL底層的執行原理,對於調優工作也是有莫大幫助的。本文我重點講述MySQL底層架構,涉及到:

  • 內存結構:buffer pool、log buffer、change buffer,buffer pool的頁淘汰機制是怎樣的;
  • 磁盤結構:系統表空間、獨立表空間、通用表空間、undo表空間、redo log;
  • 以及IO相關底層原理、查詢SQL執行流程、數據頁結構和行結構描述、聚集索引和輔助索引的底層數據組織方式、MVCC多版本併發控制的底層實現原理,以及可重複讀、讀已提交是怎麼通過MVCC實現的。

看完文本文,您將了解到:

  1. 整體架構:InnoDB存儲架構是怎樣的 (1、MySQL架構)
  2. 工作原理:查詢語句的底層執行流程是怎樣的 (2、查詢SQL執行流程)
  3. IO性能:文件IO操作寫磁盤有哪幾種方式,有什麼IO優化方式 (3.1.2、關於磁盤IO的方式)
  4. 緩存:InnoDB緩存(buffer pool, log buffer)的刷新方式有哪些(3.1.2.2、innodb_flush_method)
  5. 緩存:log buffer是在什麼時候寫入到磁盤的(3.10.2、如何保證數據不丟失 – 其中第四步log buffer持久化到磁盤的時機為)
  6. 緩存:為什麼redo log prepare狀態也要寫磁盤?(3.10.2、如何保證數據不丟失 – 為什麼第二步redo log prepare狀態也要寫磁盤?)
  7. 緩存:臟頁寫盤一般發生在什麼時候(3.10.2、如何保證數據不丟失 – 其中第五步:臟頁刷新到磁盤的時機為)
  8. 緩存:為什麼唯一索引的更新不可以藉助change buffer(3.2、Change Buffer)
  9. 緩存:log buffer的日誌刷盤控制參數innodb_flush_log_at_trx_commit對寫性能有什麼影響(3.4.1、配置參數)
  10. 緩存:buffer pool的LRU是如何實現的,為什麼要這樣實現(3.1.1、緩衝池LRU算法)
  11. 表存儲:系統表空間的結構,MySQL InnoDB磁盤存儲格式,各種表空間(系統表空間,獨立表空間,通用表空間)的作用和優缺點是什麼,ibdata、ibd、frm文件分別是幹嘛的(3.5、表空間)
  12. 行字段存儲:底層頁和行的存儲格式(3.6、InnoDB底層邏輯存儲結構)
  13. 行字段存儲:varchar,null底層是如何存儲的,最大可用存儲多大的長度(3.6.3.1、MySQL中varchar最大長度是多少)
  14. 行字段存儲:行記錄太長了,一頁存不下,該怎麼存儲?(3.6.3.2、行記錄超過頁大小如何存儲)
  15. 索引:數據庫索引的組織方式是怎樣的,明白為什麼要採用B+樹,而不是哈希表、二叉樹或者B樹(3.7、索引 – 為什麼MySQL使用B+樹)
  16. 索引:索引組織方式是怎樣的,為什麼大字段會影響表性能(查詢性能,更新性能)(3.7、索引)
  17. 索引:覆蓋索引、聯合索引什麼情況下會生效(3.7.2、輔助索引)
  18. 索引:什麼是索引下推,索引下推減少了哪方面的開銷?(3.7.2、輔助索引 – 索引條件下推)
  19. 索引:Change Buffer對二級索引DML語句有什麼優化(3.2、Change Buffer)
  20. 數據完整性:MySQL是如何保證數據完整性的,redo log、undo log和buffer pool數據完整性的關鍵作用分別是什麼(3.10.2、如何保證數據不丟失)
  21. MVCC:MVCC底層是怎麼實現的,可重複讀和讀已提交是怎麼實現的(3.11.2、MVCC實現原理)
  22. 雙寫緩衝區有什麼作用(3.9、Doublewrite Buffer)
  23. Redo Log在一個事務中是在什麼時候寫入的?binlog和Redo Log有什麼區別?(3.10.1、Redo Log在事務中的寫入時機)

1、MySQL架構

如下圖為MySQL架構涉及到的常用組件:

2、查詢SQL執行流程

有如下錶格:

我們執行以下sql:

select * from t_user where user_id=10000;

2.1、MySQL客戶端與服務器建立連接

如下圖,建立過程:

  • 客戶端通過mysql命令發起連接請求;
  • 經過三次握手后與服務端建立TCP連接;
  • 連接器接收到請求之後使用用戶密碼進行身份驗證;
  • 驗證通過之後,獲取用戶的權限信息緩存起來,該連接後面都是基於該緩存中的權限執行sql;

對於Java應用程序來說,一般會把建立好的連接放入數據庫連接池中進行復用,只要這個連接不關閉,就會一直在MySQL服務端保持着,可以通過show processlist命令查看,如下:

注意,這裡有個Time,表示這個連接多久沒有動靜了,上面例子是656秒沒有動靜,默認地,如果超過8個小時還沒有動靜,連接器就會自動斷開連接,可以通過wait_timeout參數進行控制。

2.2、執行SQL

如下圖,執行sql:

  • 服務端接收到客戶端的查詢sql之後,先嘗試從查詢緩存中查詢該sql是否已經有緩存的結果了,如果有則直接返回結果,如果沒有則執行下一步;
  • 分析器拿到sql之後會嘗試對sql語句進行詞法分析和語法分析,校驗語法的正確性,通過之後繼續往下執行;
  • 優化器拿到分析器的sql之後,開始繼續解析sql,判斷到需要走什麼索引,根據實際情況重寫sql,最終生成執行計劃;
  • 執行器根據執行計劃執行sql,執行之前會先進行操作權限校驗;然後根據表存儲引擎調用對飲接口進行查詢數據,這裏的掃描行數就是指的接口返回的記錄數,執行器拿到返回記錄之後進一步加工,如本例子:
    • 執行器拿到select * from t_user where user_id=10000的所有記錄,在依次判斷user_name是不是等於”arthinking”,獲取到匹配的記錄。

3、InnoDB引擎架構

如下圖,為存儲引擎的架構:

其實內存中的結構不太好直接觀察到,不過磁盤的還是可以看到的,我們找到磁盤中MySQL的數據文件夾看看:

cd innodb_data_home_dir 查看MySQL 數據目錄:

|- ib_buffer_pool  // 保存緩衝池中頁面的表空間ID和頁面ID,用於重啟恢復緩衝池
|- ib_logfile0  // redo log 磁盤文件1
|- ib_logfile1  // redo log 磁盤文件2,默認情況下,重做日誌存在磁盤的這兩個文件中,循環的方式寫入重做日誌
|- ibdata1  // 系統表空間文件
|- ibtmp1  // 默認臨時表空間文件,可通過innodb_temp_data_file_path屬性指定文件位置
|- mysql/
|- mysql-bin.000001  // bin log文件
|- mysql-bin.000001  // bin log文件
...
|- mysql-bin.index  // bin log文件索引
|- mysqld.local.err  // 錯誤日誌
|- mysqld.local.pid  // mysql進程號
|- performance_schema/  // performance_schema數據庫
|- sys/  // sys數據庫
|- test/  // 數據庫文件夾
    |- db.opt  // test數據庫配置文件,包含數據庫字符集屬性
    |- t.frm  // 數據表元數據文件,不管是使用獨立表空間還是系統表空間,每個表都對應有一個
    |- t.ibd  // 數據庫表獨立表空間文件,如果使用的是獨立表空間,則一個表對應一個ibd文件,否則保存在系統表空間文件中

innodb_data_home_dir[1]

ib_buffer_pool[2]

ib_logfile0[3]

ibtmp1[4]

db.opt[5]

接下來我們逐一來介紹。

3.1、buffer pool

buffer pool(緩衝池)是主內存中的一個區域,在InnoDB訪問表數據和索引數據的時候,會順便把對應的數據頁緩存到緩衝池中。如果直接從緩衝池中直接讀取數據將會加快處理速度。在專用服務器上,通常將80%左右的物理內存分配給緩衝池。

為了提高緩存管理效率,緩衝池把頁面鏈接為列表,使用改進版的LRU算法將很少使用的數據從緩存中老化淘汰掉。

3.1.1、緩衝池LRU算法

通過使用改進版的LRU算法來管理緩衝池列表。

當需要把新頁面存儲到緩衝池中的時候,將淘汰最近最少使用的頁面,並將新頁面添加到舊子列表的頭部。

該算法運行方式:

  • 默認 3/8緩衝池用於舊子列表;
  • 當新頁面如緩衝池時,首先將其插入舊子列表頭部;
  • 重複訪問舊子列表的頁面,將使其移動至新子列表的頭部;
  • 隨着數據庫的運行,頁面逐步移至列表尾部,緩衝池中未被方位的頁面最終將被老化淘汰。

相關優化參數:

  • innodb_old_blocks_pct:控制LRU列表中舊子列表的百分比,默認是37,也就是3/8,可選範圍為5~95;
  • innodb_old_blocks_time :指定第一次訪問頁面后的時間窗口,該時間窗口內訪問頁面不會使其移動到LRU列表的最前面。默認是1000,也就是1秒。

innodb_old_blocks_time很重要,有了這1秒,對於全表掃描,由於是順序掃描的,一般同一個數據頁的數據都是在一秒內訪問完成的,不會升級到新子列表中,一直在舊子列表淘汰數據,所以不會影響到新子列表的緩存。

3.1.2、關於磁盤IO的方式

O_DIRECT是innodb_flush_method參數的一個可選值。

這裏先介紹下和數據庫性能密切相關的文件IO操作方法

3.1.2.1、文件IO操作方法

數據庫系統是基於文件系統的,其性能和設備讀寫的機制有密切的關係。

open:打開文件[6]
int open(const char *pathname, int flags);

系統調用Open會為該進程一個文件描述符fd,常用的flags如下:

  • O_WRONLY:表示我們以”寫”的方式打開,告訴內核我們需要向文件中寫入數據;
  • O_DSYNC:每次write都等待物理I/O完成,但是如果寫操作不影響讀取剛寫入的數據,則不等待文件屬性更新;
  • O_SYNC:每次write都等到物理I/O完成,包括write引起的文件屬性的更新;
  • O_DIRECT:執行磁盤IO時繞過緩衝區高速緩存(內核緩衝區),從用戶空間直接將數據傳遞到文件或磁盤設備,稱為直接IO(direct IO)。因為沒有了OS cache,所以會O_DIRECT降低文件的順序讀寫的效率。
write:寫文件[7]
ssize_t write(int fd, const void *buf, size_t count);

使用open打開文件獲取到文件描述符之後,可以調用write函數來寫文件,具體表現根據open函數參數的不同而不同弄。

fsync & fdatasync:刷新文件[8]
#include <unistd.h>

int fsync(int fd);

int fdatasync(int fd);
  • fdatasync:操作完write之後,我們可以調用fdatasync將文件數據塊flush到磁盤,只要fdatasync返回成功,則可以認為數據已經寫到磁盤了;
  • fsync:與O_SYNC參數類似,fsync還會更新文件metadata到磁盤;
  • sync:sync只是將修改過的塊緩衝區寫入隊列,然後就返回,不等實際寫磁盤操作完成;

為了保證文件更新成功持久化到硬盤,除了調用write方法,還需要調用fsync。

大致交互流程如下圖:

更多關於磁盤IO的相關內容,可以閱讀:On Disk IO, Part 1: Flavors of IO[9]

fsync性能問題:除了刷臟頁到磁盤,fsync還會同步文件metadata,而文件數據和metadata通常存放在磁盤不同地方,所以fsync至少需要兩次IO操作。

對fsync性能的優化建議:由於以上性能問題,如果能夠減少metadata的更新,那麼就可以使用fdatasync了。因此需要確保文件的尺寸在write前後沒有發生變化。為此,可以創建固定大小的文件進行寫,寫完則開啟新的文件繼續寫。

3.1.2.2、innodb_flush_method

innodb_flush_method定義用於將數據刷新到InnoDB數據文件和日誌文件的方法,這可能會影響I/O吞吐量。

以下是具體參數說明:

屬性 值
命令行格式 –innodb-flush-method=value
系統變量 innodb_flush_method
範圍 全局
默認值(Windows) unbuffered
默認值(Unix) fsync
有效值(Windows) unbuffered, normal
有效值(Unix) fsync, O_DSYNC, littlesync, nosync, O_DIRECT, O_DIRECT_NO_FSYNC

比較常用的是這三種:

fsync

默認值,使用fsync()系統調用來flush數據文件和日誌文件到磁盤;

O_DSYNC

由於open函數的O_DSYNC參數在許多Unix系統上都存中問題,因此InnoDB不直接使用O_DSYNC。

InnoDB用於O_SYNC 打開和刷新日誌文件,fsync()刷新數據文件。

表現為:寫日誌操作是在write函數完成,數據文件寫入是通過fsync()系統調用來完成;

O_DIRECT

使用O_DIRECT (在Solaris上對應為directio())打開數據文件,並用於fsync()刷新數據文件和日誌文件。此選項在某些GNU/Linux版本,FreeBSD和Solaris上可用。

表現為:數據文件寫入直接從buffer pool到磁盤,不經過操作系統緩衝,日誌還是需要經過操作系統緩存;

O_DIRECT_NO_FSYNC

在刷新I/O期間InnoDB使用O_DIRECT,並且每次write操作后跳過fsync()系統調用。

此設置適用於某些類型的文件系統,但不適用於其他類型的文件系統。例如,它不適用於XFS。如果不確定所使用的文件系統是否需要fsync()(例如保留所有文件元數據),請改用O_DIRECT。

如下圖所示:

為什麼使用了O_DIRECT配置后還需要調用fsync()?

參考MySQL的這個bug:Innodb calls fsync for writes with innodb_flush_method=O_DIRECT[10]

Domas進行的一些測試表明,如果沒有fsync,某些文件系統(XFS)不會同步元數據。如果元數據會更改,那麼您仍然需要使用fsync(或O_SYNC來打開文件)。

例如,如果在啟用O_DIRECT的情況下增大文件大小,它仍將寫入文件的新部分,但是由於元數據不能反映文件的新大小,因此如果此刻系統發生崩潰,文件尾部可能會丟失。

為此:當重要的元數據發生更改時,請繼續使用fsync或除O_DIRECT之外,也可以選擇使用O_SYNC。

MySQL從v5.6.7起提供了O_DIRECT_NO_FSYNC選項來解決此類問題。

3.2、Change Buffer

change buffer是一種特殊的數據結構,當二級索引頁(非唯一索引)不在緩衝池中時,它們會緩存這些更改 。當頁面通過其他讀取操作加載到緩衝池中時,再將由INSERT,UPDATE或DELETE操作(DML)產生的change buffer合併到buffer pool的數據頁中。

為什麼唯一索引不可以使用chage buffer?

針對唯一索引,如果buffer pool不存在對應的數據頁,還是需要先去磁盤加載數據頁,才能判斷記錄是否重複,這一步避免不了。

而普通索引是非唯一的,插入的時候以相對隨機的順序發生,刪除和更新也會影響索引樹中不相鄰的二級索引樹,通過使用合併緩衝,避免了在磁盤產生大量的隨機IO訪問獲取普通索引頁。

問題

當有許多受影響的行和許多輔助索引要更新時,change buffer合併可能需要幾個小時,在此期間,I/O會增加,可能會導致查詢效率大大降低,即使在事務提交之後,或者服務器重啟之後,change buffer合併操作也會繼續發生。相關閱讀:Section 14.22.2, “Forcing InnoDB Recovery”

3.3、自適應哈希索引

自適應哈希索引功能由innodb_adaptive_hash_index變量啟用 ,或在服務器啟動時由--skip-innodb-adaptive-hash-index禁用。

3.4、Log Buffer

log buffer(日誌緩衝區)用於保存要寫入磁盤上的log file(日誌文件)的數據。日誌緩存區的內容會定期刷新到磁盤。

日誌緩衝區大小由innodb_log_buffer_size變量定義 。默認大小為16MB。較大的日誌緩衝區可以讓大型事務在提交之前無需將redo log寫入磁盤。

如果您有更新,插入或者刪除多行的事務,嘗試增大日誌緩衝區的大小可以節省磁盤I/O。

3.4.1、配置參數

innodb_flush_log_at_trx_commit

innodb_flush_log_at_trx_commit 變量控制如何將日誌緩衝區的內容寫入並刷新到磁盤。

該參數控制是否嚴格存儲ACID還是嘗試獲取更高的性能,可以通過該參數獲取更好的性能,但是會導致在系統崩潰的過程中導致數據丟失。

可選參數:

  • 0,事務提交之後,日誌只記錄到log buffer中,每秒寫一次日誌到緩存並刷新到磁盤,尚未刷新的日誌可能會丟失;
  • 1,要完全符合ACID,必須使用該值,表示日誌在每次事務提交時寫入緩存並刷新到磁盤;
  • 2,每次事務提交之後,日誌寫到page cache,每秒刷一次到磁盤,尚未刷新的日誌可能會丟失;

innodb_flush_log_at_timeout

innodb_flush_log_at_timeout 變量控制日誌刷新頻率。可讓您將日誌刷新頻率設置為N秒(其中N為1 ... 2700,默認值為1)

為了保證數據不丟失,請執行以下操作:

  • 如果啟用了binlog,則設置:sync_binlog=1;
  • innodb_flush_log_at_trx_commit=1;

配置效果如下圖所示:

3.5、表空間

一個InnoDB表及其索引可以在建在系統表空間中,或者是在一個 獨立表空間 中,或在 通用表空間。

  • 當innodb_file_per_table啟用時,通常是將表存放在獨立表空間中,這是默認配置;
  • 當innodb_file_per_table禁用時,則會在系統表空間中創建表;
  • 要在通用表空間中創建表,請使用 CREATE TABLE ... TABLESPACE語法。有關更多信息,請參見官方文檔 14.6.3.3 General Tablespaces。

表空間概覽圖:

表空間涉及的文件

相關文件默認在磁盤中的innodb_data_home_dir目錄下:

|- ibdata1  // 系統表空間文件
|- ibtmp1  // 默認臨時表空間文件,可通過innodb_temp_data_file_path屬性指定文件位置
|- test/  // 數據庫文件夾
    |- db.opt  // test數據庫配置文件,包含數據庫字符集屬性
    |- t.frm  // 數據表元數據文件,不管是使用獨立表空間還是系統表空間,每個表都對應有一個
    |- t.ibd  // 數據庫表獨立表空間文件,如果使用的是獨立表空間,則一個表對應一個ibd文件,否則保存在系統表空間文件中

frm文件

創建一個InnoDB表時,MySQL 在數據庫目錄中創建一個.frm文件。frm文件包含MySQL表的元數據(如表定義)。每個InnoDB表都有一個.frm文件。

與其他MySQL存儲引擎不同, InnoDB它還在系統表空間內的自身內部數據字典中編碼有關表的信息。MySQL刪除表或數據庫時,將刪除一個或多個.frm文件以及InnoDB數據字典中的相應條目。

因此,在InnoDB中,您不能僅通過移動.frm 文件來移動表。有關移動InnoDB 表的信息,請參見官方文檔14.6.1.4 Moving or Copying InnoDB Tables。

ibd文件

對於在獨立表空間創建的表,還會在數據庫目錄中生成一個 .ibd表空間文件。

在通用表空間中創建的表在現有的常規表空間 .ibd文件中創建。常規表空間文件可以在MySQL數據目錄內部或外部創建。有關更多信息,請參見官方文檔14.6.3.3 General Tablespaces。

ibdata文件

系統表空間文件,在 InnoDB系統表空間中創建的表在ibdata中創建。

3.5.1、系統表空間

系統表空間由一個或多個數據文件(ibdata文件)組成。其中包含與InnoDB相關對象有關的元數據(InnoDB 數據字典 data dictionary),以及更改緩衝區(change buffer), 雙寫緩衝區(doublewrite buffer)和撤消日誌(undo logs)的存儲區 。

InnoDB 如果表是在系統表空間中創建的,則系統表空間中也包含表的表數據和索引數據。

系統表空間的問題

在MySQL 5.6.7之前,默認設置是將所有InnoDB表和索引保留 在系統表空間內,這通常會導致該文件變得非常大。因為系統表空間永遠不會縮小,所以如果先加載然後刪除大量臨時數據,則可能會出現存儲問題。

在MySQL 5.7中,默認設置為 獨立表空間模式,其中每個表及其相關索引存儲在單獨的 .ibd文件中。此默認設置使使用Barracuda文件格式的InnoDB功能更容易使用,例如表壓縮,頁外列的有效存儲以及大索引鍵前綴(innodb_large_prefix)。

將所有表數據保留在系統表空間或單獨的 .ibd文件中通常會對存儲管理產生影響。

InnoDB在MySQL 5.7.6中引入了通用表空間[11],這些表空間也由.ibd文件表示 。通用表空間是使用CREATE TABLESPACE語法創建的共享表空間。它們可以在MySQL數據目錄之外創建,能夠容納多個表,並支持所有行格式的表。

3.5.2、獨立表空間

MySQL 5.7中,配置參數:innodb_file_per_table,默認處於啟用狀態,這是一個重要的配置選項,會影響InnoDB文件存儲,功能的可用性和I/O特性等。

啟用之後,每個表的數據和索引是存放在單獨的.ibd文件中的,而不是在系統表空間的共享ibdata文件中。

優點

  • 您可以更加靈活的選擇數據壓縮[12]的行格式,如:
    • 默認情況下(innodb_page_size=16K),前綴索引[13]最多包含768個字節。如果開啟innodb_large_prefix,且Innodb表的存儲行格式為 DYNAMIC 或 COMPRESSED,則前綴索引最多可包含3072個字節,前綴索引也同樣適用;
  • TRUNCATE TABLE執行的更快,並且回收的空間不會繼續保留,而是讓操作系統使用;
  • 可以在單獨的存儲設備上創建每表文件表空間數據文件,以進行I / O優化,空間管理或備份。請參見 14.6.1.2 Creating Tables Externally;

缺點

  • 獨立表空間中的未使用空間只能由同一個表使用,如果管理不當,會造成空間浪費;
  • 多個表需要刷盤,只能執行多次fsync,無法合併多個表的寫操作,這可能會導致更多的fsync操作總數;
  • mysqld必須為每個表文件空間保留一個打開的文件句柄,如果表數量多,可能會影響性能;
  • 每個表都需要自己的數據文件,需要更多的文件描述符;

即使啟用了innodb_file_per_table參數,每張表空間存放的只是數據、索引和插入緩存Bitmap頁,其他數據如回滾信息、插入緩衝索引頁、系統事務信息、二次寫緩衝等還是存放在原來的共享表空間中。

3.5.3、通用表空間

通用表空間使用CREATE TABLESPACE語法創建。

類似於系統表空間,通用表空間是共享表空間,可以存儲多個表的數據。

通用表空間比獨立表空間具有潛在的內存優勢,服務器在表空間的生存期內將表空間元數據保留在內存中。一個通用表空間通常可以存放多個表數據,消耗更少的表空間元數據內存。

數據文件可以放置在MySQL數據目錄或獨立於MySQL數據目錄。

3.5.4、undo表空間

undo表空間包含undo log。

innodb_rollback_segments變量定義分配給每個撤消表空間的回滾段的數量。

undo log可以存儲在一個或多個undo表空間中,而不是系統表空間中。

在默認配置中,撤消日誌位於系統表空間中。SSD存儲更適合undo log的I/O模式,為此,可以把undo log存放在有別於系統表空間的ssd硬盤中。

innodb_undo_tablespaces 配置選項控制undo表空間的數量。

3.5.5、臨時表空間

由用戶創建的非壓縮臨時表和磁盤內部臨時表是在共享臨時表空間中創建的。

innodb_temp_data_file_path 配置選項指定零時表空間文件的路徑,如果未指定,則默認在 innodb_data_home_dir目錄中創建一個略大於12MB 的自動擴展數據文件ibtmp1 。

使用ROW_FORMAT=COMPRESSED屬性創建的壓縮臨時表,是在獨立表空間中的臨時文件目錄中創建的 。

服務啟動的時候創建臨時表空間,關閉的時候銷毀臨時表空間。如果臨時表空間創建失敗,則意味着服務啟動失敗。

3.6、InnoDB底層邏輯存儲結構

在介紹索引之前,我們有必要了解一下InnoDB底層的邏輯存儲結構,因為索引是基於這個底層邏輯存儲結構創建的。截止到目前,我們所展示的都僅僅是物理磁盤中的邏輯視圖,接下來我們就來看看底層的視圖。

3.6.1、ibd文件組織結構

現在我們打開一個表空間ibd文件,看看裏面都是如何組織數據的?

如下圖,表空間由段(segment)、區(extent)、頁(page)組成。

InnoDB最小的存儲單位是頁,默認每個頁大小是16k。

而InnoDB存儲引擎是面向行的(row-oriented),數據按行進行存放,每個頁規定最多允許存放的行數=16k/2 – 200,即7992行。

段:如數據段、索引段、回滾段等。InnoDB存儲引擎是B+樹索引組織的,所以數據即索引,索引即數據。B+樹的恭弘=叶 恭弘子節點存儲的都是數據段的數據。

3.6.2、數據頁結構[14]

名稱 佔用空間 描述
Fil Header 38 byte 頁的基本信息,如所屬表空間,上一頁和下一頁指針。
Page Header 56 byte 數據頁專有的相關信息
Infimun + Supremum 26 byte 兩個虛擬的行記錄,用於限定記錄的邊界
User Records 動態分配 實際存儲的行記錄內容
Free Space 動態調整 尚未使用的頁空間
Page Directory 動態調整 頁中某些記錄的相對位置
Fil Trailer 8 byte 校驗頁是否完整

關於Infimun和Supremum:首次創建索引時,InnoDB會在根頁面中自動設置一個最小記錄和一個最高記錄,並且永遠不會刪除它們。最低記錄和最高記錄可以視為索引頁開銷的一部分。最初,它們都存在於根頁面上,但是隨着索引的增長,最低記錄將存在於第一或最低恭弘=叶 恭弘子頁上,最高記錄將出現在最後或最大關鍵字頁上。

3.6.3、行記錄結構描述[15]

先來講講Compact行記錄格式,Compact是MySQL5.0引入的,設計目標是高效的存儲數據,讓一個頁能夠存放更多的數據,從而實現更快的B+樹查找。

名稱 描述
變長字段長度列表 字段大小最多用2個字節表示,也就是最多限制長度:2^16=65535個字節;字段大小小於255字節,則用1個字節表示;
NULL標誌位 記錄該行哪些位置的字段是null值
記錄頭信息 記錄頭信息信息,固定佔用5個字節
列1數據 實際的列數據,NULL不佔用該部分的空間
列2數據
…

記錄頭用於將連續的記錄鏈接在一起,並用於行級鎖定。

每行數據除了用戶定義的列外,還有兩個隱藏列:

  • 6個字節的事務ID列;
  • 7個字節的回滾指針列;
  • 如果InnoDB沒有指定主鍵,還會增加一個6個字節的rowid列;

而記錄頭信息包[16]含如下內容:

名稱 大小(bit) 描述
() 1 未知
() 1 未知
deleted_flag 1 該行是否已被刪除
min_rec_flag 1 如果該記錄是預定義的最小記錄,則為1
n_owned 4 該記錄擁有的記錄數
heap_no 13 索引堆中該條記錄的排序號
record_type 3 記錄類型:000 普通,001 B+樹節點指針,010 Infimum,011 Supremum,1xx 保留
next_record 16 指向頁中下一條記錄

更詳細的頁結構參考官網:22.2 InnoDB Page Structure

更詳細的行結構參考官網:22.1 InnoDB Record Structure

更詳細的行格式參考官網:14.11 InnoDB Row Formats

根據以上格式,可以得出數據頁內的記錄組織方式:

3.6.3.1、MySQL中varchar最大長度是多少

上面表格描述我們知道,一個字段最長限制是65535個字節,這是存儲長度的限制。

而MySQL中對存儲是有限制的,具體參考:8.4.7 Limits on Table Column Count and Row Size

  • MySQL對每個表有4096列的硬限制,但是對於給定的表,有效最大值可能會更少;
  • MySQL表的每行行最大限製為65,535字節,這是邏輯的限制;實際存儲的時候,表的物理最大行大小略小於頁面的一半。如果一行的長度少於一頁的一半,則所有行都將存儲在本地頁面內。如果它超過一頁的一半,那麼將選擇可變長度列用於外部頁外存儲,直到該行大小控制在半頁之內為止。

而實際能夠存儲的字符是跟編碼有關的。

背景知識:

  • MySQL 4.0版本以下,varchar(10),代表10個字節,如果存放UTF8漢字,那麼只能存3個(每個漢字3字節);

  • MySQL 5.0版本以上,varchar(10),指的是10個字符,無論存放的是数字、字母還是UTF8漢字(每個漢字3字節),都可以存放10個,最大大小是65532字節;

因此,Mysql5根據編碼不同,存儲大小也不同。

那麼假設我們使用的是utf8編碼,那麼每個字符最多佔用3個字節,也就是最多定義varchar(21845)個字符,如果是ascii編碼,一個字符相當於一個字節,最多定義varchar(65535)個字符,下面我們驗證下。

我們嘗試創建一個這樣的字段:

CREATE TABLE `t10` ( `id` int(11) NOT NULL,
                  `a` int(11) NOT NULL,
                  PRIMARY KEY (`id`)
                 ) ENGINE=InnoDB CHARSET=ascii ROW_FORMAT=Compact;


alter table t10 add `str` varchar(21845) DEFAULT NULL;

alter table t10 add `str` varchar(65535) DEFAULT NULL;

發現提示這個錯誤:

mysql> alter table t10 add `str` varchar(65535) DEFAULT NULL;
ERROR 1118 (42000): Row size too large. The maximum row size for the used table type, not counting BLOBs, is 65535. This includes storage overhead, check the manual. You have to change some columns to TEXT or BLOBs

原因是按照以上的行格式介紹,變長字段長度列表記錄也需要佔用空間,佔用2個字節,另外這裡是允許為空字段,在8位之內,所以NULL標誌位佔用1個字節,所以我們總共可以存儲的字符數是:

65535 – 2 – 2 – 4 – 4=65534

其中 -2 個字節表示變長字段列表,-1表示NULL標誌位,兩個-4表示兩個int類型字段佔用大小

所以實際上能夠容納的varchar大小為:65524,我們驗證下:

3.6.3.2、行記錄超過頁大小如何存儲

MySQL表的內部表示具有65,535字節的最大行大小限制。InnoDB 對於4KB,8KB,16KB和32KB innodb_page_size 設置,表的最大行大小(適用於本地存儲在數據庫頁面內的數據)略小於頁面的一半 。如果包含 可變長度列的InnoDB 行超過最大行大小,那麼將選擇可變長度列用於外部頁外存儲。

可變長度列由於太長而無法容納在B樹頁面上,這個時候會把可變長度列存儲在單獨分配的磁盤頁面上,這些頁面稱為溢出頁面,這些列稱為頁外列。頁外列的值存儲在由溢出頁面構成的單鏈接列表中。

InnoDB存儲引擎支持四種行格式:REDUNDANT,COMPACT, DYNAMIC,和COMPRESSED。不同的行格式,對溢出的閾值和處理方式有所區別,詳細參考:14.11 InnoDB Row Formats。

COMPACT行格式處理方式

使用COMPACT行格式的表將前768個字節的變長列值(VARCHAR, VARBINARY和 BLOB和 TEXT類型)存儲在B樹節點內的索引記錄中,其餘的存儲在溢出頁上。

※台中搬家遵守搬運三大原則,讓您的家具不再被破壞!

台中搬家公司推薦超過30年經驗,首選台中大展搬家

如果列的值等於或小於768個字節,則不使用溢出頁,因此可以節省一些I / O。

如果查過了768個字節,那麼會按照如下方式進行存儲:

DYNAMIC行格式處理方式

DYNAMIC行格式提供與COMPACT行格式相同的存儲特性,但改進了超長可變長度列的存儲能力和支持大索引鍵前綴。

InnoDB 可以完全在頁外存儲過長的可變長度列值(針對 VARCHAR, VARBINARY和 BLOB和 TEXT類型),而聚集索引記錄僅包含指向溢出頁的20字節指針。大於或等於768字節的固定長度字段被編碼為可變長度字段。

表中大字段引發的問題

如果一個表中有過多的可變長度大字段,導致一行記錄太長,而整個時候使用的是COMPACT行格式,那麼就可能會插入數據報錯。

如,頁面大小事16k,根據前面描述我們知道,MySQL限制一頁最少要存儲兩行數據,如果很多可變長度大字段,在使用COMPACT的情況下,仍然會把大字段的前面768個字節存在索引頁中,可以算出最多支持的大字段:1024 * 16 / 2 / 768 = 10.67,那麼超過10個可變長度大字段就會插入失敗了。

這個時候可以把row format改為:DYNAMIC。

3.7、索引

前面我們了解了InnoDB底層的存儲結構,即:以B+樹的方式組織數據頁。另外了解了數據頁中的數據行的存儲方式。

而構建B+樹索引的時候必須要選定一個或者多個字段作為索引的值,如果索引選擇的是主鍵,那麼我們就稱為聚集索引,否則就是二級索引。

為什麼MySQL使用B+樹?

  • 哈希表雖然可以提供O(1)的單行數據操作性能,但卻不能很好的支持排序和範圍查找,會導致全表掃描;
  • B樹可以再非恭弘=叶 恭弘子節點存儲數據,但是這可能會導致查詢連續數據的時候增加更多的I/O操作;
  • 而B+樹數據都存放在恭弘=叶 恭弘子節點,恭弘=叶 恭弘子節點通過指針相互連接,可以減少順序遍歷時產生的額外隨機I/O

更新詳細解釋: 為什麼 MySQL 使用 B+ 樹[17]

3.7.1、聚集索引

了解到上面的底層邏輯存儲結構之後,我們進一步來看看InnoDB是怎麼通過B+樹來組織存儲數據的。

首先來介紹下聚集索引。

聚集索引

主鍵索引的InnoDB術語。

下面我們創建一張測試表,並插入數據,來構造一顆B+樹:

CREATE TABLE t20 (
id int NOT NULL,
a int NOT NULL,
b int,
c int,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;

insert into t20 values(20, 1, 2, 1);
insert into t20 values(40, 1, 2, 5);
insert into t20 values(30, 3, 2, 4);
insert into t20 values(50, 3, 6, 2);
insert into t20 values(10, 1, 1, 1);

可以看到,雖然我們是id亂序插入的,但是插入之後查出來的確是排序好的:

這個排序就是B+索引樹構建的。

我們可以通過這個在線的動態演示工具來看看B+樹的構造過程,最終結果如下:

實際存放在數據庫中的模型因頁面大小不一樣而有所不同,這裏為了簡化模型,我們按照B+樹的通用模型來解釋數據的存儲結構。

類似的,我們的數據也是這種組織形式的,該B+樹中,我們以主鍵為索引進行構建,並且把完整的記錄存到對應的頁下面:

其中藍色的是索引頁,橙色的是數據頁。

每個頁的大小默認為16k,如果插入新的數據行,這個時候就要申請新的數據頁了,然後挪動部分數據過去,重新調整B+樹,這個過程稱為頁分裂,這個過程會影響性能。

相反的,如果InnoDB索引頁的填充因子下降到之下MERGE_THRESHOLD,默認情況下為50%(如果未指定),則InnoDB嘗試收縮索引樹以釋放頁面。

自增主鍵的插入是遞增順序插入的,每次添加記錄都是追加的,不涉及到記錄的挪動,不會觸發恭弘=叶 恭弘子節點的分裂,而一般業務字段做主鍵,往往都不是有序插入的,寫成本比較高,所以我們更傾向於使用自增字段作為主鍵。

聚集索引注意事項

  • 當在表上面定義了PRIMARY KEY之後,InnoDB會把它作為聚集索引。為此,為你的每個表定義一個PRIMARY KEY。如果沒有唯一併且非空的字段或者一組列,那麼請添加一個自增列;
  • 如果您沒有為表定義PRIMARY KEY,則MySQL會找到第一個不帶null值的UNIQUE索引,並其用作聚集索引;
  • 如果表沒有PRIMARY KEY或沒有合適的UNIQUE索引,則InnoDB 內部會生成一個隱藏的聚集索引GEN_CLUST_INDEX,作為行ID,行ID是一個6字節的字段,隨着數據的插入而自增。

聚集索引查找

根據索引進行查找id=50的記錄,如下圖,沿着B+樹一直往下尋找,最終找到第四頁,然後把該頁加載到buffer pool中,在緩存中遍歷對比查找,由於裏面的行記錄是順序組織的,所以很快就可以定位到記錄了。

3.7.2、輔助索引

除了聚集索引之外的所有索引都稱為輔助索引(二級索引)。在InnoDB中,輔助索引中每個記錄都包含該行的主鍵列以及為輔助索引指定的列。

在輔助索引中查找到記錄,可以得到記錄的主鍵索引ID,然後可以通過這個主鍵索引ID去聚集索引中搜索具體的記錄,這個過程稱為回表操作。

如果主鍵較長,則輔助索引將使用更多空間,因此具有短的主鍵是有利的。

下面我們給剛剛的表添加一個組合聯合索引

-- 添加多一個字段
alter table t20 add column d varchar(20) not null default '';
-- 添加一個聯合索引
alter table t20 add index idx_abc(a, b, c);

添加之後組合索引B+樹如下,其中索引key為abc三個字段的組合,索引存儲的記錄為主鍵ID:

覆蓋索引(Using index)

InnoDB存儲引擎支持覆蓋索引,即從輔助索引中就可以得到查詢的記錄,而不需要回表去查詢聚集索引中的記錄,從而減少大量的IO操作。下面的查詢既是用到了覆蓋索引 idx_abc:

select a, b from t20 where a > 2;

執行結果如下:

可以發現,Extra這一列提示Using index,使用到了覆蓋索引,掃描的行數為2。注意:這裏的掃描行數指的是MySQL執行器從引擎取到兩條記錄,引擎內部可能會遍歷到多條記錄進行條件比較。

最左匹配原則

由於InnoDB索引式B+樹構建的,因此可以利用索引的“最左前綴”來定位記錄。

也就是說,不僅僅是用到索引的全部定義字段會走索引,只要滿足最左前綴,就可以利用索引來加速檢索。這個最左前綴可以是聯合索引的最左n個字段。

索引條件下推(Using index condition)

索引條件下推 Index Condition Pushdown (ICP),是針對MySQL使用索引從表中檢索行的情況的一種優化。

為什麼叫下推呢,就是在滿足要求的情況下,把索引的條件丟給存儲引擎去判斷,而不是把完整的記錄傳回MySQL Server層去判斷。

ICP支持range, ref, eq_ref, 和 ref_or_null類型的查找,支持MyISAM和InnoDB存儲引擎。

不能將引用子查詢的條件下推,觸發條件不能下推。詳細規則參考:Index Condition Pushdown

如果不使用ICP,則存儲引擎將遍歷索引以在聚集索引中定位行,並將結果返回給MySQL Server層,MySQL Server層繼續根據WHERE條件進行篩選行。

啟用ICP后,如果WHERE可以僅使用索引中的列來評估部分條件,則MySQL Server層會將這部分條件壓入WHERE條件下降到存儲引擎。然後,存儲引擎通過使用索引條目來判斷索引條件,在滿足條件的情況下,才回表去查找記錄返回給MySQL Server層。

ICP的目標是減少回表掃描的行數,從而減少I / O操作。對於InnoDB表,ICP僅用於二級索引。

使用索引下推的時候,執行計劃中的Extra會提示:Using index condition,而不是Using index,因為必須回表查詢整行數據。Using index代表使用到了覆蓋索引。

3.8、InnoDB Data Directory

InnoDB數據字典(Data Directory)存放於系統表空間中,主要包含元數據,用於追蹤表、索引、表字段等信息。由於歷史的原因,InnoDB數據字典中的元數據與.frm文件中的元數據重複了。

3.9、Doublewrite Buffer

雙寫緩衝區(Doublewrite Buffer)是一個存儲區,是InnoDB在tablespace上的128個頁(2個區),大小是2MB[18]。

版本區別:在MySQL 8.0.20之前,doublewrite緩衝區存儲區位於InnoDB系統表空間中。從MySQL 8.0.20開始,doublewrite緩衝區存儲區位於doublewrite文件中。

本文基於MySQL 5.7編寫。

操作系統寫文件是以4KB為單位的,那麼每寫一個InnoDB的page到磁盤上,操作系統需要寫4個塊。如果寫入4個塊的過程中出現系統崩潰,那麼會導致16K的數據只有一部分寫是成功的,這種情況下就是partial page write(部分頁寫入)問題。

InnoDB這個時候是沒法通過redo log來恢復的,因為這個時候頁面的Fil Trailer(Fil Trailer 主要存放FIL_PAGE_END_LSN,主要包含頁面校驗和以及最後的事務)中的數據是有問題的。

為此,每當InnoDB將頁面寫入到數據文件中的適當位置之前,都會首先將其寫入雙寫緩衝區。只有將緩衝區安全地刷新到磁盤后,InnoDB才會將頁面寫入最終的數據文件。

如果在頁面寫入過程中發生操作系統或者mysqld進程崩潰,則InnoDB可以在崩潰恢復期間從雙寫緩衝區中找到頁面的完好副本用於恢復。恢復時,InnoDB掃描雙寫緩衝區,併為緩衝區中的每個有效頁面檢查數據文件中的頁面是否完整。

如果系統表空間文件(“ ibdata文件 ”)位於支持原子寫的Fusion-io設備上,則自動禁用雙寫緩衝,並且將Fusion-io原子寫用於所有數據文件。

3.10、Redo Log

重做日誌(Redo Log)主要適用於數據庫的崩潰恢復,用於實現數據的完整性。

重做日誌由兩部分組成:

  • 重做日誌緩衝區 Log Buffer;
  • 重做日誌文件,重做日誌文件在磁盤上由兩個名為ib_logfile0和ib_logfile1的物理文件表示。

為了實現數據完整性,在臟頁刷新到磁盤之前,必須先把重做日誌寫入到磁盤。除了數據頁,聚集索引、輔助索引以及Undo Log都需要記錄重做日誌。

3.10.1、Redo Log在事務中的寫入時機

在事務中,除了寫Redo log,還需要寫binlog,為此,我們先來簡單介紹下binlog。

3.10.1.1、binlog

全寫:Binary Log,二進制log。二進制日誌是一組日誌文件。其中包含有關對MySQL服務器實例進行的數據修改的信息。

Redo Log是InnoDB引擎特有的,而binlog是MySQL的Server層實現的,所有引擎都可以使用。

Redo Log的文件是循環寫的,空間會用完,binlog日誌是追加寫的,不會覆蓋以前的日誌。

binlog主要的目的:

  • 主從同步,主服務器將二進制日誌中包含的事件發送到從服務器,從服務器執行這些事件,以保持和主服務器相同的數據更改;
  • 某些數據恢復操作需要使用二進制日誌,還原到某一個備份點。

binlog主要是用於主從同步和數據恢復,Redo Log主要是用於實現事務數據的完整性,讓InnoDB具有不會丟失數據的能力,又稱為crash-safe。

binlog日誌的兩種記錄形式:

  • 基於SQL的日誌記錄:事件包含產生數據更改(插入,新增,刪除)的SQL語句;
  • 基於行的日誌記錄:時間描述對單個行的更改。

混合日誌記錄默認情況下使用基於語句的日誌記錄,但根據需要自動切換到基於行的日誌記錄。

3.10.1.2、Redo Log在事務中的寫入時機

簡單的介紹完binlog,我們再來看看Redo Log的寫入流程。

假設我們這裏執行一條sql

update t20 set a=10 where id=1;

執行流程如下:

3.10.2、如何保證數據不丟失

前面我們介紹Log Buffer的時候,提到過,為了保證數據不丟失,我們需要執行以下操作:

  • 如果啟用了binlog,則設置:sync_binlog=1;
  • innodb_flush_log_at_trx_commit=1;
  • sync_binlog=0:表示每次提交事務都只 write,不 fsync;
  • sync_binlog=1:表示每次提交事務都會執行 fsync;
  • sync_binlog=N(N>1) :表示每次提交事務都 write,但累積 N 個事務后才 fsync。

這兩個的作用相當於在上面的流程最後一步,提交事務接口返回Server層之前,把binlog cache和log buffer都fsync到磁盤中了,這樣就保證了數據的落盤,不會丟失,即使奔潰了,也可以通過binlog和redo log恢複數據相關流程如下:

在磁盤和內存中的處理流程如下面編號所示:

其中第四步log buffer持久化到磁盤的時機為:

  • log buffer佔用的空間即將達到innodb_log_buffer_size一半的時候,後台線程主動寫盤;
  • InnoDB後台有個線程,每隔1秒會把log buffer刷到磁盤;
  • 由於log buffer是所有線程共享的,當其他事務線程提交時也會導致已寫入log buffer但還未提交的事務的redo log一起刷新到磁盤

其中第五步:臟頁刷新到磁盤的時機為:

  • 系統內存不足,需要淘汰臟頁的時候,要把臟頁同步回磁盤;
  • MySQL空閑的時候;
  • MySQL正常關閉的時候,會把臟頁flush到磁盤。

參數innodb_max_dirty_pages_pct是臟頁比例上限,默認值是 75%。

為什麼第二步 redo log prepare狀態也要寫磁盤?

因為這裏先寫了,才能確保在把binlog寫到磁盤后崩潰,能夠恢複數據:如果判斷到redo log是prepare狀態,那麼查看是否存XID對應的binlog,如果存在,則表示事務成功提交,需要用prepare狀態的redo log進行恢復。

這樣即使崩潰了,也可以通過redo log來進行恢復了,恢複流程如下:

Redo Log是循環寫的,如下圖:

  • writepos記錄了當前寫的位置,一邊寫位置一邊往前推進,當writepos與checkpoint重疊的時候就表示logfile寫滿了,綠色部分表示是空閑的空間,紅色部分是寫了redo log的空間;
  • checkpoint處標識了當前的LSN,每當系統崩潰重啟,都會從當前checkpoint這個位置執行重做日誌,根據重做日誌逐個確認數據頁是否沒問題,有問題就通過redo log進行修復。

LSN Log Sequence Number的縮寫。代表日誌序列號。在InnoDB中,LSN佔用8個字節,單調遞增,LSN的含義:

  • 重做日誌寫入的總量;
  • checkpoint的位置;
  • 頁的版本;

除了重做日誌中有LSN,每個頁的頭部也是有存儲了該頁的LSN,我們前面介紹頁面格式的時候有介紹過。

在頁中LSN表示該頁最後刷新時LSN的大小。[19]

3.11、Undo Logs

上面說的redo log記錄了事務的行為,可以通過其對頁進行重做操作,但是食物有時候需要進行回滾,這時候就需要undo log了。[20]

關於Undo Log的存儲:InnoDB中有回滾段(rollback segment),每個回滾段記錄1024個undo log segment,在每個undo log segment段中進行申請undo頁。系統表空間偏移量為5的頁記錄了所有的rollback segment header所在的頁。

3.11.1、undo log的格式

根據行為不同分為兩種:

insert undo log

insert undo log:只對事務本身可見,所以insert undo log在事務提交后可直接刪除,無需執行purge操作;

insert undo log主要記錄了:

next 記錄下一個undo log的位置
type_cmpl undo的類型:insert or update
*undo_no 記錄事務的ID
*table_id 記錄表對象
*len1, col1 記錄列和值
*len2, col2 記錄列和值
… …
start 記錄undo log的開始位置

假設在事務1001中,執行以下sql,t20的table_id為10:

insert into t20(id, a, b, c, d) values(12, 2, 3, 1, "init")

那麼對應會生成一條undo log:

update undo log

update undo log:執行update或者delete會產生undo log,會影響已存在的記錄,為了實現MVCC(後邊介紹),update undo log不能再事務提交時立刻刪除,需要將事務提交時放入到history list上,等待purge線程進行最後的刪除操作。

update undo log主要記錄了:

next 記錄下一個undo log的位置
type_cmpl undo的類型:insert or update
*undo_no undo日誌編號
*table_id 記錄表對象
info_bits
*DATA_TRX_ID 事務的ID
*DATA_ROLL_PTR 回滾指針
*len1, i_col1 n_unique_index
*len2, i_col2
…
n_update_fields 以下是update vector信息,表示update操作導致發送改變的列
*pos1, *len1, u_old_col1
*pos2, *len2, u_old_col2
…
n_bytes_below
*pos, *len, col1
*pos, *len, col2
…
start 記錄undo log的開始位置

假設在事務1002中,執行以下sql,t20的table_id為10:

update t20 set d="update1" where id=60;

那麼對應會生成一條undo log:

如上圖,每回退應用一個undo log,就回退一個版本,這就是MVCC(Multi versioning concurrency control)的實現原理。

下面我們在執行一個delete sql:

delete from t20 where id=60;

對應的undo log變為如下:

如上圖,實際的行記錄不會立刻刪除,而是在行記錄頭信息記錄了一個deleted_flag標誌位。最終會在purge線程purge undo log的時候進行實際的刪除操作,這個時候undo log也會清理掉。

3.11.2、MVCC實現原理

如上圖所示,MySQL只會有一個行記錄,但是會把每次執行的sql導致行記錄的變動,通過undo log的形式記錄起來,undo log通過回滾指針連接在一起,這樣我們想回溯某一個版本的時候,就可以應用undo log,回到對應的版本視圖了。

我們知道InnoDB是支持RC(Read Commit)和RR(Repeatable Read)事務隔離級別的,而這個是通過一致性視圖(consistent read view)實現的。

一個事務開啟瞬間,所有活躍的事務(未提交)構成了一個視圖數組,InnoDB就是通過這個視圖數組來判斷行數據是否需要undo到指定的版本:

RR事務隔離級別

假設我們使用了RR事務隔離級別。我們看個例子:

如下圖,假設id=60的記錄a=1

事務C啟動的瞬間,活躍的事務如下圖黃色部分所示:

也就是對於事務A、事務B、事務C,他們能夠看到的數據只有是行記錄中的最大事務IDDATA_TRX_ID<=11的,如果大於,那麼只能通過undo進行回滾了。如果TRX_ID=當前事務id,也可以看到,即看到自己的改動。

另外有一個需要注意的:

  • 在RR隔離級別下,當事務更新事務的時候,只能用當前讀來獲取最新的版本數據來更新,如果當前記錄的行鎖被其他事務佔用,就需要進入所等待;
  • 在RC隔離級別下,每個語句執行都會計算出新的一致性視圖。

所以我們分析上面的例子的執行流程:

  • 事務C執行update,執行當前讀,拿到的a=1,然後+1,最終a=2,同時添加一個TRX_ID=11的undo log;
  • 事務B執行select,使用快照讀,記錄的DATA_TRX_ID > 11,所以需要通過undo log回滾到DATA_TRX_ID=11的版本,所以拿到的a是1;
  • 事務B執行update,需要使用當前讀,拿到最新的記錄,a=2,然後加1,最終a=3;
  • 事務B執行select,拿到當前最新的版本,為自己的事務id,所以得到a=3;
  • 事務A執行select,使用快照讀,記錄的DATA_TRX_ID > 11,所以需要通過undo log回滾到DATA_TRX_ID=11的版本,所以拿到的a是1。
  • 如果是RC隔離級別,執行select的時候會計算出新的視圖,新的視圖能夠看到的最大事務ID=14,由於事務B還沒提交,事務C提交了,所以可以得到a=2:

總結

  • 數據完整性依靠:redo log
  • 事務隔離級別的實現依靠MVCC,MVCC依靠undo log實現
  • IO性能提升方式:buffer pool加快查詢效率和普通索引更新的效率,log buffer對日誌寫的性能提升
  • 查詢性能提升依賴於索引,底層用頁存儲,字段越小頁存儲越多行記錄,查詢效率越快;自增字段作為聚集索引可以加快插入操作;
  • 故障恢復:雙寫緩衝區、redo log
  • 主從同步:binlog

本文內容比較多,看完之後需要多梳理,最後大家可以對照着這個思維導圖回憶一下,這些內容是否都記住了:

這篇文章的內容就差不多介紹到這裏了,能夠閱讀到這裏的朋友真的是很有耐心,為你點個贊。

本文為arthinking基於相關技術資料和官方文檔撰寫而成,確保內容的準確性,如果你發現了有何錯漏之處,煩請高抬貴手幫忙指正,萬分感激。

大家可以關注我的博客:itzhai.com 獲取更多文章,我將持續更新後端相關技術,涉及JVM、Java基礎、架構設計、網絡編程、數據結構、數據庫、算法、併發編程、分佈式系統等相關內容。

如果您覺得讀完本文有所收穫的話,可以關注我的賬號,或者點贊吧,碼字不易,您的支持就是我寫作的最大動力,再次感謝!

關注我的公眾號,及時獲取最新的文章。

更多文章

  • JVM系列專題:公眾號發送 JVM

本文作者: arthinking

博客鏈接: https://www.itzhai.com/database/insight-into-the-underlying-architecture-of-mysql-buffer-and-disk.html

洞悉MySQL底層架構:遊走在緩衝與磁盤之間

版權聲明: BY-NC-SA許可協議:創作不易,如需轉載,請聯繫作者,謝謝!

References

  1. innodb_data_home_dir. Retrieved from https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_data_home_dir ↩︎

  2. ib_buffer_pool. Retrieved from https://dev.mysql.com/doc/refman/5.6/en/innodb-preload-buffer-pool.html ↩︎

  3. ib_logfile0. Retrieved from https://dev.mysql.com/doc/refman/5.7/en/innodb-redo-log.html ↩︎

  4. ibtmp1. Retrieved from https://dev.mysql.com/doc/refman/5.7/en/innodb-temporary-tablespace.html ↩︎

  5. db.opt. Retrieved from https://dev.mysql.com/doc/refman/8.0/en/data-dictionary-file-removal.html ↩︎

  6. Linux Programmer’s Manual – OPEN(2). (2020-02-09). Retrieved from http://man7.org/linux/man-pages/man2/open.2.html ↩︎

  7. man-pages.write. (2019-10-10). Retrieved from http://man7.org/linux/man-pages/man2/write.2.html ↩︎

  8. man-pages.fdatasync. (2019-03-06). Retrieved from http://man7.org/linux/man-pages/man2/fdatasync.2.html ↩︎

  9. On Disk IO, Part 1: Flavors of IO. medium.com. Retrieved from https://medium.com/databasss/on-disk-io-part-1-flavours-of-io-8e1ace1de017 ↩︎

  10. Innodb calls fsync for writes with innodb_flush_method=O_DIRECT. Retrieved from https://bugs.mysql.com/bug.php?id=45892 ↩︎

  11. 14.6.3.3 General Tablespaces. Retrieved from https://dev.mysql.com/doc/refman/5.7/en/general-tablespaces.html ↩︎

  12. MYSQL INNODB表壓縮. (2018-03-09). Retrieved from https://cloud.tencent.com/developer/article/1056453 ↩︎

  13. 前綴索引,一種優化索引大小的解決方案. (2015-03-03). Retrieved from https://www.cnblogs.com/studyzy/p/4310653.html ↩︎

  14. MySQL Internals Manual – innodb page structure[EB/OL]. (2020-05-04). Retrieved 2020-0530, from https://dev.mysql.com/doc/internals/en/innodb-page-structure.html ↩︎

  15. official.MySQL Internals Manual – innodb record structure[EB/OL]. (2020-05-04). Retrieved 2020-0530, from https://dev.mysql.com/doc/internals/en/innodb-record-structure.html ↩︎

  16. 姜承堯. MySQL技術內幕-InnoDB存儲引擎第二版[M]. 机械工業出版社, 2013-5:104. ↩︎

  17. 為什麼 MySQL 使用 B+ 樹. draveness.me. (2019-12-11). Retrieved from https://draveness.me/whys-the-design-mysql-b-plus-tree/ ↩︎

  18. InnoDB DoubleWrite Buffer as Read Cache using SSDs∗. Retrieved from https://www.usenix.org/legacy/events/fast12/poster_descriptions/Kangdescription2-12-12.pdf ↩︎

  19. 姜承堯. MySQL技術內幕-InnoDB存儲引擎第二版[M]. 机械工業出版社, 2013-5:302-303. ↩︎

  20. 姜承堯. MySQL技術內幕-InnoDB存儲引擎第二版[M]. 机械工業出版社, 2013-5:306. ↩︎

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

※台中搬家公司費用怎麼算?

擁有20年純熟搬遷經驗,提供免費估價且流程透明更是5星評價的搬家公司

【asp.net core 系列】3 視圖以及視圖與控制器_台中搬家公司

※台中搬家公司教你幾個打包小技巧,輕鬆整理裝箱!

還在煩惱搬家費用要多少哪?台中大展搬家線上試算搬家費用,從此不再擔心「物品怎麼計費」、「多少車才能裝完」

0.前言

在之前的幾篇中,我們大概介紹了如何創建一個asp.net core mvc項目以及http請求如何被路由轉交給對應的執行單元。這一篇我們將介紹一下控制器與視圖直接的關係。

1. 視圖

這裏的視圖不是數據庫里的視圖,是一種展示技術。在asp.net core mvc項目中視圖是指以cshtml做擴展名的文件,通常在Views文件夾。

那麼現在我們進到之前創建的測試項目 MvcWeb的Views目錄下,如果小夥伴們沒有做修改的話,能看到如下的目錄結構:

├── Home
│   ├── Index.cshtml
│   └── Privacy.cshtml
├── Shared
│   ├── Error.cshtml
│   ├── _Layout.cshtml
│   └── _ValidationScriptsPartial.cshtml
├── _ViewImports.cshtml
└── _ViewStart.cshtml

在Views根目錄下,有兩個文件分別是:_ViewImports.cshtml 、 _ViewStart.cshtml 兩個文件(注意,有個前置下劃線)。

1.1 在視圖中引用命名空間

我們知道,在cshtml文件中,雖然極大的減少了服務器代碼,但是有時候無法避免的使用一些C#代碼。那麼就會產生一個問題,很多類都有自己的命名空間,如果我們在某個或某幾個或某些視圖中需要訪問這些類和方法,那麼一個視圖一個視圖的寫引用有點不太現實,因為這太繁瑣了。

所以asp.net core mvc 設置了在名為_ViewImports.cshtml的文件中添加引用,則在Views下所有視圖中都生效。那麼,先來看看這個文件里有啥吧:

@using MvcWeb
@using MvcWeb.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

可以看到,這裏引用了項目的命名空間和項目下Modes命名空間的所有內容。因為我們之前創建的測試項目名稱就是 MvcWeb。

最後一行是一個 cshtml標記引用,第一個星號表示當前項目的所有TagHelper實現都引用,後面的表示引入aps.net core mvc內置的TagHelper。

關於 TagHelper,這篇就先不介紹了。

1.2 ViewsStart

_ViewStart.cshtml 作用從名字中可見一二,這個文件用來配置一些在視圖剛開始加載時的一些配置內容。先看一下,默認的裏面是什麼吧:

@{
    Layout = "_Layout";
}

先做個介紹,@符號後面用一對大括號包裹,裏面是C# 代碼。也就是說 Layout = "_Layout",這行的意思是給某個名為Layout的屬性設置值為_Layout。

那麼,Layout的屬性是哪裡的呢?

對於asp.net core mvc而言,一個視圖也是一個類只不過這個類是動態生成的,不是一個由程序員編寫出來的類,但是這個類繼承自:

namespace Microsoft.AspNetCore.Mvc.Razor
{
    public abstract class RazorPageBase : IRazorPage
    {
    }
}

Layout正好是這個類的一個屬性,表示視圖是否使用了某個布局頁。所以上面的代碼錶示,Views里的新建視圖,默認是使用名為_Layout的視圖作為布局頁。

當然,這個頁面不只有這個作用,小夥伴們可以自己嘗試下哦。

1.3 視圖檢索

在上一節中,我們指定了一個布局頁的名稱。布局頁也是視圖中的一種,但我們也只指定了名稱,但沒有指定路徑。asp.net core是如何發現這個名稱的視圖呢?

asp.net core 會按照以下順序查找對應的視圖文件:

  • Views/[ControllerName]/[ViewName].cshtml
  • Views/Shared/[ViewName].cshtml

所以,_Layout也會按照這個順序查找,為了避免不必要的混淆,我們只在Shared目錄下寫了_Layout.cshtml。這也是通常的做法,該文件表示一個全局的布局頁。

2. 控制器與視圖的關係

在上一篇《【asp.net core 系列】2 控制器與路由的恩怨情仇》中,我們介紹了三種創建控制器的方法,並且最後推薦使用名字以Controller結尾並繼承Controller類的寫法。我將在這裏為大家再次講解為什麼推薦這樣寫:

  • 以Controller結尾,可以很明確的告訴其他人或者未來的自己這是一個控制器,不是別的類
  • 繼承Controller,是因為Controller類為我們提供了控制器用到的屬性和方法

嗯,暫時就這兩點。別看少,但是這很重要。

2.1 使用視圖

在之前介紹的時候,有提到過當我們訪問一個URL的時候,路由會自動為我們尋找到對應的可執行代碼單元。但是,沒有進一步內容的介紹。當我們尋找到對應的可執行代碼單元也就是Action之後,Action進行一系列的處理,會對這個請求做出響應。有一種響應就是返回一個展示頁面,也就是View。

那麼,如何返回一個View呢?

創建一個控制器,名為ViewDemoController,並添加一個方法Index,返回類型為IActionResult:

using Microsoft.AspNetCore.Mvc;

namespace MvcWeb.Controllers
{
    public class ViewDemoController:Controller
    {
        public IActionResult Index()
        {
            return View();
        }
    }
}

其中 View() 表示返回一個View,這View的名稱是 Index,在ViewDemo控制器下。所以,它的路徑應該是:

Views/ViewDemo/Index.cshtml

在對應目錄創建該文件,然後在文件里隨便寫一些內容,之後啟動項目(項目的端口在第一部分就已經修改過了):

http://localhost:5006 

然後訪問:

http://localhost:5006/ViewDemo/

應該是類似的頁面。

※推薦台中搬家公司優質服務,可到府估價

台中搬鋼琴,台中金庫搬運,中部廢棄物處理,南投縣搬家公司,好幫手搬家,西屯區搬家

IActionResult 是一個接口,表示是一個Action的處理結果,在這裏可以理解為固定寫法。

2.2 指定視圖

在控制器里,View 方法表示使用一個視圖進行渲染,默認是使用方法同名的視圖。當然,既然是默認的,那就一定有不默認的時候。對的,View方法提供了幾個重載版本,這些重載版本里有一個名字為viewName的參數,這個參數就是用來指定視圖名稱的。

那麼,我們可以指定哪些視圖名稱:

  • 同一個控制器文件夾下的其他視圖
  • Shared 文件夾下的視圖

這兩種都是不用攜帶路徑的視圖名,可以省略文件擴展名(cshtml)。

當然,還可以指定其他路徑下的視圖文件,如:

  • Views/Home/About.cshtml 表示從根目錄下查找到這個視圖,這種寫法必須指定擴展名
  • ../Manage/Index 表示在Manage控制器目錄下的Index

2.3 給視圖傳遞數據

之前介紹了如何使用視圖、如何指定視圖名稱,但是還缺最關鍵的一步,那就是如何給視圖傳遞數據。

通常情況下,Action方法中給視圖傳遞數據,只有這三種是推薦的:

  • 使用ViewData
  • 使用ViewDataAttribute
  • 使用ViewBag
  • 使用ViewModel

Controller類有一個屬性是 ViewData,它的聲明如下:

public ViewDataDictionary ViewData { get; set; }

可以看到這是一個字典型的屬性,所以給它賦值是這樣使用的:

public IActionResult Index()
{
    ViewData["Title"] = "ViewDemo";
    return View();
}

ViewBag也是 Controller類的一個屬性,它的聲明如下:

public dynamic ViewBag { get; }

可以看到這是一個動態類,實際上ViewBag里的數據與ViewData是互通的,換句話說就是ViewBag是對ViewData的一次封裝,兩者並沒有實際上的區別。賦值使用:

public IActionResult Index()
{
    ViewBag.Name = "小李";
    return View();
}

而ViewDataAttribute則與上兩個,不太一樣,這個屬性標註給控制器的屬性上,asp.net core mvc就會把這個屬性的值填充給ViewData,鍵值就是屬性名:

[ViewData]
public string AttributeTest{get;set;}

與 ViewData["AttributeTest"]效果一致。

在View方法的一些重載版本里,需要一個名為 model的參數,類型是object。這個參數就是一個ViewModel。使用:

在MvcWeb/Models 下添加一個類:

namespace MvcWeb.Models
{
    public class ViewModelTestModel
    {
        public string Name{get;set;}
        public int Age{get;set;}
    }
}

回到剛剛的Index方法里,創建一個ViewModelTestModel實例,並傳給View方法:

public IActionResult Index()
{
    ViewData["Title"] = "ViewDemo";
    ViewBag.Name = "小李";
    var model = new ViewModelTestModel
    {
        Name = "測試實例",
        Age = 1
    };
    return View(model);
}

2.4 在視圖中使用

在上一小節中,我們分別使用ViewData和ViewBag以及ViewModel給視圖傳遞了三個數據,那麼如何在視圖中獲取這三個數據呢?

<h2>@ViewData["Title"]</h2>
<!--實際會显示 <h2>ViewDemo</h2>-->

與字典一樣,@起頭,表示後面跟着一個屬性或者一段C#表達式,並將表達式的結果輸出到頁面上。

ViewBag的訪問與ViewData類似,只不過ViewBag是動態對象,可以認為它的類型並沒有發生改變,繼續按照之前的類型進行使用:

<h4>@ViewBag.Name</h4>

對於ViewModel的使用,View內置了一個dynamic的Model屬性,在不做特殊處理的情況下,我們在頁面上使用@Model 會得到一個dynamic對象(如果傳了ViewModel的話)。雖然也能用,但是這不太友好。

這時候,就需要我們在視圖的開頭處,添加:

@model ViewModelTestModel

這時候,再使用@Model的時候,就會自動解析成ViewModelTestModel了。

整體Index.cshtml內容如下:

@model ViewModelTestModel
Hello  World!
<h2>@ViewData["Title"]</h2>

<h4>@ViewBag.Name</h4>
@Model.Name +  @Model.Age

然後重啟服務后,刷新頁面,會看到類似的內容:

3. 總結

我們在這一篇介紹了視圖的一些概念,並介紹了如何使用控制器給視圖傳遞數據。下一篇將講解一下路由的高級作用,如何通過路由攜帶數據。

更多內容煩請關注我的博客《高先生小屋》

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

※推薦台中搬家公司優質服務,可到府估價

台中搬鋼琴,台中金庫搬運,中部廢棄物處理,南投縣搬家公司,好幫手搬家,西屯區搬家

真正越野賽車和實際買到的SUV有什麼區別?_網頁設計公司

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網站的第一印象網頁設計,決定了客戶是否繼續瀏覽的意願。台北網動廣告製作的RWD網頁設計,採用精簡與質感的CSS語法,提升企業的專業形象與簡約舒適的瀏覽體驗,讓瀏覽者第一眼就愛上它。

2、進氣系統一輛標準的帕傑羅過水雖然沒有問題,但是速度要十分慢,快過沙子慢淌水這個道理大家都懂,但是賽車時前方的水坑也要全速沖,所以不改進氣車子過水的時候可能發動機就進水了。3、懸架懸架系統主要承擔緩衝和吸收衝擊的作用,以保護車身機械系統以及車輛電氣系統,如果懸架的強度不夠不能支持車輛飛躍等,這樣就導致每次爬坡到坡頂都必須減速,會浪費掉許多的時間。

對於越野車,不同的人有不同的理解,但是對於大多數人來說,大梁四驅就是越野車,可是如果越野賽車呢?

越野賽車大家都十分陌生,對於小編來說也一樣,不過通過這次參加騰衝站COC越野賽的機會,小編有幸感受到了真正的越野賽車的魅力。

越野賽車和越野車的區別在哪呢?

速度

歸根結底就在於速度兩字,其實對於豐田普拉多/三菱帕傑羅這類的我們熟悉的越野車來說,越野場地的大多數項目都不是問題,但是如果讓一輛原廠狀態的大切諾基來用越野賽車的跑法跑越野賽道的話,可能跑不完一圈車子就趴窩了。

原因就在於越野賽車需要有較高的速度,這樣就需要車子的輪胎/車身/懸架能夠承受更大的衝擊,因此真正的越野賽車對於這些方面都會進行改裝,那麼如果想讓你的車擁有迅速快速地跑完越野賽道的實力,需要改裝哪些地方呢?

1、輪胎

輪胎是和路面接觸的地方,越野賽場的路面可不一般,看似平坦實際上尖石粒到處都是,而越野賽車需要以70km/h左右的高速度碾壓路面,因此輪胎的耐用性十分重要,

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

台中景泰電動車行只是一個單純的理由,將來台灣的環境,出門可以自由放心的深呼吸,讓空氣回歸自然的乾淨,減少污染,留給我們下一代有好品質無空污的優質環境

而且在泥地、沙地也需要輪胎提供足夠的抓地力,在車輛飛躍的時候輪胎也需要承擔並緩衝巨大的衝擊力。所以再強的越野車,如果輪胎不行,那麼也是白搭。

2、進氣系統

一輛標準的帕傑羅過水雖然沒有問題,但是速度要十分慢,快過沙子慢淌水這個道理大家都懂,但是賽車時前方的水坑也要全速沖,所以不改進氣車子過水的時候可能發動機就進水了。

3、懸架

懸架系統主要承擔緩衝和吸收衝擊的作用,以保護車身機械系統以及車輛電氣系統,如果懸架的強度不夠不能支持車輛飛躍等,這樣就導致每次爬坡到坡頂都必須減速,會浪費掉許多的時間。

4、前後包圍

為什麼包圍要改呢?一般車輛的接近角和離去角都比較小,對於高強度越野來說肯定是不夠的,所以拆掉前後包圍是最簡單的做法,也是普遍採取的做法。

5、防翻滾架/座位

這個雖然和性能無關,但是卻是保護安全的神器,防翻滾架是每部賽車的標配,即使車輛翻滾散架,防翻滾架都會保持完整。也能夠保護車內的駕乘人員。而安全帶則無需多言了,你以為3點式安全帶夠用?

最後的最後,可能有人就會問了,我不會把車改成這個樣子啊,其實小編想說的是,有的人開車遇到溝坎減速帶什麼的都不減速的,原理其實和賽車一樣,如果你真的需要暴力開,還真的要把車子改成高強度的狀態,量產車隨便虐只會大大減少壽命,甚至會直接弄毀車輛。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

以設計的實用美學觀點,規劃出舒適、美觀的視覺畫面,有效提昇使用者的心理期待,營造出輕鬆、愉悅的網站瀏覽體驗。

買誰都不吃虧 博越和RX5該如何選?_如何寫文案

※別再煩惱如何寫文案,掌握八大原則!

什麼是銷售文案服務?A就是幫你撰寫適合的廣告文案。當您需要販售商品、宣傳活動、建立個人品牌,撰寫廣告文案都是必須的工作。

0L兩款發動機,而且主力車型就是1。8T的動力裝配。吉利博越的1。8T發動機賬面參數是184ps馬力,最大扭矩285N。m,對比RX5 1。5T 169ps馬力和250N。m的峰值扭矩還是強了不少。配置博越的配置更加豐富1。8TD自動智慧型的吉利博越和 20T兩驅自動旗艦版的榮威RX5指導價格差價僅僅只是在1000元,而且兩車的配置都算不錯,操控配置更是完全一樣。

當下的汽車增長速度最快的車型無疑是SUV,無論是合資企業還是自主品牌車企對於SUV車型的研發力度也是非常巨大,同樣作為自主品牌,吉利博越和榮威RX5各自代表了目前各自品牌的最高技術,這兩款售價區間重疊明顯,同為國貨精品的SUV如何選擇?

我們拿出這兩台車售價最接近的細分配置車型進行對比;售價也符合當下眾多購車人士的預算訴求,十五萬以內:

吉利博越2016款1.8TD 自動智慧型,售價13.98萬

榮威RX5 2016款 20T 兩驅自動旗艦版,售價13.88萬

外觀

爭議頗大VS迎合主流

吉利博越到底好不好看?不同的人有不同的答案;吉利博越的外觀一直存在着爭議,主要的焦點集中於那採用自沃爾沃概念車的漣漪式前臉格柵。但是從辨識度上說,吉利博越是成功的,漣漪式前臉已經成為了吉利的家族標誌,精緻的設計風格已經為吉利樹立起了一種不錯的品牌形象。

榮威RX5的設計則是一種非常迎合當下審美潮流的風格,前臉進氣格柵線條流暢舒展,配合上矩陣式的燈組設計,層次豐富之下更體現出不少的細節處理。榮威RX5的車身運用了大量的黃金比例理念,將整車的視覺效果做到了很好的均衡,看過實車的人都會說,RX5是一款看上去很“大氣”的SUV。

內飾

各有千秋;博越設計感更豐富

在內飾層面小編個人是比較喜歡博越的內飾設計,運用了更多的柔和曲線設計,官方稱為西湖斷橋的元素,從使用上說,博越的內飾沒有為了體現設計感而喪失人機工程的實用性。功能性按鍵布局顯得清晰明確,操作起來也十分方便。

反觀榮威RX5,

※教你寫出一流的銷售文案?

銷售文案是什麼?A文案是廣告用的文字。舉凡任何宣傳、行銷、販賣商品時所用到的文字都是文案。在網路時代,文案成為行銷中最重要的宣傳方式,好的文案可節省大量宣傳資源,達成行銷目的。

第一眼看到榮威RX5的內飾我會想到大眾,整體設計以平實簡潔的路線為主,儘管在設計感上或許相比較於博越有一定的欠缺,但是在功能性空間表現性上也一樣有着很優秀的表現。

空間

榮威RX5空間更充裕

儘管兩台車的定位都是緊湊型SUV,但是榮威RX5的車身尺寸要比博越大了半圈,作為一款有着2700軸距的SUV車型,兩車從乘坐空間表現性上說榮威RX5要更勝一籌。

動力

博越的動力更加澎湃

榮威RX5搭載的是兩套動力總成,分別是1.5T和2.0T的渦輪增壓發動機,而吉利博越同樣也是兩套動力總成,但是1.8T以及2.0L兩款發動機,而且主力車型就是1.8T的動力裝配。

吉利博越的1.8T發動機賬面參數是184ps馬力,最大扭矩285N.m,對比RX5 1.5T 169ps馬力和250N.m的峰值扭矩還是強了不少。

配置

博越的配置更加豐富

1.8TD自動智慧型的吉利博越和 20T兩驅自動旗艦版的榮威RX5指導價格差價僅僅只是在1000元,而且兩車的配置都算不錯,操控配置更是完全一樣。

但是總體上來說博越的配置更加豐富,主要的是體現在安全性配置方面,博越搭載的前排側安全氣囊和前後頭部氣囊在名為旗艦版的榮威RX5身上並沒有裝配。

而在燈光配置方面,榮威RX5的配置則顯得有些寒酸了,原廠搭載的是鹵素光源,可以選裝LED,並且沒有霧燈標配,而這一點,價位幾乎相同的博越則有了更完善的裝備。

編輯總結:兩款車對比下來,更建議購買的還是吉利博越,榮威RX5更多的是以2.0T的車型作為主力車型向合資緊湊型SUV進行競爭,而1.5T的動力車型所搭載的配置則明顯的與2.0T車型拉開了差距。

榮威RX5的2.0T車型售價在16.68-18.68萬,從定價來說這個價格並不算低,儘管以互聯網為背景的科技性配置的確是一大亮點,但是更多的消費者更注重的是車內所能給予的實用性配置享受,而兩車相比,13.98萬的吉利博越1.8TD自動智慧型則更具有性價比。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

擁有後台管理系統的網站,將擁有強大的資料管理與更新功能,幫助您隨時新增網站的內容並節省網站開發的成本。

20萬預算買進口SUV 動力和個性化這三台都可以_網頁設計公司

※想知道最厲害的網頁設計公司嚨底家"!

RWD(響應式網頁設計)是透過瀏覽器的解析度來判斷要給使用者看到的樣貌

一些細節方面,卡繽還是做得比較有意思的。副駕駛位的抽屜箱是橫拉打開,而非傳統的圓弧形打開,這樣駕駛員就可以在打開抽屜時,清楚看到裏面的東西。前排座椅後方用了五條限位繩來固定物品,而非傳統的網兜,這點我就覺得不慎實用。

斯巴魯XV

指導價:18.98—22.98萬

編者點評:

XV可能是這個價位區間中我們比較常見的進口SUV,以這個價格就能享受到水平對置發動機與全時四驅所帶來的快感,確實不賴。然而只有2.0L自然吸氣發動機+CVT變速箱,動力上難言充足,也僅僅是夠用而已。儘管跑得不夠快,但是有全時四驅的加持,操控性那是沒得說。

進入車廂內,XV給人的感覺比較嚴肅,到處都是黑色和銀色的內飾。不過,這也有一個好處,那就是比較耐臟。雖然整個內飾略簡單,但是從那些接線處細細一看還是能發現XV的做工有一種精緻范。

保養方面,按照4S店的建議,6萬公里下來的保養費用為10594元。這個價格就算是比較貴了,不過,想想要維護一台水平對置發動機,這又似乎變得不是那麼難以接受。

雷諾 卡繽

指導價:13.98—18.88萬

編者點評:

卡繽這台車可能很少有人聽說過,不過它可曾是歐洲小型SUV的銷量冠軍。能做冠軍肯定是有不少真材實料的。先從外形說起,卡繽採用了時下流行的雙色車身,而且都是明亮的色彩,車尾還可以選擇一些拉花,整台車看起來就是各種酷炫,相當討年輕人喜歡的風格。

動力總成方面,卡繽搭載的是1.2T渦輪增壓發動機+6速雙離合。1.2T的發動機最大功率為85KW,最大扭矩為190Nm。動力相對pSA那台1.2T發動機來說,是“書生”了一點點,不過卡繽也不是一台性能取向的車。

一些細節方面,卡繽還是做得比較有意思的。副駕駛位的抽屜箱是橫拉打開,而非傳統的圓弧形打開,

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

透過資料庫的網站架設建置,建立公司的形象或購物系統,並提供最人性化的使用介面,讓使用者能即時接收到相關的資訊

這樣駕駛員就可以在打開抽屜時,清楚看到裏面的東西。前排座椅後方用了五條限位繩來固定物品,而非傳統的網兜,這點我就覺得不慎實用。尾箱隔板有兩面,一面是常規的絨面,可以用來放置一些摩擦力較小的物品,防止滑動;另一面是光面,可以用來放一些比較容易弄髒的物品,這樣也便於清潔。在這點上,法國人還是想得比較細心。

保養方面,6萬公里中的保養費用為5926元。這樣的養護成本在同級別中,算是很便宜了。雷諾還給出了一個殺手鐧,那就是送車主10次免費更換機油和機油濾清器,這樣的待遇恐怕也沒哪個廠家敢給。

鈴木 吉姆尼

指導價:14.18—16.08萬

編者點評:

吉姆尼這台車是很多越野愛好者心中的完美伴侶,車身長度僅為3665mm,這樣短小精悍的車身造型可以很好地通過不少比較窄的路面,在城市中穿越也比較輕鬆,特別是在找停車位的時候,許多地方的停車位規劃並不完善,而且尺寸偏小。這時候,吉姆尼甚至比普通轎車還更容易泊進去。

不過,吉姆尼的安全配置就十分寒酸了,僅僅有ABS防抱死,連ESp都沒有。其他配置方面,也同樣乏善可陳。買回來要好好越野肯定是要先去改裝一下的,起碼給它裝兩把前橋和後橋的差速鎖。

吉姆尼的內飾可以說是樸實無華,一眼望去儘是各種塑料。話雖如此,但是它的塑料還是做得比較用心的。油耗方面,手動版的百公里平均油耗為7.7L,而自動版的則為9.3L。相差還是挺大的。可以看出,儘管吉姆尼的排量較低,但也不是一個省油的主。

除非你是一個越野愛好者,不然這台車在城市中也許只能帶給你較高的回頭率。

這三款車各有各的特點,XV算是進口車中比較常見的,卡繽則充滿陽光,相當有范,而吉姆尼顯然就屬於劍走偏鋒的類型。選擇一台進口車,同時也是選擇了另一種生活。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

當全世界的人們隨著網路時代而改變向上時您還停留在『網站美醜不重要』的舊有思維嗎?機會是留給努力改變現況的人們,別再浪費一分一秒可以接觸商機的寶貴時間!

超乾貨!為了讓你徹底弄懂MySQL事務日誌,我通宵肝出了這份圖解!_如何寫文案

※別再煩惱如何寫文案,掌握八大原則!

什麼是銷售文案服務?A就是幫你撰寫適合的廣告文案。當您需要販售商品、宣傳活動、建立個人品牌,撰寫廣告文案都是必須的工作。

還記得剛上研究生的時候,導師常掛在嘴邊的一句話,“科研的基礎不過就是數據而已。”如今看來,無論是人文社科,還是自然科學,或許都可在一定程度上看作是數據的科學。

倘若剝開研究領域的外衣,將人的操作抽象出來,那麼科研的過程大概就是根據數據流動探索其中的未知信息吧。當然科學研究的範疇涵蓋甚廣,也不是一兩句話能夠拎得清的。不過從這個角度上的闡述,也只是為了引出數據的重要性。

在當今社會,充斥着大量的數據。從眾多APP上的賬戶資料到銀行信用體系等個人檔案,都離不開對大量數據的組織、存儲和管理。而這,便是數據庫存在的目的和價值。

目前數據庫的類型主要分為兩種,一種是關係型數據庫,另一種是非關係型數據庫(NoSQL)。而我們今天的主角MySQL就是關係型數據庫中的一種。

1 關係型數據庫與NoSQL

關係型數據庫,顧名思義,是指存儲的數據之間具有關係。這種所謂的關係通常用二維表格中的行列來表示,即一個二維表的邏輯結構能夠反映表中數據的存儲關係。

概念總是拗口難懂的。那麼簡單來說,關係型數據庫的存儲就是按照表格進行的。數據的存儲實際上就是對一個或者多個表格的存儲。通過對這些表格進行分類、合併、連接或者選取等運算來實現對數據庫的管理。常見的關係型數據庫有MySQL、Oracle、DB2和SqlServer等。

非關係型數據庫(NoSQL)是相對於關係型數據庫的一種泛指,它的特點是去掉了關係型數據庫中的關係特性,從而可獲得更好的擴展性。NoSQL並沒有嚴格的存儲方式,但採用不同的存儲結構都是為了獲得更高的性能和更高的併發。NoSQL根據存儲方式可分為四大類,鍵值存儲數據庫、列存儲數據庫、文檔型數據庫和圖形數據庫。這四種數據的存儲原理不盡相同,因而在應用場景上也有些許的差異。一般常用的有作為數據緩存的redis和分佈式系統的HBase。目前常見的數據庫排名可見網站:

https://db-engines.com/en/ranking

關係型數據庫與非關係型數據庫本質上的區別就在於存儲的數據是否具有一定的邏輯關係,由此產生的兩類數據庫看的性能和優劣勢上也有一定的區別。二者對比可見下圖。

2 MySQL簡介

介紹

在關係型數據庫中,MySQL可以說是其中的王者。它是目前最流行的數據庫之一,由瑞典 MySQL AB 公司開發,目前屬於 Oracle 公司。MySQL數據庫具有以下幾個方面的優勢:

  • 體積小、速度快;
  • 代碼開源,採用了 GPL 協議,可以修改源碼來開發自己的 MySQL 系統;
  • 支持大型的數據庫,可以處理擁有上千萬條記錄的大型數據庫;
  • 使用標準的 SQL 數據語言形式,並採用優化的 SQL 查詢算法,有效地提高查詢速度;
  • 使用 C 和 C++ 編寫,並使用多種編譯器進行測試,保證源代碼的可移植性;
  • 可運行在多個系統上,並且支持多種語言;
  • 核心程序採用完全的多線程編程,可以靈活地為用戶提供服務,充分利用CPU資源。

邏輯架構

MySQL的邏輯架構可分為四層,包括連接層、服務層、引擎層和存儲層,各層的接口交互及作用如下圖所示。需要注意的是,由於本文將主要講解事務的實現原理,因此下文針對的都是InnoDB引擎下的情況。

連接層:負責處理客戶端的連接以及權限的認證。

服務層:定義有許多不同的模塊,包括權限判斷,SQL接口,SQL解析,SQL分析優化, 緩存查詢的處理以及部分內置函數執行等。MySQL的查詢語句在服務層內進行解析、優化、緩存以及內置函數的實現和存儲。

引擎層:負責MySQL中數據的存儲和提取。MySQL中的服務器層不管理事務,事務是由存儲引擎實現的。其中使用最為廣泛的存儲引擎為InnoDB,其它的引擎都不支持事務。

存儲層:負責將數據存儲與設備的文件系統中。

3 MySQL事務

事務是MySQL區別於NoSQL的重要特徵,是保證關係型數據庫數據一致性的關鍵技術。事務可看作是對數據庫操作的基本執行單元,可能包含一個或者多個SQL語句。這些語句在執行時,要麼都執行,要麼都不執行。

事務的執行主要包括兩個操作,提交和回滾。

提交:commit,將事務執行結果寫入數據庫。

回滾:rollback,回滾所有已經執行的語句,返回修改之前的數據。

MySQL事務包含四個特性,號稱ACID四大天王。

原子性(Atomicity) :語句要麼全執行,要麼全不執行,是事務最核心的特性,事務本身就是以原子性來定義的;實現主要基於undo log日誌實現的。

持久性(Durability :保證事務提交后不會因為宕機等原因導致數據丟失;實現主要基於redo log日誌。

隔離性(Isolation) :保證事務執行盡可能不受其他事務影響;InnoDB默認的隔離級別是RR,RR的實現主要基於鎖機制、數據的隱藏列、undo log和類next-key lock機制。

一致性(Consistency) :事務追求的最終目標,一致性的實現既需要數據庫層面的保障,也需要應用層面的保障。

原子性

事務的原子性就如原子操作一般,表示事務不可再分,其中的操作要麼都做,要麼都不做;如果事務中一個SQL語句執行失敗,則已執行的語句也必須回滾,數據庫退回到事務前的狀態。只有0和1,沒有其它值。

事務的原子性表明事務就是一個整體,當事務無法成功執行的時候,需要將事務中已經執行過的語句全部回滾,使得數據庫回歸到最初未開始事務的狀態。

事務的原子性就是通過undo log日誌進行實現的。當事務需要進行回滾時,InnoDB引擎就會調用undo log日誌進行SQL語句的撤銷,實現數據的回滾。

持久性

事務的持久性是指當事務提交之後,數據庫的改變就應該是永久性的,而不是暫時的。這也就是說,當事務提交之後,任何其它操作甚至是系統的宕機故障都不會對原來事務的執行結果產生影響。

事務的持久性是通過InnoDB存儲引擎中的redo log日誌來實現的,具體實現思路見下文。

隔離性

原子性和持久性是單個事務本身層面的性質,而隔離性是指事務之間應該保持的關係。隔離性要求不同事務之間的影響是互不干擾的,一個事務的操作與其它事務是相互隔離的。

由於事務可能並不只包含一條SQL語句,所以在事務的執行期間很有可能會有其它事務開始執行。因此多事務的併發性就要求事務之間的操作是相互隔離的。這一點跟多線程之間數據同步的概念有些類似。

鎖機制

事務之間的隔離,是通過鎖機制實現的。當一個事務需要對數據庫中的某行數據進行修改時,需要先給數據加鎖;加了鎖的數據,其它事務是不運行操作的,只能等待當前事務提交或回滾將鎖釋放。

鎖機制並不是一個陌生的概念,在許多場景中都會利用到不同實現的鎖對數據進行保護和同步。而在MySQL中,根據不同的劃分標準,還可將鎖分為不同的種類。

按照粒度劃分:行鎖、表鎖、頁鎖

按照使用方式劃分:共享鎖、排它鎖

按照思想劃分:悲觀鎖、樂觀鎖

鎖機制的知識點很多,由於篇幅不好全部展開講。這裏對按照粒度劃分的鎖進行簡單介紹。

粒度:指數據倉庫的數據單位中保存數據的細化或綜合程度的級別。細化程度越高,粒度級就越小;相反,細化程度越低,粒度級就越大。

MySQL按照鎖的粒度劃分可以分為行鎖、表鎖和頁鎖。

行鎖:粒度最小的鎖,表示只針對當前操作的行進行加鎖;

表鎖:粒度最大的鎖,表示當前的操作對整張表加鎖;

頁鎖:粒度介於行級鎖和表級鎖中間的一種鎖,表示對頁進行加鎖。

這三種鎖是在不同層次上對數據進行鎖定,由於粒度的不同,其帶來的好處和劣勢也不一而同。

表鎖在操作數據時會鎖定整張表,因而併發性能較差;

行鎖則只鎖定需要操作的數據,併發性能好。但是由於加鎖本身需要消耗資源(獲得鎖、檢查鎖、釋放鎖等都需要消耗資源),因此在鎖定數據較多情況下使用表鎖可以節省大量資源。

MySQL中不同的存儲引擎能夠支持的鎖也是不一樣的。MyIsam只支持表鎖,而InnoDB同時支持表鎖和行鎖,且出於性能考慮,絕大多數情況下使用的都是行鎖。

併發讀寫問題

在併發情況下,MySQL的同時讀寫可能會導致三類問題,臟讀、不可重複度和幻讀。

(1)臟讀:當前事務中讀到其他事務未提交的數據,也就是臟數據。

以上圖為例,事務A在讀取文章的閱讀量時,讀取到了事務B為提交的數據。如果事務B最後沒有順利提交,導致事務回滾,那麼實際上閱讀量並沒有修改成功,而事務A卻是讀到的修改后的值,顯然不合情理。

(2)不可重複讀:在事務A中先後兩次讀取同一個數據,但是兩次讀取的結果不一樣。臟讀與不可重複讀的區別在於:前者讀到的是其他事務未提交的數據,後者讀到的是其他事務已提交的數據。

以上圖為例,事務A在先後讀取文章閱讀量的數據時,結果卻不一樣。說明事務A在執行的過程中,閱讀量的值被其它事務給修改了。這樣使得數據的查詢結果不再可靠,同樣也不合實際。

(3)幻讀:在事務A中按照某個條件先後兩次查詢數據庫,兩次查詢結果的行數不同,這種現象稱為幻讀。不可重複讀與幻讀的區別可以通俗的理解為:前者是數據變了,後者是數據的行數變了。

以上圖為例,當對0<閱讀量<100的文章進行查詢時,先查到了一個結果,後來查詢到了兩個結果。這表明同一個事務的查詢結果數不一,行數不一致。這樣的問題使得在根據某些條件對數據篩選的時候,前後篩選結果不具有可靠性。

隔離級別

根據上面這三種問題,產生了四種隔離級別,表明數據庫不同程度的隔離性質。

在實際的數據庫設計中,隔離級別越高,導致數據庫的併發效率會越低;而隔離級別太低,又會導致數據庫在讀寫過程中會遇到各種亂七八糟的問題。

因此在大多數數據庫系統中,默認的隔離級別時讀已提交(如Oracle)或者可重複讀RR(MySQL的InnoDB引擎)。

MVCC

又是一個難嚼的大塊頭。MVCC就是用來實現上面的第三個隔離級別,可重複讀RR。

MVCC:Multi-Version Concurrency Control,即多版本的併發控制協議。

MVCC的特點就是在同一時刻,不同事務可以讀取到不同版本的數據,從而可以解決臟讀和不可重複讀的問題。

MVCC實際上就是通過數據的隱藏列和回滾日誌(undo log),實現多個版本數據的共存。這樣的好處是,使用MVCC進行讀數據的時候,不用加鎖,從而避免了同時讀寫的衝突。

在實現MVCC時,每一行的數據中會額外保存幾個隱藏的列,比如當前行創建時的版本號和刪除時間和指向undo log的回滾指針。這裏的版本號並不是實際的時間值,而是系統版本號。每開始新的事務,系統版本號都會自動遞增。事務開始時的系統版本號會作為事務的版本號,用來和查詢每行記錄的版本號進行比較。

每個事務又有自己的版本號,這樣事務內執行數據操作時,就通過版本號的比較來達到數據版本控制的目的。

另外,InnoDB實現的隔離級別RR時可以避免幻讀現象的,這是通過next-key lock機制實現的。這裏簡單講講吧。

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

擁有後台管理系統的網站,將擁有強大的資料管理與更新功能,幫助您隨時新增網站的內容並節省網站開發的成本。

next-key lock實際上就是行鎖的一種,只不過它不只是會鎖住當前行記錄的本身,還會鎖定一個範圍。比如上面幻讀的例子,開始查詢0<閱讀量<100的文章時,只查到了一個結果。next-key lock會將查詢出的這一行進行鎖定,同時還會對0<閱讀量<100這個範圍進行加鎖,這實際上是一種間隙鎖。間隙鎖能夠防止其他事務在這個間隙修改或者插入記錄。這樣一來,就保證了在0<閱讀量<100這個間隙中,只存在原來的一行數據,從而避免了幻讀。

間隙鎖:封鎖索引記錄中的間隔

雖然InnoDB使用next-key lock能夠避免幻讀問題,但卻並不是真正的可串行化隔離。再來看一個例子吧。

首先提一個問題,在T6事務A提交事務之後,猜一猜文章A和文章B的閱讀量為多少?

答案是,文章AB的閱讀量都被修改成了10000。這代表着事務B的提交實際上對事務A的執行產生了影響,表明兩個事務之間並不是完全隔離的。雖然能夠避免幻讀現象,但是卻沒有達到可串行化的級別。

這還說明,避免臟讀、不可重複讀和幻讀,是達到可串行化的隔離級別的必要不充分條件。可串行化是都能夠避免臟讀、不可重複讀和幻讀,但是避免臟讀、不可重複讀和幻讀卻不一定達到了可串行化。

一致性

一致性是指事務執行結束后,數據庫的完整性約束沒有被破壞,事務執行的前後都是合法的數據狀態。

一致性是事務追求的最終目標:前面提到的原子性、持久性和隔離性,都是為了保證數據庫狀態的一致性。

這就不多說了吧。你細品。

4 MySQL日誌系統

了解完MySQL的基本架構,大體上能夠對MySQL的執行流程有了比較清晰的認知。接下來我將在講述MySQL事務之前,先為大家介紹以下日誌系統,以方便之後更好的理解事務的特性和實現。

MySQL日誌系統是數據庫的重要組件,用於記錄數據庫的更新和修改。若數據庫發生故障,可通過不同日誌記錄恢複數據庫的原來數據。因此實際上日誌系統直接決定着MySQL運行的魯棒性和穩健性。

MySQL的日誌有很多種,如二進制日誌(binlog)、錯誤日誌、查詢日誌、慢查詢日誌等,此外InnoDB存儲引擎還提供了兩種日誌:redo log(重做日誌)和undo log(回滾日誌)。這裏將重點針對InnoDB引擎,對重做日誌、回滾日誌和二進制日誌這三種進行分析。

重做日誌(redo log)

重做日誌(redo log)是InnoDB引擎層的日誌,用來記錄事務操作引起數據的變化,記錄的是數據頁的物理修改。

重做日記的作用其實很好理解,我打個比方。數據庫中數據的修改就好比你寫的論文,萬一哪天論文丟了怎麼呢?以防這種不幸的發生,我們可以在寫論文的時候,每一次修改都拿個小本本記錄一下,記錄什麼時間對某一頁進行了怎麼樣的修改。這就是重做日誌。

InnoDB引擎對數據的更新,是先將更新記錄寫入redo log日誌,然後會在系統空閑的時候或者是按照設定的更新策略再將日誌中的內容更新到磁盤之中。這就是所謂的預寫式技術(Write Ahead logging)。這種技術可以大大減少IO操作的頻率,提升數據刷新的效率。

臟數據刷盤

值得注意的是,redo log日誌的大小是固定的,為了能夠持續不斷的對更新記錄進行寫入,在redo log日誌中設置了兩個標誌位置,checkpoint和write_pos,分別表示記錄擦除的位置和記錄寫入的位置。redo log日誌的數據寫入示意圖可見下圖。

當write_pos標誌到了日誌結尾時,會從結尾跳至日誌頭部進行重新循環寫入。所以redo log的邏輯結構並不是線性的,而是可看作一個圓周運動。write_pos與checkpoint中間的空間可用於寫入新數據,寫入和擦除都是往後推移,循環往複的。

當write_pos追上checkpoint時,表示redo log日誌已經寫滿。這時不能繼續執行新的數據庫更新語句,需要停下來先刪除一些記錄,執行checkpoint規則騰出可寫空間。

checkpoint規則:checkpoint觸發后,將buffer中臟數據頁和臟日誌頁都刷到磁盤。

臟數據:指內存中未刷到磁盤的數據。

redo log中最重要的概念就是緩衝池buffer pool,這是在內存中分配的一個區域,包含了磁盤中部分數據頁的映射,作為訪問數據庫的緩衝。

當請求讀取數據時,會先判斷是否在緩衝池命中,如果未命中才會在磁盤上進行檢索後放入緩衝池;

當請求寫入數據時,會先寫入緩衝池,緩衝池中修改的數據會定期刷新到磁盤中。這一過程也被稱之為刷臟 。

因此,當數據修改時,除了修改buffer pool中的數據,還會在redo log中記錄這次操作;當事務提交時,會根據redo log的記錄對數據進行刷盤。如果MySQL宕機,重啟時可以讀取redo log中的數據,對數據庫進行恢復,從而保證了事務的持久性,使得數據庫獲得crash-safe能力。

臟日誌刷盤

除了上面提到的對於臟數據的刷盤,實際上redo log日誌在記錄時,為了保證日誌文件的持久化,也需要經歷將日誌記錄從內存寫入到磁盤的過程。redo log日誌可分為兩個部分,一是存在易失性內存中的緩存日誌redo log buff,二是保存在磁盤上的redo log日誌文件redo log file。

為了確保每次記錄都能夠寫入到磁盤中的日誌中,每次將redo log buffer中的日誌寫入redo log file的過程中都會調用一次操作系統的fsync操作。

fsync函數:包含在UNIX系統頭文件#include <unistd.h>中,用於同步內存中所有已修改的文件數據到儲存設備。

在寫入的過程中,還需要經過操作系統內核空間的os buffer。redo log日誌的寫入過程可見下圖。

二進制日誌(binlog)

二進制日誌binlog是服務層的日誌,還被稱為歸檔日誌。binlog主要記錄數據庫的變化情況,內容包括數據庫所有的更新操作。所有涉及數據變動的操作,都要記錄進二進制日誌中。因此有了binlog可以很方便的對數據進行複製和備份,因而也常用作主從庫的同步。

這裏binlog所存儲的內容看起來似乎與redo log很相似,但是其實不然。redo log是一種物理日誌,記錄的是實際上對某個數據進行了怎麼樣的修改;而binlog是邏輯日誌,記錄的是SQL語句的原始邏輯,比如”給ID=2這一行的a字段加1 “。binlog日誌中的內容是二進制的,根據日記格式參數的不同,可能基於SQL語句、基於數據本身或者二者的混合。一般常用記錄的都是SQL語句。

這裏的物理和邏輯的概念,我的個人理解是:

物理的日誌可看作是實際數據庫中數據頁上的變化信息,只看重結果,而不在乎是通過“何種途徑”導致了這種結果;

邏輯的日誌可看作是通過了某一種方法或者操作手段導致數據發生了變化,存儲的是邏輯性的操作。

同時,redo log是基於crash recovery,保證MySQL宕機后的數據恢復;而binlog是基於point-in-time recovery,保證服務器可以基於時間點對數據進行恢復,或者對數據進行備份。

事實上最開始MySQL是沒有redo log日誌的。因為起先MySQL是沒有InnoDB引擎的,自帶的引擎是MyISAM。binlog是服務層的日誌,因此所有引擎都能夠使用。但是光靠binlog日誌只能提供歸檔的作用,無法提供crash-safe能力,所以InnoDB引擎就採用了學自於Oracle的技術,也就是redo log,這才擁有了crash-safe能力。這裏對redo log日誌和binlog日誌的特點分別進行了對比:

在MySQL執行更新語句時,都會涉及到redo log日誌和binlog日誌的讀寫。一條更新語句的執行過程如下:

從上圖可以看出,MySQL在執行更新語句的時候,在服務層進行語句的解析和執行,在引擎層進行數據的提取和存儲;同時在服務層對binlog進行寫入,在InnoDB內進行redo log的寫入。

不僅如此,在對redo log寫入時有兩個階段的提交,一是binlog寫入之前prepare狀態的寫入,二是binlog寫入之後commit狀態的寫入。

之所以要安排這麼一個兩階段提交,自然是有它的道理的。現在我們可以假設不採用兩階段提交的方式,而是採用“單階段”進行提交,即要麼先寫入redo log,后寫入binlog;要麼先寫入binlog,后寫入redo log。這兩種方式的提交都會導致原先數據庫的狀態和被恢復后的數據庫的狀態不一致。

先寫入redo log,后寫入binlog:

在寫完redo log之後,數據此時具有crash-safe能力,因此系統崩潰,數據會恢復成事務開始之前的狀態。但是,若在redo log寫完時候,binlog寫入之前,系統發生了宕機。此時binlog沒有對上面的更新語句進行保存,導致當使用binlog進行數據庫的備份或者恢復時,就少了上述的更新語句。從而使得id=2這一行的數據沒有被更新。

先寫入binlog,后寫入redo log:

寫完binlog之後,所有的語句都被保存,所以通過binlog複製或恢復出來的數據庫中id=2這一行的數據會被更新為a=1。但是如果在redo log寫入之前,系統崩潰,那麼redo log中記錄的這個事務會無效,導致實際數據庫中id=2這一行的數據並沒有更新。

由此可見,兩階段的提交就是為了避免上述的問題,使得binlog和redo log中保存的信息是一致的。

回滾日誌(undo log)

回滾日誌同樣也是InnoDB引擎提供的日誌,顧名思義,回滾日誌的作用就是對數據進行回滾。當事務對數據庫進行修改,InnoDB引擎不僅會記錄redo log,還會生成對應的undo log日誌;如果事務執行失敗或調用了rollback,導致事務需要回滾,就可以利用undo log中的信息將數據回滾到修改之前的樣子。

但是undo log不redo log不一樣,它屬於邏輯日誌。它對SQL語句執行相關的信息進行記錄。當發生回滾時,InnoDB引擎會根據undo log日誌中的記錄做與之前相反的工作。比如對於每個數據插入操作(insert),回滾時會執行數據刪除操作(delete);對於每個數據刪除操作(delete),回滾時會執行數據插入操作(insert);對於每個數據更新操作(update),回滾時會執行一個相反的數據更新操作(update),把數據改回去。undo log由兩個作用,一是提供回滾,二是實現MVCC。

5 主從複製

主從複製的概念很簡單,就是從原來的數據庫複製一個完全一樣的數據庫,原來的數據庫稱作主數據庫,複製的數據庫稱為從數據庫。從數據庫會與主數據庫進行數據同步,保持二者的數據一致性。

主從複製的原理實際上就是通過bin log日誌實現的。bin log日誌中保存了數據庫中所有SQL語句,通過對bin log日誌中SQL的複製,然後再進行語句的執行即可實現從數據庫與主數據庫的同步。

主從複製的過程可見下圖。主從複製的過程主要是靠三個線程進行的,一個運行在主服務器中的發送線程,用於發送binlog日誌到從服務器。兩外兩個運行在從服務器上的I/O線程和SQL線程。I/O線程用於讀取主服務器發送過來的binlog日誌內容,並拷貝到本地的中繼日誌中。SQL線程用於讀取中繼日誌中關於數據更新的SQL語句並執行,從而實現主從庫的數據一致。

之所以需要實現主從複製,實際上是由實際應用場景所決定的。主從複製能夠帶來的好處有:

1 通過複製實現數據的異地備份,當主數據庫故障時,可切換從數據庫,避免數據丟失。

2 可實現架構的擴展,當業務量越來越大,I/O訪問頻率過高時,採用多庫的存儲,可以降低磁盤I/O訪問的頻率,提高單個機器的I/O性能。

3 可實現讀寫分離,使數據庫能支持更大的併發。

4 實現服務器的負載均衡,通過在主服務器和從服務器之間切分處理客戶查詢的負荷。

6 總結

MySQL數據庫應該算是程序員必須掌握的技術之一了。無論是項目過程中還是面試中,MySQL都是非常重要的基礎知識。不過,對於MySQL來說,真的東西太多了。我在寫這篇文章的時候,查閱了大量的資料,發現越看不懂的越多。還真是應了那句話:

你知道的越多,不知道的也就越多。

這篇文章着重是從理論的角度去解析MySQL基本的事務和日誌系統的基本原理,我在表述的時候盡可能的避免採用實際的代碼去描述。即便是這篇將近一萬字+近二十副純手工繪製的圖解,也難以將MySQL的博大精深分析透徹。

但是我相信,對於初學者而言,這些理論能夠讓你對MySQL有一個整體的感知,讓你對“何謂關係型數據庫”這麼一個問題有了比較清晰的認知;而對於熟練掌握MySQL的大佬來說,或許本文也能夠喚醒你塵封已久的底層理論基礎,對你之後的面試也會有一定幫助。

技術這種東西沒有絕對的對錯,倘若文中有誤還請諒解,並歡迎與我討論。自主思考永遠比被動接受更有效。

7 reference

https://www.cnblogs.com/kismetv/p/10331633.html

https://www.cnblogs.com/ivy-zheng/p/11094528.html

https://blog.csdn.net/qq_39016934/article/details/90116706

https://www.jianshu.com/p/5af73b203f2a

https://www.cnblogs.com/f-ck-need-u/archive/2018/05/08/9010872.html#auto_id_2

微信搜索業餘碼農,閱讀更多技術隨筆。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

※教你寫出一流的銷售文案?

銷售文案是什麼?A文案是廣告用的文字。舉凡任何宣傳、行銷、販賣商品時所用到的文字都是文案。在網路時代,文案成為行銷中最重要的宣傳方式,好的文案可節省大量宣傳資源,達成行銷目的。

帶你學夠浪:Go語言基礎系列-環境配置和 Hello world_網頁設計公司

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

當全世界的人們隨著網路時代而改變向上時您還停留在『網站美醜不重要』的舊有思維嗎?機會是留給努力改變現況的人們,別再浪費一分一秒可以接觸商機的寶貴時間!

文章每周持續更新,原創不易,「三連」讓更多人看到是對我最大的肯定。可以微信搜索公眾號「 後端技術學堂 」第一時間閱讀(一般比博客早更新一到兩篇)

前面幾周陸陸續續寫了一些後端技術的文章,包括數據庫、微服務、內存管理等等,我比較傾向於成體系的學習,所以數據庫和微服務還有後續系列文章補充。

最近工作上比較多的 Golang 編程,現在很多互聯網公司都在轉向 Golang 開發,所以打算寫一寫有關 Go 語言學習的系列文章,目標是從 Go 基礎到進階輸出一系列文章,沉澱下這些知識同時也給大家做參考,力求做到通俗易懂,即使你是 Golang 小白也能看懂,如果你是老手也能溫故知新。

本文將要和你分享 linux 下安裝 Golang 環境,並且講解如何通過配置 VSCode 遠程開發調試 Golang 程序。

下載源碼

你可以用系統自帶的包管理工具比如 yum 或 apt-get 來安裝Golang開發環境。不過,為了通用性,我選擇通過源碼的方式來安裝和講解,在官網下載源碼,下載地址 https://golang.org/dl/

手動安裝

解壓安裝

我這裏下載下來的源碼包 go1.14.2.linux-amd64.tar.gz 放到遠程 Linux 服務器目錄下。執行以下命令安裝到 /usr/local 目錄。

tar -zxvf -C /usr/local/ `go1.14.2.linux-amd64.tar.gz`

創建工作空間

工作空間是你Go項目的「工作目錄」,挑選一個合適目錄,執行下面操作:

mkdir GoPath
mkdir -p GoPath/src
mkdir -p GoPath/bin
mkdir -p GoPath/pkg

三個目錄含義:

  src: 源碼路徑(例如:.go、.c、.h、.s 等)
  pkg: 編譯包時,生成的.a文件存放路徑
  bin: 編譯生成的可執行文件路徑

配置環境變量

安裝過程中有這麼幾個環境變量需要配置,先來了解一下:

GOROOT:Go的安裝路徑,也就是前面我們解壓到的目錄 /usr/local/go。

GOBIN:Go項目的二進制文件存放目錄。

GOPATH:Go的工作空間。前面有介紹的工作空間目錄。

在 /etc/profile 文件追加以下內容完成設置。

export GOROOT=/usr/local/go
export GOPATH=/yourpath/GoPath # 設置你自己的GoPath路徑 
export GOBIN=$GOPATH/bin
export PATH=$PATH:$GOROOT/bin  # 加入到PATH環境變量
export PATH=$PATH:$GOPATH/bin
# source /etc/profile #立即生效

驗證安裝

# go version  #檢查版本
# go version go1.14.2 linux/amd64 # 輸出版本號

如果看到版本信息就代表安裝成功了!

遠程開發

上面我們在 Linux 環境下安裝好了 Golang 開發環境,但我不想每次打開終端登錄服務器編寫調試程序,怎麼才能在本地PC開發調試Golang程序呢?

看過我上一篇Vscode遠程開發的小夥伴應該能想到方法,我們就要用Vscode搭建Golang遠程開發環境。具體的遠程開發配置可以查看我的另一篇文章。

Golang開發插件

首先安裝官方推薦的 Go 開發插件,如下,點他安裝。

接着還會出現如下的提示,是因為缺少其他 Go 開發相關插件,點 install all 全都裝上就行。

Hello World

編程界有個慣例,什麼語言開始學習都是從 Hello World 開始。現在,我們就用 Golang 編寫第一個 HelloWorld 程序吧。

上代碼:

package main // 所有Go程序從main包開始運行

import "fmt" // 導入fmt包

func main() {
	fmt.Print("hello world", " i am ready to go :)\n")
	fmt.Println("hello world", "i am ready to go :)")
}

格式化 包

fmt 實現了類似 C++/C 語言的格式IO庫功能。

Print 和 Println 都可用於打印輸出,但是功能略有不同。可以看到我在Print 函數中,對后一個字符串加了空格和換行符,這樣兩個打印出來的結果是相同的。

Print

func Print(a ...interface{}) (n int, err error)

Print採用默認格式將其參數格式化並寫入標準輸出。如果兩個相鄰的參數都不是字符串,會在它們的輸出之間添加空格。返回寫入的字節數和遇到的任何錯誤。

※網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

透過資料庫的網站架設建置,建立公司的形象或購物系統,並提供最人性化的使用介面,讓使用者能即時接收到相關的資訊

Println

func Println(a ...interface{}) (n int, err error)

Println採用默認格式將其參數格式化並寫入標準輸出。總是會在相鄰參數的輸出之間添加空格並在輸出結束后添加換行符。返回寫入的字節數和遇到的任何錯誤。

調試

終端調試

在終端命令行源碼所在目錄輸入go run 運行程序。


# go run HelloWorld.go 
//輸出
hello world i am ready to go :)
hello world i am ready to go :)

也可以先編譯go build 得到可執行文件后再運行。

# go build HelloWorld.go 
# ls
HelloWorld  HelloWorld.go
# ./HelloWorld 
hello world i am ready to go :)
hello world i am ready to go :)

Vscode調試

按F5啟動調試,編輯與調試控制台輸出如下:

命令行參數獲取

命令行參數可以通過os 包的 Args 函數獲取,代碼如下:

package main

import (
	"fmt"
	"os"
	"strconv"
)

func main() {
	// 命令行參數獲取,os.Args第一個參數是程序自身
	fmt.Println(os.Args)
	for idx, args := range os.Args {
		fmt.Println("參數"+strconv.Itoa(idx)+":", args)
	}
}

終端設置

以下是帶參數argv1 argv2 運行golang程序和輸出。

# go run basic.go argv1 argv2 

# 輸出
[/tmp/go-build441686724/b001/exe/basic argv1 argv2]
參數0: /tmp/go-build441686724/b001/exe/basic
參數1: argv1
參數2: argv2

VSCode設置

launch.json文件的 args 屬性配置可以設置程序啟動調試的參數。

設置之後,按F5 啟動調試,就會在調試控制台輸出配置的參數。

環境變量獲取

命令行參數可以通過os 包的 Getenv 函數獲取,代碼如下:

package main

import (
	"fmt"
	"os"
)

func main() {
	// 獲取環境變量
	fmt.Println(os.Getenv("type"), os.Getenv("name"), os.Getenv("GOROOT"))
}

VSCode設置環境變量

launch.json 文件的 args 屬性配置可以設置 VSCode 調試的 Golang 程序環境變量。

設置的格式是:name:vaule 形式,注意都是字符串。

終端設置環境變量

終端的環境變量設置就是可以用 Linux 的 export 命令設置,之後就可以用 os.Getenv 函數讀取。

比如我們最初設置 GOROOT 環境變量的命令

export GOROOT=/usr/local/go

就可以用 os.Getenv("GOROOT") 讀取,比較簡單,這裏就不多說了。

總結

現在,你有了一個可以遠程開發調試 Golang 的環境,趕緊去寫個 hello world 體驗一下吧!今天的分享就到這,下一篇文章講解基礎語法。

老規矩,感謝各位的閱讀,文章的目的是分享對知識的理解,技術類文章我都會反覆求證以求最大程度保證準確性,若文中出現明顯紕漏也歡迎指出,我們一起在探討中學習。今天的技術分享就到這裏,我們下期再見。

Reference

設置GOPATH

Visual Studio Code變量參考

Golang 獲取系統環境變量

os庫獲取命令行參數

原創不易,不想被白票,如果在我這有收穫,就動動手指「點贊」和「轉發」是對我持續創作的最大支持。

可以微信搜索公眾號「 後端技術學堂 」回復「資料」「1024」有我給你準備的各種編程學習資料。文章每周持續更新,我們下期見!

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

※想知道最厲害的網頁設計公司嚨底家"!

RWD(響應式網頁設計)是透過瀏覽器的解析度來判斷要給使用者看到的樣貌

學習源碼的第八個月,我成了Spring的開源貢獻者_網頁設計

※推薦評價好的iphone維修中心

擁有專業的維修技術團隊,同時聘請資深iphone手機維修專家,現場說明手機問題,快速修理,沒修好不收錢

我的經歷

關注我的朋友都知道,關注兩個字划重點,要考!

我最近一直在寫Spring的文章,而且僅僅是Spring FrameWork的文章 ,從最開始的官網入門到現在源碼的深度分析。主要就是三個系列

官網入門系列,Spring官網讀書筆記,這一系列的文章是入門Spring的不二之選,也是後續源碼閱讀的基礎

雜談系列,Spring雜談,這主要是一些補充內容,可以幫助大家更全面學習到Spring中的各個知識點,同時也會分享一些源碼閱讀技巧,個人學習心得之類的,雜談嘛,就是不知道放哪裡的文章都打算放這裏,比如這篇文章。

源碼分析系列,Spring源碼解析,該專欄目前正在創作中,相對而言學習難度比較大,而且因為筆者寫的比較細,估計大部分同學看起來會很費勁,不過如果你能認真看完,收穫絕對巨大!當然有不懂得地方也可以給筆者留言,或者關注文章末尾的公眾號。

本文的主要目的是教(zhuang)學(bi)

就是從筆者的實際經驗出發,談一談怎麼成為一個開源項目的貢獻者。

我先說說我自己的經歷吧,在創作上篇文章的時候,筆者發現Spring在實例化對象的時候有這麼一段代碼,在org.springframework.beans.factory.support.ConstructorResolver#resolveConstructorArguments方法中

// 本文不探討技術細節,只是為了簡單說明這個問題,所以省略無關代碼	
private int resolveConstructorArguments(String beanName, RootBeanDefinition mbd, BeanWrapper bw,
			ConstructorArgumentValues cargs, ConstructorArgumentValues resolvedValues) {

      // ....
		for (Map.Entry<Integer, ConstructorArgumentValues.ValueHolder> entry : cargs.getIndexedArgumentValues().entrySet()) {
			int index = entry.getKey();
			if (index < 0) {
				throw new BeanCreationException(mbd.getResourceDescription(), beanName,
						"Invalid constructor argument index: " + index);
			}
            // 問題就出在這裏
			if (index > minNrOfArgs) {
				minNrOfArgs = index + 1;
			}
       // ..... 

上述代碼中,minNrOfArgs這個變量就是保存方法需要的最小參數個數,但是index是下標索引,索引是從0開始的,如果有下標為n的元素,那麼最小的參數個數應該是n+1嘛,所以if中的邏輯是沒有問題的,但是if這個判斷是有問題的,正確的做法應該是

if (index+1 > minNrOfArgs) {
    minNrOfArgs = index + 1;
}

當發現這個問題的時候,第一反應就是肯定是我的姿勢不對,錯的怎麼可能是代碼,肯定是我!

接下來,我就對這段代碼進行了慘無人道的調試,在無數次debug后,我發現,這個地方確實有問題!

在確認了這個問題之後,我要思考的就是怎麼把自己的想法反饋給Spring,換而言之,怎麼為偉大的開源來做貢獻呢?正常來要達到這個目的有兩個方式

  • 提交issue
  • 直接在GitHub上提交PR(pull request)

對應的就是在GitHub上點擊下圖紅框選中的兩個位置

如果是使用提交issue的方式,相當於給官方團隊提交了一個議題,這個議題可能是你發現代碼中的某個bug,也可能是你覺得官方的做法不夠好,你有更好的想法等等。感興趣的話,大家可以去看看Spring中現在有哪些還未關閉的issue,說不定其中一個你就能解決呢~!

如果要採用提交PR的方式的話,首先你得將代碼fork到自己的GitHub中,然後在從自己的GitHub檢出到本地,在本地做完修改后,提交到GitHub倉庫中,最後從自己的GitHub向Spring官方倉庫發起一個PR。

像我的話很早就已經將代碼fork到了自己GitHub

上圖中的第一個紅框,說明我這個倉庫是從Spring官方fork過來的,第二個紅框就是可以從這裏向Spring官方提交一個PR。關於詳細的如何提交PR,大家可以自行百度,這裏不做詳細的介紹了。

另外,說了這麼多,先給大家看下我提交的issue吧。

issue鏈接:https://github.com/spring-projects/spring-framework/issues/25130

因為內容也不長,所以我這裏把原文就直接放到下面了

In ConstructorResolver:

private int resolveConstructorArguments(String beanName, RootBeanDefinition mbd, BeanWrapper bw,
			ConstructorArgumentValues cargs, ConstructorArgumentValues resolvedValues) {
		TypeConverter customConverter = this.beanFactory.getCustomTypeConverter();
		// ...

		for (Map.Entry<Integer, ConstructorArgumentValues.ValueHolder> entry : cargs.getIndexedArgumentValues().entrySet()) {
			int index = entry.getKey();
			if (index < 0) {
				throw new BeanCreationException(mbd.getResourceDescription(), beanName,
						"Invalid constructor argument index: " + index);
			}
			if (index > minNrOfArgs) {
				minNrOfArgs = index + 1;
			}
			// ....
		}
// ....
 return minNrOfArgs;
}

I assume that method resolveConstructorArguments is to resolve contructor arguments in the XML file and return the minimum number of parameters required by contructor 。but if the first parameter is autowired , the second parameter is config by XML file,the method will not work well。

example:

public class FactoryObject {
	
 public DmzService getDmz(String name, int age, Date birthDay, OrderService orderService) {

	public DmzService getDmz(OrderService orderService,String name) {
		
		return new DmzService(orderService,name);
	}

}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
	   default-autowire="constructor">
	<bean id="factoryObject" class="com.dmz.spring.first.instantiation.service.FactoryObject"/>

	<bean class="com.dmz.spring.first.instantiation.service.OrderService" id="orderService"/>

	<bean id="dmzService" factory-bean="factoryObject" factory-method="getDmz">
		<constructor-arg index="1"  value="dmz"/>
	</bean>

</beans>

the resolveConstructorArguments method will return 1,but correct answer is 2。

I think the problem arises because of this judgment:

if (index > minNrOfArgs) {
 minNrOfArgs = index + 1;
}

It might be better to change it to look like this

if (index + 1 > minNrOfArgs) {
 minNrOfArgs = index + 1;
}s

我在提交issue時主要是按照這種思路

※網頁設計最專業,超強功能平台可客製化

窩窩以「數位行銷」「品牌經營」「網站與應用程式」「印刷品設計」等四大主軸,為每一位客戶客製建立行銷脈絡及洞燭市場先機。

  1. 首先擺出有問題的代碼
  2. 描述具體的問題,我是直接通過一個例子來描述的
  3. 說出自己的建議

這幾天我又多看了看別人提交的issue,對比起來,我覺得至少應該還要添加一點

  • 應該要明確的指出具體哪個版本上出現的問題

碰到的問題

1、擔心鬧烏龍

雖然在之前我已經調試過了無數次代碼,但是心裏還是沒譜啊。畢竟我這麼謹(cai)慎(ji)的一個人,萬一被人噴了怎麼辦?不知道你會不會這麼想,反正我當時就是這麼想的,如果你是這麼想的,建議你去看看別人提交的issue。搜索條件如下

is:closed label:”status: invalid”

我覺得你看幾個,自然就有信心了!

2、不知道要怎麼提交

每個開源的項目,只要作者希望這個項目越來越好的話,都會詳細的說明如何給這個項目做開源貢獻,Spring肯定也不例外,這裏還是以提交issue為例,當你點擊New issue的時候會出現下面這張圖

在上圖左邊的框里很明確的告訴了你提交issue應該要注意什麼

  • 首先,你應該要去Stack Overflow提問
  • 如果是bug,你應該要指明版本以及你想要做什麼
  • 如果是一個增強的話,要提供上下文並且描述清楚問題
  • 同一個問題,issue跟PR最好只提交一個,因為GitHub認為它們是一樣的,如果你還不能確定的話,先提交一個issue

而右上角還有更加詳細的文檔可供參考。

3、英文

大家應該看到了,整個issue都是用英文寫的,那麼英文不好怎麼辦呢?這個時候就要掏出我們的神器了

嗯,就是詞典,筆者習慣是使用有道詞典。我建議英文不好的同學可以這樣,先將整個issue用中文寫好,如果你真的英文一竅不通的話,可以直接通過翻譯軟件逐句翻譯,然後粘貼到GitHub上。但是千萬千萬不要使用中文,就像下面這個哥們

issue鏈接:https://github.com/spring-projects/spring-framework/pull/25127

像這種issue是會被直接打上invalid(不合格)標籤的,你就想想吧,你學不會英文,你指望我們的外國朋友能看懂中文嘛?是我中華上線五千年的文化不夠博大精深嗎?

4、擔心問題描述的不清楚

其實這個問題就是因為英文不好衍生出來的。因為英文不好,自然就會擔心我寫的東西他能不能看懂呢?我的建議就是,結合你測試的代碼去描述問題。你不用去擔心別人看不懂你寫的代碼,就以我那個issue的處理流程為例吧。

在你剛剛提交issue時,有專門的issuemaster(issue管理員)會給你提交的issue打上一個wait-for-triage的標籤,標誌這個issue是待處理的。

隨後我提交的這個issue,就被指派給了jhoeller。你要擔心他看不懂代碼嗎?給你看兩個東西吧

你知道那個紅框是啥意思嗎?就是說我發現的那個有問題代碼的類的作者就是他。

再看一張

就是說,jhoeller從2003年開始就已經是Spring這個項目的管理者以及發布經理了。2003年,我還是一個小學生……..

所以啊,只要你稍微正常點,基本上人家都能get到你的點。

給你的建議

其實筆者從發現這個問題到最終提交issue大概經過了一周時間,期間一直在猶豫要不要提交issue,就是因為上面提到的幾個問題,一直躊躇不前。但是等我下定決心要去做這件事的時候總共就花了幾個小時的時間。包括研究issue提交的規則以及寫一篇英文版的issue。並且我提交issue的第二天就馬上被處理了,並且jhoeller在 f9aae8d 這個commit中已經接受我的建議。

所以我要說的就是,

真正動手的話,不管什麼問題總能找到解決方案

而只是停留在空想,在躊躇,你永遠有一堆問題

臨淵羡魚,不如退而結網

以此文與君共勉!

如果本文對你由幫助的話,記得點個贊吧!也歡迎關注我的公眾號,微信搜索:程序員DMZ,或者掃描下方二維碼,跟着我一起認認真真學Java,踏踏實實做一個coder。

我叫DMZ,一個在學習路上匍匐前行的小菜鳥!

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

※台北網頁設計公司這麼多該如何選擇?

網動是一群專業、熱情、向前行的工作團隊,我們擁有靈活的組織與溝通的能力,能傾聽客戶聲音,激發創意的火花,呈現完美的作品

初窺Ansible playbook_貨運

※回頭車貨運收費標準

宇安交通關係企業,自成立迄今,即秉持著「以誠待人」、「以實處事」的企業信念

Ansible是一個系列文章,我會盡量以通俗易懂、詼諧幽默的總結方式給大家呈現這些枯燥的知識點,讓學習變的有趣一些。
Ansible系列博文直達鏈接:Ansible入門系列

前言

在上一篇文章中說到Ansible有兩種玩法,一種是Ansible Ad-Hoc,另一種是就是這裏要說的playbook。playbook是Ansible進行配置管理的組件,雖然Ansible的日常Ad-Hoc命令功能很強大,能完成一些基本的配置管理工作,但是Ad-Hoc命令無法支撐複雜環境的配置管理工作。在我們實際使用Ansible的工作中,大部分時間都是在編寫playbook,接下來就重點說說如何玩轉這個playbook。

執行playbook命令

我們都是按照yaml語法規則來編寫playbook,至於yaml怎麼玩,後面的文章我會總結一下的。在我們按照要求編寫好了yaml文件后,如何來執行這個yaml文件呢?

Ansible提供了一個單獨的命令:ansible-playbook命令,我們可以通過這個命令來執行yaml腳本。常見的ansible-playbook的使用方法如下:

最簡單的使用方法:

ansible-playbook copyDemo.yaml

我們還可以使用以下命令查看輸出的細節:

ansible-playbook copyDemo.yaml --verbose

我們也可以使用以下命令查看該yaml腳本將影響的主機列表:

ansible-playbook copyDemo.yaml --list-hosts

還可以使用以下命令檢查yaml腳本語法是否正確:

ansible-playbook copyDemo.yaml --syntax-check

上面的幾種使用方法基本就涵蓋了我們日常工作中80%的場景了,剩餘的20%場景,比如并行、異步等,很少用到,等真正用到的時候再去查閱相關資料也來的及。而工作中,更多的時候,我們不是在編寫playbook,就是在編寫playbook的路上。所以,接下來我重點說說如何寫這個playbook,也就是playbook的基本語法。

playbook基本語法

最基本的playbook腳本分為三個部分:

  1. 在哪些機器上以什麼身份執行
  2. 執行的任務有哪些
  3. 善後任務有哪些

我們在編寫playbook腳本的時候,總是離不開上面的三個部分的。下面先來一個稍微有點複雜的playbook腳本,讓大家先有一個整體的認識。

---
- hosts: server1
  user: root
  vars:
    http_port: 80
    max_clients: 200

  tasks:
    - name: Write apache config file
      template: src=/home/test1/httpd.j2 dest=/home/test2/httpd.conf
      notify:
        - restart apache
    - name: Ensure apache is running
      service: name=httpd state=started

  handlers:
    - name: restart apache
      service: name=httpd state=restarted

現在就對上述三部分稍作詳細總結。

主機和用戶

上面的yaml腳本,我們一開始就會看到hosts、user和vars,其中vars在後面的文章進行專門總結。而這裏的hosts和user就是表示我們這個yaml將要在哪些主機上用哪個用戶身份去操作。而這裏的深一層次的關係如下錶所示:

key 含義
hosts 為主機的IP,或者主機組名,或者關鍵字all
user 在遠程以哪個身份執行
become 切換成其他用戶身份執行,值為yes或者no
become_method 與become一起使用,值可以為sudo/su等
become_user 與become一起使用,可以是root或者其它用戶名

在實際工作中,如果我們不指定user時,則默認使用連接遠程主機的用戶進行操作,如果指定了執行用戶而與ansible_ssh_user指定用戶不一致時,則需要開啟become操作,這裏的become配置與ansible.cfg中配置將相互配合完成工作,yaml中的become優先級高於ansible.cfg中配置中的優先級。

任務列表

任務列表是整個playbook的核心,對於任務列表,我們首先需要知道以下三點內容:

※智慧手機時代的來臨,RWD網頁設計為架站首選

網動結合了許多網際網路業界的菁英共同研發簡單易操作的架站工具,及時性的更新,為客戶創造出更多的網路商機。

  • 任務是從上到下順序執行的,如果中間發生錯誤,那麼整個playbook會中止;
  • 每一個任務都是對模塊的一次調用,只是使用不同的參數和變量而已;
  • 每一個任務最好有一個name屬性,這樣在執行yaml腳本時,可以看到執行進度信息。

對於任務的參數有兩種不同的寫法,我們在編寫yaml腳本時,可以按照自己的喜好進行選擇。

寫法一:

- name: Write apache config file
  template: src=/home/test1/httpd.j2 dest=/home/test2/httpd.conf

寫法二:

- name: Write apache config file
  template: 
    src: /home/test1/httpd.j2
    dest: /home/test2/httpd.conf

這兩種寫法都是OK的,我一般喜歡第二種寫法。

最後,對於任務我們還需要特別一個點,那就是任務的執行狀態。我們在執行Ansible Ad-Hoc或者ansible-playbook的時候,在輸出中都會有一個changed字段,比如:

192.168.1.3                : ok=2    changed=0    unreachable=0    failed=0  

或者

192.168.1.3                : ok=2    changed=1    unreachable=0    failed=0

這裏的這個changed就是人物的執行狀態,但是它為什麼一會是0,一會有是1呢?這就要說到Ansible中一個叫做“冪等性”的概念。

冪等性

冪等性是數學和計算機科學上一個常見的概念,多次執行產生的結果不會發生改變,這樣的特性就被成為冪等性。

大多數的Ansible模塊在設計時保證了冪等性,冪等性保證了Ansible腳本多次執行情況下的相同結果,盡可能的避免使用那些不能滿足冪等性的模塊。比如我們經常使用的shell模塊就是非冪等性的。

我們要明白Ansible是以“結果為導向的”,我們指定了一個“目標狀態”,Ansible會自動判斷“當前狀態”是否與“目標狀態”一致,如果一致,則不進行任何操作;如果不一致,那麼就將“當前狀態”變成“目標狀態”,這就是“冪等性”,“冪等性”可以保證我們重複的執行同一項操作時,得到的結果是一樣的。

那這個冪等性與上面的changed又有什麼關係呢?且聽我下面慢慢道來!

  • 當changed為false或者0時,表示Ansible沒有進行任何操作,沒有“改變什麼”;
  • 當changed為true或者大於0時,表示Ansible執行了操作,“當前狀態”已經被Ansible改變成了“目標狀態”。

拿copy這個模塊來舉例子說明,當我們準備將一個文件通過Ansible拷貝到遠程主機時,copy模塊首先檢查遠程是否已經存在了該文件,如果不存在,則把文件拷貝過去,返回changed為大於0;如果存在時,則開始比對兩個文件的md5值,如果md5值一致,則說明兩個文件是一樣的,則不需要拷貝,此時copy模塊則什麼都不幹,返回changed為0。

總結

通過三篇文章總結了Ansible中的常用模塊、Ansible Ad-Hoc和ansible-playbook的一些慣用用法,從我的實際學習經驗來說,學到這裏,你可以將這三塊內容結合起來使用了,至少可以在你們生產環境鼓搗一下了。生來就是折騰,更何況我們這麼拚命、努力的學習呢!

果凍想,認真玩技術的地方。

2019年5月18日,於內蒙古呼和浩特。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

※評比南投搬家公司費用收費行情懶人包大公開

搬家價格與搬家費用透明合理,不亂收費。本公司提供下列三種搬家計費方案,由資深專業組長到府估價,替客戶量身規劃選擇最經濟節省的計費方式

面試官問我會不會Elasticsearch,我語塞了…_網頁設計公司

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

節能減碳愛地球是景泰電動車的理念,是創立景泰電動車行的初衷,滿意態度更是服務客戶的最高品質,我們的成長來自於你的推薦。

少點代碼,多點頭髮

本文已經收錄至我的GitHub,歡迎大家踴躍star 和 issues。

https://github.com/midou-tech/articles

從今天開始準備給大家帶來全新的一系列文章,Elasticsearch系列

新系列肯定會有很多疑惑,先為大家答疑解惑,下面是今天要講的問題

為什麼寫Elasticsearch系列文章?

之前在文章中也陸陸續續的提到過,龍叔是做搜索引擎的。搜索引擎技術屬於商業技術,大家耳熟能詳的百度搜索,Google搜索,這可都是因為把握核心搜索技術,從而誕生了商業帝國。

每個互聯網大廠都想去分一杯搜索的羹,360搜索、神馬、頭條、搜狗搜索等等,由此可見搜索技術的商業作用和機密性了。

搜索把握用戶的入口

蘑菇街的搜索引擎是一款使用C++開發、完全自研、沒有開源的搜索引擎,沒有開源就是不能隨便寫出來的。

但是現在不一樣了

第一、我離職了,離開了意味着不在持有那些商業機密了,就算不講出來我也沒啥心理負擔(但還是不能講的,離職協議寫的很清楚,不能泄露公司商業機密)。

第二、去新的公司還是在搜索領域,他們用Es Elasticsearch是一個開源搜索,開源的東西可以隨便說,但還是不能說公司的商業數據。

自己一直在搜索領域做,輸出搜索相關的文章,第一個可以讓自己更好的學習和總結,第二個可以讓粉絲們了解到搜索這個神秘的技術,增加大家自身的核心競爭力。

後面會說到,Elasticsearch是搜索引擎,但不簡單隻能使用在搜索領域,他可以作用的場景非常多。

Elasticsearch是什麼?

Elasticsearch 是一個分佈式的開源搜索和分析引擎,適用於所有類型的數據,包括文本、数字、地理空間、結構化和非結構化數據。

Elasticsearch 在 Apache Lucene 的基礎上開發而成,Elasticsearch 以其簡單的 REST 風格 API、分佈式特性、速度和可擴展性而聞名,是 Elastic Stack 的核心組件。

Elastic Stack 是適用於數據採集、充實、存儲、分析和可視化的一組開源工具。人們通常將 Elastic Stack 稱為 ELK Stack(代指 Elasticsearch、Logstash 和 Kibana),目前 Elastic Stack 包括一系列豐富的輕量型數據採集代理,這些代理統稱為 Beats,可用來向 Elasticsearch 發送數據。

※南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

搬家費用:依消費者運送距離、搬運樓層、有無電梯、步行距離、特殊地形、超重物品等計價因素後,評估每車次單

Elasticsearch 的實現原理主要分為以下幾個步驟,首先用戶將數據提交到Elasticsearch 數據中心,再通過分詞控制器去將對應的數據分詞,將其權重和分詞結果一併存入數據,當用戶搜索數據時候,再根據權重將結果排名,打分,再將返回結果呈現給用戶。

是什麼差不多搞清楚了,再說說ES都哪些成熟的應用以及在哪些領域使用。

Elasticsearch在哪些領域使用?

  • 應用程序搜索
  • 網站搜索
  • 企業搜索
  • 日誌處理和分析
  • 基礎設施指標和容器監測
  • 應用程序性能監測
  • 地理空間數據分析和可視化
  • 安全分析
  • 業務分析

Elasticsearch有哪些特點?

Elasticsearch 很快。 由於 Elasticsearch 是在 Lucene 基礎上構建而成的,所以在全文本搜索方面表現十分出色。Elasticsearch 同時還是一個近實時的搜索平台,這意味着從文檔索引操作到文檔變為可搜索狀態之間的延時很短,一般只有一秒。因此,Elasticsearch 非常適用於對時間有嚴苛要求的用例,例如安全分析和基礎設施監測。

Elasticsearch 具有分佈式的本質特徵。 Elasticsearch 中存儲的文檔分佈在不同的容器中,這些容器稱為分片,可以進行複製以提供數據冗餘副本,以防發生硬件故障。Elasticsearch 的分佈式特性使得它可以擴展至數百台(甚至數千台)服務器,並處理 PB 量級的數據。

Elasticsearch 包含一系列廣泛的功能。 除了速度、可擴展性和彈性等優勢以外,Elasticsearch 還有大量強大的內置功能(例如數據匯總和索引生命周期管理),可以方便用戶更加高效地存儲和搜索數據。

Elastic Stack 簡化了數據採集、可視化和報告過程。 通過與 Beats 和 Logstash 進行集成,用戶能夠在向 Elasticsearch 中索引數據之前輕鬆地處理數據。同時,Kibana 不僅可針對 Elasticsearch 數據提供實時可視化,同時還提供 UI 以便用戶快速訪問應用程序性能監測 (APM)、日誌和基礎設施指標等數據。

學習Elasticsearch能提高哪些競爭力?

看到Elasticsearch在這麼多的領域在使用,特點也這麼明顯。看到這裏估計都不用在說什麼核心競爭力,你已經意識到了。

Elastic 於 2018 年 6 月 29 日正式推出 Elastic Certified Engineer 認證考試,認證通過可以獲得官方頒發的證書和徽章,title就是 Elastic認證工程師

具體認證的細節和含金量,沒有具體研究過,但是可以很明顯的感受到官方出了這樣一個認證,表明社會需要大量這樣的人才,而這方面人才的培養和考核指標還欠缺。

有沒有必要一定要考這個認證?

個人覺得,和英語四六級一樣,通過了再說沒用。

如果你是學生,可以考慮去考一個認證,因為你很難有業務場景驅使你去做這方面的成長,認證一定是有難度的,一個一個的困難會驅使你成長,最終這個認證也會成為招聘時一個非常大的亮點。

這個認證會有哪些幫助?

  • 對於快速的構建知識體系幫助。

  • 對於全面的熟悉官方文檔幫助。

  • 對於實戰解決線上問題幫助。(遇到了相關技術問題基本上不需要再求助於社區,80%以上的問題自己基本就能解決。)

  • 對於增強信心、克服英文恐懼幫助。

Elasticsearch 支持哪些編程語言?

  • Java
  • JavaScript (Node.js)
  • Go
  • .NET (C#)
  • PHP
  • Perl
  • Python
  • Ruby

哪裡可以找到有關 Elasticsearch 的更多信息?

  • Elasticsearch GitHub 存儲庫:https://github.com/elastic
  • Elasticsearch 官方文檔:https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html
  • Elasticsearch中文社區:https://elasticsearch.cn

我是龍叔,一個分享互聯網技術和心路歷程的star。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

※網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

透過選單樣式的調整、圖片的縮放比例、文字的放大及段落的排版對應來給使用者最佳的瀏覽體驗,所以不用擔心有手機版網站兩個後台的問題,而視覺效果也是透過我們前端設計師優秀的空間比例設計,不會因為畫面變大變小而影響到整體視覺的美感。