MyRocksTTL特性介紹
概述
MyRocks TTL(Time To Live) 特性允許使用者指定表資料的自動過期時間,表資料根據指定的時間在compact過程中進行清理。
MyRocks TTL 簡單用法如下,
在comment中通過ttl_duration指定過期時間,ttl_col指定過期時間列
CREATE TABLE t1 (
a bigint(20) NOT NULL,
b int NOT NULL,
ts bigint(20) UNSIGNED NOT NULL,
PRIMARY KEY (a),
KEY kb (b)
) ENGINE=rocksdb
COMMENT=`ttl_duration=1;ttl_col=ts;`;
也可以不指定過期時間列ttl_col,插入資料時會隱式將當前時間做為過期時間列儲存到記錄中。
CREATE TABLE t1 (
a bigint(20) NOT NULL,
PRIMARY KEY (a)
) ENGINE=rocksdb
COMMENT=`ttl_duration=1;`;
分割槽表也同樣支援TTL
CREATE TABLE t1 (
c1 BIGINT,
c2 BIGINT UNSIGNED NOT NULL,
name VARCHAR(25) NOT NULL,
event DATE,
PRIMARY KEY (`c1`) COMMENT `custom_p0_cfname=foo;custom_p1_cfname=bar;custom_p2_cfname=baz;`
) ENGINE=ROCKSDB
COMMENT="ttl_duration=1;custom_p1_ttl_duration=100;custom_p1_ttl_col=c2;custom_p2_ttl_duration=5000;"
PARTITION BY LIST(c1) (
PARTITION custom_p0 VALUES IN (1, 2, 3),
PARTITION custom_p1 VALUES IN (4, 5, 6),
PARTITION custom_p2 VALUES IN (7, 8, 9)
);
RocksDB TTL
介紹MyRocks TTL實現之前,先來看看RocksDB TTL。
RocksDB 本身也支援TTL, 通過DBWithTTL::Open介面,可以指定每個column_family的過期時間。
每次put資料時,會呼叫DBWithTTLImpl::AppendTS將過期時間append到value最後。
在Compact時通過自定義的TtlCompactionFilter , 去判斷資料是否可以清理。具體參考DBWithTTLImpl::IsStale
bool DBWithTTLImpl::IsStale(const Slice& value, int32_t ttl, Env* env) {
if (ttl <= 0) { // Data is fresh if TTL is non-positive
return false;
}
int64_t curtime;
if (!env->GetCurrentTime(&curtime).ok()) {
return false; // Treat the data as fresh if could not get current time
}
int32_t timestamp_value =
DecodeFixed32(value.data() + value.size() - kTSLength);
return (timestamp_value + ttl) < curtime;
}
RocksDB TTL在compact時才清理過期資料,所以,過期時間並不是嚴格的,會有一定的滯後,取決於compact的速度。
MyRocks TTL 實現
和RocksDB TTL column family級別指定過期時間不同,MyRocks TTL可表級別指定過期時間。
MyRocks TTL表過期時間儲存在資料字典INDEX_INFO中,表中可以指定過期時間列ttl_col, 也可以不指定, 不指定時會隱式生成ttl_col.
對於主鍵,ttl_col的值儲存在value的頭8個位元組中,對於指定了過期時間列ttl_col的情況,value中ttl_col位置和valule的頭8個位元組都會儲存ttl_col值,這裡有一定的冗餘。具體參考convert_record_to_storage_format
讀取資料會自動跳過ttl_col佔用的8個位元組,參考convert_record_from_storage_format
對於二級索引,也會儲存ttl_col同主鍵保持一致,其ttl_col儲存在value的unpack_info中,
if (m_index_type == INDEX_TYPE_SECONDARY &&
m_total_index_flags_length > 0) {
// Reserve space for index flag fields
unpack_info->allocate(m_total_index_flags_length);
// Insert TTL timestamp
if (has_ttl() && ttl_bytes) {
write_index_flag_field(unpack_info,
reinterpret_cast<const uchar *const>(ttl_bytes),
Rdb_key_def::TTL_FLAG);
}
}
二級索引ttl_col同主鍵保持一致。 對於更新顯式指定的ttl_col列時,所有的二級索引都需要更新,即使此列不在二級索引列中
MyRocks TTL 清理
MyRocks TTL 清理也發生在compact時,由Rdb_compact_filter定義清理動作, 具體參考should_filter_ttl_rec
RocksDB TTL中過期時間和當前時間做比較,而MyRocks TTL 的過期時間是和最老的快照時間(m_snapshot_timestamp )做比較(當沒有快照時,也取當前時間)。
bool should_filter_ttl_rec(const rocksdb::Slice &key,
const rocksdb::Slice &existing_value) const {
uint64 ttl_timestamp;
Rdb_string_reader reader(&existing_value);
if (!reader.read(m_ttl_offset) || reader.read_uint64(&ttl_timestamp)) {
std::string buf;
buf = rdb_hexdump(existing_value.data(), existing_value.size(),
RDB_MAX_HEXDUMP_LEN);
// NO_LINT_DEBUG
sql_print_error("Decoding ttl from PK value failed in compaction filter, "
"for index (%u,%u), val: %s",
m_prev_index.cf_id, m_prev_index.index_id, buf.c_str());
abort();
}
/*
Filter out the record only if it is older than the oldest snapshot
timestamp. This prevents any rows from expiring in the middle of
long-running transactions.
*/
return ttl_timestamp + m_ttl_duration <= m_snapshot_timestamp;
}
MyRocks TTL 讀過濾
前面講到, RocksDB TTL 過期時間並不嚴格,取決於compaction速度。MyRocks TTL也有類似問題,因此MyRocks引入引數rocksdb_enable_ttl_read_filtering, 當開啟此引數時,過期時間是嚴格的。
每次讀取記錄會呼叫should_hide_ttl_rec判斷此記錄是否過期,當compact操作不及時而沒有清理的過期記錄,在讀取時會被過濾掉。
bool ha_rocksdb::should_hide_ttl_rec(const Rdb_key_def &kd,
const rocksdb::Slice &ttl_rec_val,
const int64_t curr_ts) {
DBUG_ASSERT(kd.has_ttl());
DBUG_ASSERT(kd.m_ttl_rec_offset != UINT_MAX);
/*
Curr_ts can only be 0 if there are no snapshots open.
should_hide_ttl_rec can only be called when there is >=1 snapshots, unless
we are filtering on the write path (single INSERT/UPDATE) in which case
we are passed in the current time as curr_ts.
In the event curr_ts is 0, we always decide not to filter the record. We
also log a warning and increment a diagnostic counter.
*/
if (curr_ts == 0) {
update_row_stats(ROWS_HIDDEN_NO_SNAPSHOT);
return false;
}
if (!rdb_is_ttl_read_filtering_enabled() || !rdb_is_ttl_enabled()) {
return false;
}
Rdb_string_reader reader(&ttl_rec_val);
/*
Find where the 8-byte ttl is for each record in this index.
*/
uint64 ts;
if (!reader.read(kd.m_ttl_rec_offset) || reader.read_uint64(&ts)) {
/*
This condition should never be reached since all TTL records have an
8 byte ttl field in front. Don`t filter the record out, and log an error.
*/
std::string buf;
buf = rdb_hexdump(ttl_rec_val.data(), ttl_rec_val.size(),
RDB_MAX_HEXDUMP_LEN);
const GL_INDEX_ID gl_index_id = kd.get_gl_index_id();
// NO_LINT_DEBUG
sql_print_error("Decoding ttl from PK value failed, "
"for index (%u,%u), val: %s",
gl_index_id.cf_id, gl_index_id.index_id, buf.c_str());
DBUG_ASSERT(0);
return false;
}
/* Hide record if it has expired before the current snapshot time. */
uint64 read_filter_ts = 0;
#ifndef NDEBUG
read_filter_ts += rdb_dbug_set_ttl_read_filter_ts();
#endif
bool is_hide_ttl =
ts + kd.m_ttl_duration + read_filter_ts <= static_cast<uint64>(curr_ts);
if (is_hide_ttl) {
update_row_stats(ROWS_FILTERED);
}
return is_hide_ttl;
}
MyRocks TTL 潛在問題
Issue#683 中談到了MyRocks TTL 有個潛在問題, 當更新顯式指定的ttl_col列值時,compact時有可能將新的記錄清理掉,而老的記錄仍然保留,從而有可能讀取到本該不可見的老記錄。此問題暫時還沒有close.
最後
MyRocks TTL 是一個不錯的特性,可以應用在歷史資料清理的場景。相比傳統的Delete資料的方式,更節約空間和CPU資源,同時傳統的Delete還會影響查詢的效率。目前MyRocks TTL 還不夠成熟,還有許多需要改進的地方。
相關文章
- Jetbrains CLion特性介紹AI
- Jetbrains pycharm特性介紹AIPyCharm
- Jetbrains datagrip特性介紹AI
- Jetbrains goland特性介紹AIGoLand
- Kyma Application Connectivity 特性介紹APP
- Zookeeper 節點特性介紹
- TiDB 5.0.0新特性介紹TiDB
- MySQL 8.0 新增特性介紹MySql
- 【IDL】IDL 8.4新特性介紹
- Docker的原理及特性介紹Docker
- AnalyticDB for PostgreSQL 6.0 新特性介紹SQL
- Apache Cassandra 4.0新特性介紹Apache
- Angular 8的新特性介紹Angular
- JavaScript 6 的新特性介紹JavaScript
- [譯] Chrome 71 新特性介紹Chrome
- webpack4新特性介紹Web
- React v16.6 新特性介紹React
- Redis4.0的新特性介紹Redis
- Dash 2.16版本新特性介紹
- K8s的介紹和特性K8S
- Dash 2.17版本新特性介紹
- BootstrapBlazor 智慧生成神器(一)AutoGenerateColumnAttribute 特性介紹bootBlazor
- CSS的特性之層疊性介紹CSS
- SQL?Server新特性SequenceNumber用法介紹YTZBSQLServer
- Flutter 3.7 新特性:介紹後臺isolate通道Flutter
- SAP UI5 Decision Table 的特性介紹UI
- EventBridge 特性介紹|以 IaC 的方式使用 EventBridge
- OneAuth 3月釋出: Cloud UD 特性介紹Cloud
- 三腳工字電感的基本特性介紹gujing
- 貼程式碼框架PasteForm特性介紹之markdown和richtext框架ASTORM
- ECMAScript6.0新特性介紹第七篇
- 疊層貼片電感特性介紹與應用gujing
- 【劉文彬】 EOS1.1版本新特性介紹
- SQL Server 2022 AlwaysOn新特性之包含可用性組介紹SQLServer
- 什麼是Ansible?特性及優點介紹!Linux基礎教程Linux
- 雲原生應用程式執行時 Kyma 的主要特性介紹
- 【kingsql分享】Oracle Database 19c的各種新特性介紹SQLOracleDatabase
- Golang Agent 可觀測性的全面升級與新特性介紹Golang