[論文筆記] WiscKey: Separating Keys from Values in SSD-Conscious Storage

閱讀 WiscKey 論文時隨手記錄一些筆記。

這篇論文的核心思想理解起來還是很簡單的,但是具體涉及到實現還有一些想不明白的地方,后來看到 TiKV 的 Titan 實現也很有趣,索性把這些問題都記錄下來并拋出來。

本文中和論文相關的內容,斜體均為我個人的主觀想法,關于 Titan 的實現,我只看過幾篇公開文章以及粗淺的掃過一遍代碼,如果這兩部分的內容有理解錯誤歡迎指出,感謝!

背景

基于 LSM 樹(Log-Structured Merge-Trees)的鍵值存儲已經廣泛應用,其特點是保持了數據的順序寫入和存儲,利用磁盤的順序 IO 得到了很高的性能(在 HDD 上尤其顯著)。但是同一份數據會在生命周期中寫入多次,隨之帶來高額的寫放大。

以 LevelDB 為例,數據寫入的整個流程為:

  1. 數據首先會被寫入 memtable 和 WAL
  2. 當 memtable 達到上限后,會轉換為 immutable memtable,之后持久化到 L0(稱為 flush),L0 中每個文件都是一個持久化的 immutable memtable,多個文件間可以有相互重疊的 Key
  3. 當 L0 中的文件達到一定數量時,便會和 L1 中的文件進行合并(稱為 compaction)
  4. 自 L1 開始所有文件都不會再有相互重疊的 Key,并且每個文件都會按照 Key 的順序存儲。每一層通常是上一層大小的 10 倍,當一層的大小超過限制時,就會挑選一部分文件合并到下一層

由此可以計算出 LevelDB 的寫放大比率:

  1. 由于每一層是上一層大小的 10 倍,所以在最壞情況下,上一層的一個文件可能需要和下一層的十個文件進行合并,所以合并的寫放大是 10
  2. 假設每行數據經過一系列 compaction 最終都會落入最終層,每層都需要重新寫一次,那么從 L1 到 L6 的寫放大為 5
  3. 加上 WAL 和 L0,最終寫放大可能會超過 50

另一方面,由于數據在 LevelDB 中的每一層(memtable/L0/L1~L6)都有可能存在,所以對于讀請求,也會有一定的讀放大:

  1. 由于 L0 的多個文件允許有數據重疊,所以最壞情況需要查詢所有文件
  2. 對于 L1 到 L6,因為數據有序且不重疊,所以每層需要查詢一個文件
  3. 為了確認 Key 是否存在,對于每個文件都需要讀取 index block、bloom-filter blocks 和 data block

論文中提供了一個實際的數據:

WiscKey 介紹

設計目標

WiscKey 的核心思想是將數據中的 Key 和 Value 分離,只在 LSM-Tree 中有序存儲 Key,而將 Value 存放在單獨的 Log 中。這樣帶來了兩點好處:

  1. 當 LSM-Tree 進行 compaction 時,只會對 Key 進行排序和重寫,不會影響到沒有改變的 Value,也就顯著降低了寫放大
  2. 將 Value 分離后,LSM-Tree 本身會大幅減小,所以對應磁盤中的層級會更少,可以減少查詢時從磁盤讀取的次數,并且可以更好的利用緩存的效果

另外,WiscKey 的設計很大一部分還建立在 SSD 的普及上,相比 HDD,SSD 有一些變化:

  1. SSD 的隨機 IO 和順序 IO 的性能差距并不像 HDD 那么大,所以 LSM-Tree 為了避免隨機 IO 而采用了大量的順序 IO,反而可能會造成了帶寬浪費
  2. SSD 擁有內部并行性,但 LSM-Tree 并沒有利用到該特性
  3. SSD 會因為大量的重復寫入而產生硬件損耗,LSM-Tree 的高寫入放大率會降低設備的壽命

下圖展示了在不同請求大小和并發度時,隨機讀和順序讀的吞吐量,可以看到在請求大于 16KB 時,32 線程的隨機讀已經接近了順序讀的吞吐:

實現

在 LSM-Tree 的基礎上,WiscKey 引入了一個額外的存儲用于存儲分離出的值,稱為 Value Log。整體的讀寫路徑為:

  1. 當用戶添加一個 KV 時,WiscKey 會先將 Value 寫入到 Value-Log 中(順序寫),然后將 Key 和 Value 在 Value Log 中的地址寫入 LSM-Tree
  2. 當用戶刪除一個 Key 時,僅在 LSM-Tree 中刪除 Key 的記錄,之后通過 GC 清理掉 Value Log 中的數據
  3. 當用戶查詢一個 Key 時,會先從 LSM-Tree 中查詢到 Value 的地址,再根據地址將 Value 真正從 Value-Log 中讀取出來(隨機讀)

假設 Key 的大小為 16 Bytes,Value 的大小為 1KB,優化后的效果為:

  1. 如果在 LSM-Tree 中的單層的寫放大率是 10,那么使用 WiscKey 后單層的寫放大率將變為 ((16 x 10) + (1024 x 1)) / (16 + 1024) = 1.14,遠小于之前的 10 倍
  2. 如果一個標準的 LSM-Tree 的大小為 100G,那么將 Value 分離后 LSM-Tree 本身的大小將會減少到 2G,層級會減少 1~2 級,并且緩存到內存中的比例會更高,從而降低讀放大

看上去實現很簡單,效果也很好,但是背后也存在了一些挑戰和優化。

挑戰一:范圍查詢

在標準的 LSM-Tree 中,由于 Key 和 Value 是按照順序存儲在一起的,所以范圍查詢只需要順序讀即可遍歷整個 SSTable 的所有數據。但是在 WiscKey 中,每個 Key 都需要額外的一次隨機讀才能讀取到對應的 Value,因此效率會很差。

論文中的解決方案是利用上文中所提到的 SSD 內部的并行能力。WiscKey 內部會有一個 32 線程的線程池,當用戶使用迭代器迭代一行時,迭代器會預先取出多個 Key,并放入到一個隊列中,線程池會從隊列中讀取 Key 并行的查找對應的 Value。

疑問:

  1. 預取在某些場景是否會有浪費?(用戶不準備迭代完所有數據的場景,例如 Limit 或是 Filter)
  2. 為什么用線程池 + 隊列,而不是直接用異步 IO?

挑戰二:垃圾收集(GC)

上文中提到了,當用戶刪除一個 Key 時,WiscKey 只會將 LSM-Tree 中的 Key 刪除掉,所以需要一個額外的方式清理 Value-Log 中的值。

最簡單的方法是定期掃描整個 LSM-Tree,獲得所有還有引用的 Value 地址,然后將沒有引用的 Value 刪除,但是這個邏輯非常重。

論文中介紹的方式是通過維護一個 Value Log 的有效區間(由 head 和 tail 兩個地址組成),通過不斷地搬運有效數據來達到淘汰無效數據。整個流程為:

  1. 對于 Value-Log 中的每個值,需要額外存儲 Key,為了方便從 LSM-Tree 中進行反查(相對 Value,Key 會比較小,所以寫入放大不會增加太多)
  2. 從 tail 的位置讀取 KV,通過 Key 在 LSM-Tree 中查詢 Value 是否還在被引用
  3. 如果 Value 還在被引用,則將 Value 寫入到 head,并將新的 Value 地址寫回 LSM-Tree 中
  4. 如果 Value 已經沒有被引用,則跳過這行數據,接著讀取下一個 KV
  5. 當已經確認數據寫入 head 之后,就可以將 tail 之后的數據都刪除掉了

因為需要重新寫入一次 Value,并且需要將 Key 回填到 LSM-Tree 中,所以這個 GC 策略會造成額外的寫放大。并且即使不做 GC,也只會影響到空間放大(刪除的數據沒有真正清理),所以感覺可以配置一些策略:

  1. 根據磁盤負載和 LSM-Tree 的負載計算,僅在低峰期執行
  2. 計算每一段數據中被刪除的比例有多少,當空洞變得比較大的時候才觸發 GC

挑戰三:崩潰一致性

當系統崩潰時,LSM-Tree 可以保證數據寫入的原子性和恢復的有序性,所以 WiscKey 也需要保證這兩點。

WiscKey 通過查詢時的容錯機制保證 Key 和 Value 的原子性:

  1. 當用戶查詢時,如果在 LSM-Tree 中找不到 Key,則返回 Key 不存在
  2. 如果在 LSM-Tree 中可以找到 Key,但是通過地址在 Value-Log 中無法找到匹配的 Value,則說明 Value 在寫入時丟失了,同樣返回不存在

這個前提建立在于 WiscKey 通過一個 Write Buffer 批量提交 Value Log(下面有詳細介紹),所以才會出現 Key 寫入成功后 Value 丟失的場景,用戶也可以通過設置同步寫入,這樣在刷新 Value Log 之后,才會將 Key 寫入 LSM-Tree 中。

另外,WiscKey 通過現代的文件系統的特性保證了寫入的有序性,即寫入一個字節序列 b1, b2, b3…bn,如果 b3 在寫入時丟失了,那么 b3 之后的所有值也一定會丟失。

優化一:Write Buffer

為了提高寫入效率,WiscKey 首先會將 Value 寫入到 Write Buffer 中,等待 Write Buffer 達到一定大小再一起刷新到文件中。所以查詢時首先也要先從 WriteBuffer 中查詢。當崩潰時,Write Buffer 中的數據會丟失,此時的行為就是上文中的崩潰一致性。

疑問:

  1. 根據這個描述,Value-Log 似乎是異步寫入?結合上文中崩潰一致性的介紹,會有給用戶返回成功但是數據丟失的情況?

優化二:WAL 優化

LSM-Tree 通過 WAL 保證了在系統崩潰時 memtable 中的數據可恢復,但是也帶來了額外的一倍寫放大。

而在 WiscKey 中,Value-Log 和 WAL 都是基于用戶的寫入順序進行存儲的,并且也具備了恢復數據的所有內容(前提是基于上文中的 GC 實現,Value Log 里存有 Key),所以理論上 Value-Log 是可以同時作為 WAL 的,從而減少 WAL 的寫放大。

由于 Value Log 的 GC 比 WAL 更加低頻,并且包含了大量已經持久化的數據,直接通過 Value-Log 進行恢復的話可能會導致回放大量已經持久化到 SST 的數據。所以 WiscKey 會定期將已經持久化到 SST 的 head 寫入到 LSM-Tree 中,這樣當恢復時只需要從最新持久化的 head 開始恢復即可。

疑問:

  1. Delete 操作只需要寫 LSM-Tree,但如果需要 Value Log 作為 WAL,則 Delete 也需要寫入到 Value Log 中
  2. 如果不應用這個優化,則可以做到只將大 Value 分離出 LSM-Tree,應用此優化后,小 Value 也必須要額外存到 Value Log 中了
  3. 與其說是用 Value Log 替代 WAL,不如說是讓 WAL 支持讀 Value…

效果

說完實現再看看效果,論文中有 db_bench 和 YCSB 的數據,為了節約篇幅,只貼一部分 db_bench 的數據。

db_bench 的場景分兩種,一種是所有 Key 按順序寫入(這樣寫放大會更低,數據在每一層會更緊湊),另一種是隨機寫入(寫放大更高,數據在每一層分布更均勻)。

順序寫入

效果應該來自兩部分:

  1. WAL 沒了直接省了一倍寫放大
  2. 順序寫入,每一層合并可以認為沒有寫放大,但是數據依舊要在每一層寫一次,100 G 可能是 4~5 次(對應 L5 的大小)

隨機寫入

效果對比順序寫入,如果說為什么差距會這么大,只有可能是每一層合并造成的寫放大了。

點查

  1. 當 Value 比較小時,WiscKey 的劣勢在于額外的一次隨機讀,而 LevelDB 的劣勢在于讀放大。當 Value 變得更大時,基于 SSD 內部的并行能力,隨機讀依舊能讀滿帶寬,但是 LevelDB 讀放大造成的帶寬浪費卻沒有改善。
  2. 另外這個測試場景是數據庫大小為 100G,對于 LevelDB 來說,層級和 KV 大小掛鉤,對于 WiscKey 來說,層級和 Key 大小掛鉤,所以當 Value 越大,WiscKey 中的 LSM-Tree 反而更小,層級也就更低,甚至可能僅在內存中 (例如 Value 為 256KB 時,Key 加起來才 100G / (16 + 256?1024)?16 ~= 610KB)

這個有點看不懂…:

  1. 在數據集是順序寫的場景下,LevelDB 的性能隨著 Value 的增大反而降低了,這個不太理解原因(理論上讀放大不會很大,而且是順序讀,很容易就能讀滿帶寬),WiscKey 因為是隨機讀,并且有上文中提到的 LSM-Tree 本身很小,隨著 Value 變大性能越高是符合預期的
  2. 在數據集是隨機寫的場景下,一開始 WiscKey 性能低是因為隨機讀的延遲,隨著 Value 增大,優勢應該和點查類似

GC

上文提到了 GC 會重寫 Value 以及寫回 LSM-Tree,造成額外的寫入。當空余空間的占比越高時(大部分數據都已經被刪了),回寫的數據越少,對性能的影響也就越小。

Titan 的實現

BlobDB 和 Badger 的實現都和論文比較接近,并且也都是玩具。反而 TiKV 的 Titan 有一些獨特的設計可以學習和討論,所以下面只介紹這一案例。

核心實現

和 WiscKey 的主要區別在于:Titan 在 flush/compaction 時才開始分離鍵值,并且用于存儲分離后 Value 的文件(BlobFile)會按照 Key 的順序存儲,而不是寫入的順序(其實在這個階段,已經沒有寫入順序了)。

因此導致實現上的差異有:

  1. 范圍查詢:由于 Value-Log 沒有按照 Key 排序,所以 WiscKey 需要將一個范圍查詢拆解為多個隨機讀。而 Titan 保證了局部有序,在單個 BlobFile 內部可以順序讀,但是會有多個 BlobFile 的范圍有重疊,需要額外做歸并。另外對于預取策略,WiscKey 建立在 SSD 并行的優勢上,可以靠增加并發預取增加吞吐,而 Titan 暫時沒有如此激進的預取策略
  2. WAL 優化:在 WiscKey 的實現中通過 Value Log 替代 WAL 減少了一倍寫放大,而 Titan 在 flush/compaction 時才進行鍵值分離,肯定是沒辦法做這個優化的,不過這一點在 Titan 的設計文檔里也提到了:「假設 LSM-tree 的 max level 是 5,放大因子為 10,則 LSM-tree 總的寫放大大概為 1 + 1 + 10 + 10 + 10 + 10,其中 Flush 的寫放大是 1,其比值是 42 : 1,因此 Flush 的寫放大相比于整個 LSM-tree 的寫放大可以忽略不計。」,個人覺得還是還比較信服的
  3. GC 策略:Titan 目前有兩個版本的 GC 策略,會在下面詳細介紹

GC 策略

第一種策略(傳統 GC):

  1. 首先挑選一些需要合并的 BlobFile,在 flush/compaction 時可以統計出每個 BlobFile 中已經刪除的數據大小,從而挑選出空洞較大的文件
  2. 迭代這些 BlobFile,通過 Key 查詢 LSM-Tree,判斷 Value 是否還在被引用
  3. 如果 Value 還在被引用就寫到新的 BlobFile 中,并把更新后的地址回填到 LSM-Tree 中

這個實現和論文中的 GC 方案類似,只不過論文為了 WAL 需要寫入一條完整的 Value Log,所以需要維護 head 和 tail。Titan 的實現只需要每次都生成新的 BlobFile 即可。

不同點在于:WiscKey 是隨機讀,Value Log 的大小不會影響到讀 Value 的成本。GC 策略在于寫放大和空間放大的權衡,所以 GC 可以更加低頻。而 BlobFile 是順序讀,如果 BlobFile 中的無效數據太多,會影響到預取的效率,間接也會影響到讀的性能。

第二種策略(Level-Merge):

  1. 不存在單獨的 GC,由 LSM-Tree 的 compaction 觸發
  2. compaction 時,如果遍歷到的值已經是一個 BlobIndex(代表值已經寫入了某個 BlobFile),依舊將其讀出來重新寫入新的 BlobFile,也就是說每次 compaction 都會生成一批與新的 SST 完全對應的 BlobFile
  3. 目前 Level Merge 只在最后兩層開啟

開啟 Level Merge 后相當于 GC 頻率和 compaction 頻率持平了(GC 頻率最多也只能和 compaction 持平),并且在這個基礎上,直接在 compaction 里做 GC,可以減少一次回寫 LSM-Tree 的成本(因為在 compaction 的過程中就能將老的 Value 地址替換掉)。

這種策略的優點在于 BlobFile 中不再有無效數據,可以用更加激進的預取策略提高范圍查詢的性能,缺點是寫放大肯定會比之前更大(個人覺得開啟后,寫放大就和標準 LSM-Tree 完全一樣了吧(一次 compaction 需要合并的 Key 和 Value 都需要重寫一遍)?),所以只在最后兩層開啟。

效果

Titan 的性能測試結果摘自官網的文章,大部分結論都和 WiscKey 類似,并且文章中也分析了原因,就不在此贅述了。

因為文章是 19 年初的,所以還沒有上文中的 Level Merge GC,不過 GC 策略理論上只影響范圍查詢的性能,所以在此貼一下范圍查詢的性能:

在實現 Level Merge GC 的策略之前,Titan 的范圍查詢只有 RocksDB 的 40%,主要原因應該還是分離后需要額外讀一次 Value,以及沒辦法并行預取增加吞吐。 這點文章最后也提到了:

我們通過測試發現,目前使用 Titan 做范圍查詢時 IO Util 很低,這也是為什么其性能會比 RocksDB 差的重要原因之一。因此我們認為 Titan 的 Iterator 還存在著巨大的優化空間,最簡單的方法是可以通過更加激進的 prefetch 和并行 prefetch 等手段來達到提升 Iterator 性能的目的。

另外在?TiDB in Action?也提到了 Level Merge GC 可以「大幅提升 Titan 的范圍查詢性能」,不知道除了完全去掉無效數據之外,是否還有其他的優化,還需要再看下代碼。

感受

個人認為 WiscKey 的核心思想還是比較有意義的,畢竟適用的場景很典型而且還比較常見:大 Value、寫多讀少、點查多范圍查詢少,只要業務場景命中一個特點,效果應該就會非常顯著了。

對于論文中的具體實現是否能套用在一個真實的工業實現中,我覺得大部分實現還是簡單有效的,但是也有一些設計個人不太喜歡,例如使用 Value Log 替代 WAL 的方案,感覺有些過于追求減少寫放大了,可能反而會引入其他問題,以及默認的 GC 策略還要寫回 LSM-Tree 也有些別扭。

在和其他同事討論內部項目的實現時,也暢想過一些其他玩法,例如只將 Value 中的一部分分離出來單獨存儲,或是一個分布式的 WAL 是否也能轉換為 Value Log,會有哪些問題。包括看到 Titan 的實現時,我也很好奇設計成 BlobFile 這種順序讀的方式是否有什么深意(畢竟論文都把利用 SSD 寫到標題里了),或者只是因為從 compaction 才開始分離鍵值最簡單的做法就是按順序存儲 KV。

總之,期待將來能有更多工業實現落地,看到更多有趣的案例。

參考資料

  • WiscKey: Separating Keys from Values in SSD-conscious Storage
  • https://nan01ab.github.io/2018/07/WiscKey.html
  • Titan 的設計與實現
  • Titan 原理介紹

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/93450.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/93450.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/93450.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

week1-[循環嵌套]畫正方形

week1-[循環嵌套]畫正方形 題目描述 輸入一個正整數 nnn,請使用數字 000 到 999 拼成一個這樣的正方形圖案(參考樣例輸入輸出):由上至下、由左至右依次由數字 000 到 999 填充。每次使用數字 999 填充后,將從頭使用數字…

在 Vue2 中使用 pdf.js + pdf-lib 實現 PDF 預覽、手寫簽名、文字批注與高保真導出

本文演示如何在前端(Vue.js)中結合 pdf.js、pdf-lib 與 Canvas 技術實現 PDF 預覽、圖片簽名、手寫批注、文字標注,并導出高保真 PDF。 先上demo截圖,后續會附上代碼倉庫地址(目前還有部分問題暫未進行優化&#xff0…

tomcat 定時重啟

tomcat 定時重啟 定時重啟的目的是:修復內存泄漏等問題,tomcat 長時間未重啟,導致頁面卡頓,卡死,無法訪問,影響用戶訪問 1.編寫腳本 su - tomcat [tomcat@u1abomap02 ~]$ ls restart_tomcat_gosi.sh tomcat_gosi.log vi restart_tomcat_gosi.sh #!/bin/bash# 定義日志目…

WinForm 簡單用戶登錄記錄器實現教程

目錄 功能概述 實現思路 一、程序入口(Program.cs) 二、登錄用戶控件(Login.cs) 2.1 控件初始化與密碼顯示邏輯 2.2 登錄控件設計器(Login.Designer.cs) 三、主窗體(Form1.cs&#xff09…

docker 安裝 使用

Docker安裝 一鍵安裝命令 sudo curl -fsSL https://get.docker.com| bash -s docker --mirror Aliyun啟動docker sudo service docker startpull鏡像加速配置 sudo vi /etc/docker/daemon.json輸入下列內容,最后按ESC,輸入 :wq! 保存退出。 {"regis…

無人機探測器技術解析

一、工作模式 無人機探測器通過多模式協同實現全流程防御閉環: 1. 主動掃描模式 雷達主動探測:發射電磁波(如Ka/Ku波段),通過回波時差與多普勒頻移計算目標距離、速度及航向,適用于廣域掃描(…

Linux學習-軟件編程(進程與線程)

進程回收wait原型:pid_t wait(int *wstatus); 功能:回收子進程空間 參數:wstatus:存放子進程結束狀態空間的首地址 返回值:成功返回回收到的子進程的PID失敗返回-1WIFEXITED(wstatus):測試進程是否正常結束…

大模型微調分布式訓練-大模型壓縮訓練(知識蒸餾)-大模型推理部署(分布式推理與量化部署)-大模型評估測試(OpenCompass)

大模型微調分布式訓練 LLama Factory與Xtuner分布式微調大模型 大模型分布式微調訓練的基本概念 為什么需要分布式訓練? 模型規模爆炸:現代大模型(如GPT-3、LLaMA等)參數量達千億級別,單卡GPU無法存儲完整模型。 …

物聯網、大數據與云計算持續發展,樓宇自控系統應用日益廣泛

在深圳某智慧園區的控制中心,管理人員通過云端平臺實時監控著5公里外園區內每臺空調的運行參數、每盞路燈的開關狀態和每個區域的能耗數據。當系統檢測到某棟樓宇的電梯運行振動異常時,大數據算法自動預判可能的故障點并推送維修建議;物聯網傳…

在實驗室連接地下車庫工控機及其數據采集設備

在實驗室連接地下車庫工控機及其數據采集設備 我們小組為項目的數據采集組,目前在車頂集成了一個工控機、兩個激光雷達、兩個攝像頭、一個戶外電源 由于地下車庫蚊子太多了,我們可受不了這個苦,所以想坐在實驗室吹著空調就能連接工控機來修改…

icmpsh、PingTunnel--安裝、使用

用途限制聲明,本文僅用于網絡安全技術研究、教育與知識分享。文中涉及的滲透測試方法與工具,嚴禁用于未經授權的網絡攻擊、數據竊取或任何違法活動。任何因不當使用本文內容導致的法律后果,作者及發布平臺不承擔任何責任。滲透測試涉及復雜技…

系統思考:情緒內耗與思維模式

我們正在努力解決的問題,很多時候,根源就在我們自己。 在日常的工作和生活中,我們常常感到焦慮、內耗和失控。這些情緒和狀態,似乎總是在不斷循環。但如果停下來仔細思考,會發現,問題的背后,并不…

詳解grafana k6 中stage的核心概念與作用

在Grafana k6中,??Stage(階段)?? 是負載測試腳本的核心配置概念,用于動態控制虛擬用戶(VUs)的數量隨時間的變化。通過定義多個階段,用戶可以模擬真實場景中的流量波動(如用戶逐步…

JS 和 JSX 的區別

JS 和 JSX 是兩種不同的概念,盡管它們都與 JavaScript 密切相關,尤其是在 React 開發中。以下是它們的主要區別:1. 定義JS (JavaScript): 一種通用的編程語言,用于開發動態網頁、服務器端應用程序等。它是標準的 ECMAScript 語言。…

Linux軟件編程-進程(2)及線程(1)

1.進程回收資源空間&#xff08;1&#xff09;wait函數頭文件&#xff1a;#include <sys/types.h>#include <sys/wait.h>函數接口&#xff1a;pid_t wait(int *wstatus);功能&#xff1a;阻塞等待回收子進程的資源空間參數&#xff1a;wstatus &#xff1a;保存子進…

java 集合 之 集合工具類Collections

前言早期開發者經常需要對集合進行各種操作比如排序、查找最大最小值等等但是當時沒有統一的工具類來處理所以導致代碼重復且容易出錯java.util.Collections 工具類的引入為開發者提供了大量 靜態方法 來操作集合它就像一個經驗豐富的助手和數組工具類 Arrays 一樣避免了我們重…

2025 年電賽 C 題 發揮部分 1:多正方形 / 重疊正方形高精度識別與最小邊長測量

2025 年全國大學生電子設計競賽 C 題 發揮部分 1&#xff1a;多正方形 / 重疊正方形高精度識別與最小邊長測量 香橙派 OpenCV C 全流程解析 目錄 賽題背景與需求技術難點全景圖系統總體架構硬件平臺與接線軟件架構與線程模型算法流水線逐幀拆解 6.1 圖像預處理6.2 輪廓提取與…

【自動駕駛】自動駕駛概述 ② ( 自動駕駛技術路徑 | L0 ~ L5 級別自動駕駛 )

文章目錄一、自動駕駛技術路徑1、L0 級別 自動駕駛2、L1 級別 自動駕駛3、L2 級別 自動駕駛4、L3 級別 自動駕駛5、L4 級別 自動駕駛6、L5 級別 自動駕駛一、自動駕駛技術路徑 美國汽車工程師學會 ( SAE ) 將 自動駕駛 分為 L0 ~ L5 六個級別 : 其中 L0 級別 是 完全手動 , L5…

C++少兒編程(二十二)—條件結構

1.理解條件結構小朋友們&#xff0c;今天讓我們一起來探索一個神奇而有趣的知識——程序的條件結構&#xff01;首先&#xff0c;讓我們來想象一個有趣的場景。比如說&#xff0c;你們正在準備去公園玩耍。在出發之前&#xff0c;你們會看看天氣怎么樣。如果天氣晴朗&#xff0…

Ubuntu20.04下Px4使用UORB發布消息

1 .msg文件夾定義數據類型及 變量名文件位置如圖&#xff0c;在PX4-Autopilot/msg文件夾下&#xff0c;筆者創建的文件名為gps_msg.msggps_msg.msg內容如下 uint64 timestamp # 時間戳 float32 latitude float32 longitude float32 altitude 同時&#xff0c;在CM…