您好,登錄后才能下訂單哦!
身處在現在這個大數據時代,我們處理的數據量需以 TB、PB, 甚至 EB 來計算,怎么處理龐大的數據集是從事數據庫領域人員的共同問題。解決這個問題的核心在于,數據庫中存儲的數據是否都是有效的、有用的數據,因此如何提高數據中有效數據的利用率、將無效的過期數據清洗掉,便成了數據庫領域的一個熱點話題。在本文中我們將著重講述如何在數據庫中處理過期數據這一問題。
在數據庫中清洗過期數據的方式多種多樣,比如存儲過程、事件等等。在這里筆者舉個例子來簡要說明 DBA 經常使用的存儲過程 + 事件來清理過期數據的過程。
存儲過程是由一條或多條 SQL 語句組成的集合,當對數據庫進行一系列的讀寫操作時,存儲過程可將這些復雜的操作封裝成一個代碼塊以便重復使用,大大減少了數據庫開發人員的工作量。通常存儲過程編譯一次,可以執行多次,因此也大大的提高了效率。
存儲過程有以下優點:
以 MySQL 為例,假如要刪除數據的表結構如下:
mysql> SHOW CREATE TABLE person;
+--------+---------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+--------+---------------------------------------------------------------------------------------------------------------------------------+
| person | CREATE TABLE `person` (
`age` int(11) DEFAULT NULL,
`inserttime` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+--------+---------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
創建一個名為 person 的表,其中 inserttime 字段為 datetime 類型,我們用 inserttime 字段存儲數據的生成時間。
創建一個刪除指定表數據的存儲過程,如下:
mysql> delimiter //
mysql> CREATE PROCEDURE del_data(IN `date_inter` int)
-> BEGIN
-> DELETE FROM person WHERE inserttime < date_sub(curdate(), interval date_inter day);
-> END //
mysql> delimiter ;
創建一個名為 del_data 的存儲過程,參數 date_inter 指定要刪除的數據距離當前時間的天數。當表 person 的 inserttime 字段值(datetime 類型)加上參數 date_inter 天小于當前時間,則認為數據過期,將過期的數據刪除。
事件是在相應的時刻調用的過程式數據庫對象。一個事件可調用一次,也可周期性的啟動,它由一個特定的線程來管理,也就是所謂的“事件調度器”。
事件和觸發器類似,都是在某些事情發生的時候啟動。當數據庫上啟動一條語句的時候,觸發器就啟動了,而事件是根據調度事件來啟動的。由于它們彼此相似,所以事件也稱為臨時性觸發器。事件調度器可以精確到每秒鐘執行一個任務。
如下創建一個事件,周期性的在某個時刻調用存儲過程,來進行清理數據。
mysql> CREATE EVENT del_event
-> ON SCHEDULE
-> EVERY 1 DAY
-> STARTS '2020-03-20 12:00:00'
-> ON COMPLETION PRESERVE ENABLE
-> DO CALL del_data(1);
創建一個名為 del_event 的事件,該事件從 2020-03-20 開始,每天的 12:00:00 執行存儲過程 del_data(1)。
然后執行:
mysql> SET global event_scheduler = 1;
打開事件。這樣事件 del_event 就會在指定的時間自動在后臺執行。通過上述的存儲過程 del_data 和事件 del_event,來達到定時自動刪除過期數據的目的。
通過上述存儲過程和事件的組合可以定時清理數據庫中的過期數據。圖數據庫 Nebula Graph 提供了 更加簡單高效的方式—使用 TTL 的方式來自動清洗過期數據。
使用 TTL 方式自動清洗過期數據的好處如下:
TTL,全稱 Time To Live,用來指定數據的生命周期,數據時效到期后這條數據會被自動刪除。在圖數據庫 Nebula Graph 中,我們實現 TTL 功能,用戶設置好數據的存活時間后,在預定時間內系統會自動從數據庫中刪除過期的點或者邊。
在 TTL 中,過期數據會在下次 compaction 時被刪除,在下次 compaction 之前,query 會過濾掉過期的點和邊。
圖數據庫 Nebula Graph 的 TTL 功能需
ttl_col
和
ttl_duration
兩個字段一起使用,到期閾值是
ttl_col
指定的屬性對應的值加上
ttl_duration
設置的秒數。其中 ttl_col 指定的字段的類型應為 integer 或 timestamp,ttl_duration 的計量單位為秒。
針對 tag / edge,Nebula Graph 在 TTL 中將讀數據過濾邏輯下推到 storage 層進行處理。在 storage 層,首先獲取該 tag / edge 的 TTL 信息,然后依次遍歷每個頂點或邊,取出 ttl_col 字段值,根據 ttl_duration 的值加上 ttl_col 列字段值,跟當前時間的時間戳進行比較,判斷數據是否過期,過期的數據將被忽略。
圖數據庫 Nebula Graph 底層存儲使用的是 RocksDB,RocksDB 在磁盤上的文件是分為多層的,默認是 7 層,如下圖所示:
SST文件在磁盤上的組織方式
Level 0 層包含的文件,是由內存中的 Memtable flush 到磁盤,生成的 SST 文件,單個文件內部按 key 有序排列,文件之間無序。其它 Level 上的多個文件之間都是按照 key 有序排列,并且文件內也有序,如下圖所示:
非Level 0 層的文件數據劃分
RocksDB 是基于 LSM 實現,但 LSM 并不是一個具體的數據結構,而是一種數據結構的概念和設計思想,具體細節參考
LSM論文。而 LSM 中最重要部分就是 compaction,由于數據文件采用 Append only 方式寫入,而對于過期的數據,重復的、已刪除的數據,需要通過 compaction 進行逐步的清理。
我們采用的 RocksDB 的 compaction 策略為 Level compaction。當數據寫到
RocksDB 時,會先將數據寫入到一個 Memtable 中,當一個 Memtable 寫滿之后,就會變成 Immutable 的 Memtable。RocksDB 在后臺通過一個 flush 線程將這個 Memtable flush 到磁盤,生成一個 Sorted String Table (SST) 文件,放在 Level 0 層。當 Level 0 層的 SST 文件個數超過閾值之后,就會與Level 1 層進行 compaction。通常必須將 Level 0 的所有文件 compaction 到 Level 1 中,因為 Level 0 的文件的 key 是有交疊的。
Level 0 與 Level 1 的 compaction 如下:
Level 0 與 Level 1 的 compaction
其他 Level 的 compaction 規則一樣,以 Level 1與 Level 2 的 compaction 為例進行說明,如下所示:
Level 1 與 Level 2 的 compaction
當 Level 0 compaction 完成后,Level 1 的文件總大小或者文件數量可能會超過閾值,觸發 Level 1 與 Level 2 的 compaction。從 Level 1 層至少選擇一個文件 compaction 到 Level 2 的 key 重疊的文件中。compaction 后可能會觸發下一個 Level 的 compaction,以此類推。
如果沒有 compaction,寫入是非常快的,但這樣會造成讀性能降低,同樣也會造成很嚴重的空間放大問題。為了平衡寫入、讀取、空間三者的關系,RocksDB 會在后臺執行 compaction,將不同 Level 的 SST 進行合并。
除了上述默認的compaction操作外(sst文件合并),RocksDB 還提供了CompactionFilter 功能,可以讓用戶自定義個性化的compaction邏輯。Nebula Graph 使用了這個CompactionFilter來定制本文討論的TTL功能。該功能是 RocksDB 在 compaction 過程中,每讀取一條數據時,都會調用一個定制的Filter 函數。TTL compaction 的實現方法就是在 Filter 函數中實現 TTL 過期數據刪除邏輯,具體如下:
在圖數據庫 Nebula Graph 中,edge 和 tag 實現邏輯一致,在這里僅以 tag 為例,來介紹 Nebula Graph 中 TTL 用法。
Nebula Graph 中使用 TTL 屬性分為兩種方式:
create tag 時指定 ttl_duration 來表示數據的持續時間,單位為秒。ttl_col 指定哪一列作為 TTL 列。語法如下:
nebula> CREATE TAG t (id int, ts timestamp ) ttl_duration=3600, ttl_col="ts";
當某一條記錄的 ttl_col 列字段值加上 ttl_duration 的值小于當前時間的時間戳,則該條記錄過期,否則該記錄不過期。
或者 create tag 時沒有指定 TTL 屬性,后續想使用 TTL 功能,可以使用 alter tag 來設置 TTL 屬性。語法如下:
nebula> CREATE TAG t (id int, ts timestamp );
nebula> ALTER TAG t ttl_duration=3600, ttl_col="ts";
創建完 tag 可以使用以下語句查看 tag 的 TTL 屬性:
nebula> SHOW CREATE TAG t;
=====================================
| Tag | Create Tag |
=====================================
| t | CREATE TAG t (
id int,
ts timestamp
) ttl_duration = 3600, ttl_col = id |
-------------------------------------
可以使用 alter tag 語句修改 TTL 的屬性:
nebula> ALTER TAG t ttl_duration=100, ttl_col="id";
當不想使用 TTL 屬性時,可以刪除 TTL 屬性:
可以設置
ttl_col
字段為空,或刪除配置的
ttl_col
字段,或者設置
ttl_duration
為 0 或者 -1。
nebula> ALTER TAG t1 ttl_col = ""; -- drop ttl attribute
刪除配置的
ttl_col
字段:
nebula> ALTER TAG t1 DROP (a); -- drop ttl_col
設置 ttl_duration 為 0 或者 -1:
nebula> ALTER TAG t1 ttl_duration = 0; -- keep the ttl but the data never expires
下面的例子說明,當使用 TTL 功能,并且數據過期后,查詢該 tag 的數據時,過期的數據被忽略。
nebula> CREATE TAG t(id int) ttl_duration=100, ttl_col="id";
nebula> INSERT VERTEX t(id) values 102:(1584441231);
nebula> FETCH prop on t 102;
Execution succeeded (Time spent: 5.945/7.492 ms)
注意:
edge 同 tag 的邏輯一樣,這里就不在詳述了。
TTL 的介紹就到此為止了,如果你對圖數據庫 Nebula Graph 的 TTL 有改進想法或其他要求,歡迎去 GitHub:
https://github.com/vesoft-inc/nebula issue 區向我們提 issue 或者前往官方論壇:
https://discuss.nebula-graph.io/ 的
Feedback
分類下提建議 ????
作者有話說:Hi,我是 panda sheep,是圖數據庫 Nebula Graph 研發工程師,對數據庫領域非常感興趣,也有自己的一點點心得,希望寫的經驗分享能給大家帶來幫助,如有不當之處也希望能幫忙糾正,謝謝~
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。