OceanBase数据库:换个角度看 “宏块” 1

宏块是数据文件写数据的最小单位,话不多说。

ObDataStoreDesc 数据存储描述类

static const int64_t DEFAULT_RESERVE_PERCENT = 90;
static const int64_t MIN_MICRO_BLOCK_SIZE = 4 * 1024; //4KB
static const int64_t MIN_RESERVED_SIZE = 1024; //1KB;
static const int64_t MIN_SSTABLE_SNAPSHOT_VERSION = 1; // ref to ORIGIN_FOZEN_VERSION
static const int64_t MIN_SSTABLE_END_LOG_TS = 1; // ref to ORIGIN_FOZEN_VERSION
static const ObCompressorType DEFAULT_MINOR_COMPRESSOR_TYPE = ObCompressorType::LZ4_COMPRESSOR;
// emergency magic table id is 10000
static const uint64_t EMERGENCY_TENANT_ID_MAGIC = 0;
static const uint64_t EMERGENCY_LS_ID_MAGIC = 0;
static const ObTabletID EMERGENCY_TABLET_ID_MAGIC;
  • DEFAULT_RESERVE_PERCENT = 90,每一个宏块空间使用的百分比,默认使用宏块空间的90%,会预留10%的空间用于后续的操作,如新增数据或数据变动。
  • MIN_MICRO_BLOCK_SIZE = 4 * 1024,微块的最小大小为4KB,当前主流的SSD的物理页大小是4KB,微块在编码和压缩时最小的处理单位就是4KB,微块是变长的,那么微块的大小一定是4KB的倍数,如16KB,这样做是为了提高数据的处理效率。
  • MIN_RESERVED_SIZE = 1024,宏块中最少保留的空间大小是1KB,预防一些特殊情况,避免空间不足导致一些写入操作失败。
  • MIN_SSTABLE_SNAPSHOT_VERSION = 1,最小SSTable快照版本为1,这是快照的起始版本即最小冻结版本。
  • MIN_SSTABLE_END_LOG_TS = 1,最小SSTable结束日志时间戳为1,通常在多版本中表示最小最原始的冻结版本。
  • DEFAULT_MINOR_COMPRESSOR_TYPE ,默认生成L1层的Minor SSTable 时使用的通用压缩算法lz4,这个算法在压缩速度和压缩率之间更加平衡一些,使得数据在I/O时有更高的吞吐量。
  • emergency magic table id is 10000 ,应急magic的table id 为10000,这不是正常集群使用的,在集群元数据损坏或者极端异常情况才能使用的应急模式,通过应急配置可以进入修复或恢复模式。
  • EMERGENCY_TENANT_ID_MAGIC = 0 ,在应急模式时,设置租户ID,跳过租户相关的配置校验。
  • EMERGENCY_LS_ID_MAGIC = 0 ,在应急模式时,设置日志流ID,跳过日志流同步相关的校验。
  • ObTabletID EMERGENCY_TABLET_ID_MAGIC 在应急模式时,设置tablet id 为 0 表示无效的tablet id ,跳过 tablet 相关校验。
class ObTabletID
{
public:
  static const uint64_t INVALID_TABLET_ID = 0;
}
const ObTabletID ObDataStoreDesc::EMERGENCY_TABLET_ID_MAGIC = ObTabletID(0);
share::ObLSID ls_id_;
ObTabletID tablet_id_;
int64_t macro_block_size_;
int64_t macro_store_size_; //macro_block_size_ * reserved_percent
int64_t micro_block_size_;
int64_t micro_block_size_limit_;
int64_t row_column_count_;
int64_t rowkey_column_count_; // mv rowkey cnt
ObRowStoreType row_store_type_;
bool need_build_hash_index_for_micro_block_;
int64_t schema_version_;
int64_t schema_rowkey_col_cnt_;
int64_t full_stored_col_cnt_; // table stored column count including hidden columns
ObMicroBlockEncoderOpt encoder_opt_;
ObSSTableMergeInfo *merge_info_;
storage::ObMergeType merge_type_;
  • share::ObLSID ls_id_ ,宏块所属日志流ID。
  • ObTabletID tablet_id_ ,宏块所属tablet id。

通过日志流 id + tablet id 来定位一个SSTable,在分布式数据库中,LS日志流来区分不同的节点。

  • int64_t macro_block_size_ ,宏块的总大小,一般是2MB。
  • int64_t macro_store_size_; // macro_block_size_ * reserved_percent ,宏块实际使用的总空间,2MB * 90% 是宏块可使用空间,剩余空间为保留空间,用于后续操作。
  • int64_t micro_block_size_ ,微块的总大小,是变长的,默认约等于16KB。微块是数据库读操作的最小单位,这个大小不是固定的,在创建表时可以自定义微块的长度,最小为 MIN_MICRO_BLOCK_SIZE 4KB,最大的大小限制为 micro_block_size_limit_ 。
CREATE TABLE table_name
table_option:
    block_size
  • int64_t micro_block_size_limit_ ,微块大小的最大限制。
micro_block_size_limit_ = macro_block_size_
                          - ObMacroBlockCommonHeader::get_serialize_size()
                          - ObSSTableMacroBlockHeader::get_fixed_header_size()
                          - MIN_RESERVED_SIZE;

微块大小的最大限制值=宏块大小2MB-宏块通用头部序列化空间-宏块头部固定空间-最小保留空间大小 1KB ,这个值基本上接近于2MB。

  • int64_t row_column_count_ ,行中包含的列总数,包含隐藏列。
  • int64_t rowkey_column_count_ ,行键包含的列数。
  • ObRowStoreType row_store_type_ ,行存储的类型。
enum ObRowStoreType : uint8_t
{
  FLAT_ROW_STORE = 0,
  ENCODING_ROW_STORE = 1,
  SELECTIVE_ENCODING_ROW_STORE = 2,
  MAX_ROW_STORE,
  DUMMY_ROW_STORE = UINT8_MAX, // invalid dummy row store type for compatibility
};

常用的三种存储类型分别是 FLAT_ROW_STORE 扁平存储模式,高写入、低延迟和低压缩,适用于OLTP 模式;ENCODING_ROW_STORE 编码存储模式,低写入、中等延时和高压缩,一般用于数据较稳定时,适用于OLAP 模式;SELECTIVE_ENCODING_ROW_STORE 选择编码模式,列存或向量化使用,适用于HTAP 模式。

  • bool need_build_hash_index_for_micro_block_ ,是否为微块构建hash索引,点查的场景下使用。
  • int64_t schema_version_ ,schema的版本号。
  • int64_t schema_rowkey_col_cnt_ ,当前schema的行键列总数。
  • int64_t full_stored_col_cnt_ ,表存储的列计数,包括隐藏列。
  • ObMicroBlockEncoderOpt encoder_opt_ ,微块的编码选项。
struct ObMicroBlockEncoderOpt
{
  OB_INLINE void set_store_type(common::ObRowStoreType store_type) {
    switch (store_type) {
      case SELECTIVE_ENCODING_ROW_STORE:
        enable_bit_packing_ = false;
        store_sorted_var_len_numbers_dict_ = true;
        encodings_ = ENCODINGS_FOR_PERFORMANCE;
        break;
      case ENCODING_ROW_STORE:
        enable_bit_packing_ = true;
        store_sorted_var_len_numbers_dict_ = false;
        encodings_ = ENCODINGS_DEFAULT;
        break;
      default:
        enable_bit_packing_ = false;
        store_sorted_var_len_numbers_dict_ = false;
        encodings_ = ENCODINGS_NONE;
        break;
    }
  }
};

在设置微块存储编码模式时,默认的编码为 ENCODINGS_NONE ,其它类型 为 ObRowStoreType 中对应。

  • ObSSTableMergeInfo *merge_info_ ,合并信息。
  • storage::ObMergeType merge_type_ ,合并类型。
enum ObMergeType
{
  INVALID_MERGE_TYPE = -1,
  MINOR_MERGE = 0,  // minor merge, compaction several mini sstable into one larger mini sstable
  HISTORY_MINOR_MERGE = 1,
  META_MAJOR_MERGE = 2,
  MINI_MERGE = 3,  // mini merge, only flush memtable
  MAJOR_MERGE = 4,
  MEDIUM_MERGE = 5,
  DDL_KV_MERGE = 6,
  BACKFILL_TX_MERGE = 7,
  MDS_TABLE_MERGE = 8,
  MERGE_TYPE_MAX
};

在日常使用或学习的过程中,数据合并的类型只接触过 MINI_MERGE(转储) MINOR_MERGE(小合并) MAJOR_MERGE(大合并) MEDIUM_MERGE (自适应合并),这四种合并方式,继续了解一下其它的合并方式。

  • INVALID_MERGE_TYPE = -1, 无效的合并类型。
  • MINOR_MERGE = 0, // minor merge, compaction several mini sstable into one larger mini sstable ,小合并,将多个小的mini sstable 合并为一个大的mini sstable。
  • HISTORY_MINOR_MERGE = 1, 历史小合并,多版本(MVCC)的情况下,合并历史版本数据,历史版本过期清理,如在一个事物提交后,提交前的快照版本数据可回收后会进行清理。
  • META_MAJOR_MERGE = 2, 元数据大合并(基线合并),常见于schema变更、租户业务数据的元数据信息。
  • MINI_MERGE = 3, // mini merge, only flush memtable ,转储,仅将 memtable 数据刷到磁盘。
  • MAJOR_MERGE = 4, 大合并(基线合并)。
  • MEDIUM_MERGE = 5, 自适应合并。
  • DDL_KV_MERGE = 6, DDL KV 合并。
virtual_table/ob_all_virtual_tablet_ddl_kv_info.cpp
#include "observer/virtual_table/ob_all_virtual_tablet_ddl_kv_info.h"
#include "storage/tx_storage/ob_ls_service.h"
#include "storage/ddl/ob_tablet_ddl_kv.h"
#include "storage/ddl/ob_tablet_ddl_kv_mgr.h"

class ObDDLKV : public blocksstable::ObSSTable
{
public:

  ObDDLKV();
  virtual ~ObDDLKV();
  int init(ObTablet &tablet,
           const share::SCN &ddl_start_scn,
           const int64_t snapshot_version,
           const share::SCN &last_freezed_scn,
           const int64_t data_format_version);
  int set_macro_block(ObTablet &tablet, const ObDDLMacroBlock ¯o_block);

private:
  share::SCN ddl_start_scn_; // the log ts of ddl start log
  int64_t snapshot_version_; // the snapshot version for major sstable which is completed by ddl
  common::TCRWLock lock_; // lock for block_meta_tree_ and freeze_log_ts_
  common::ObArenaAllocator arena_allocator_;
  share::SCN last_freezed_scn_; // the freezed log ts of last ddl kv. the log ts range of this ddl kv is (last_freezed_log_ts_, freeze_log_ts_]
  share::SCN min_scn_; // the min log ts of macro blocks
  share::SCN max_scn_; // the max log ts of macro blocks
  share::SCN freeze_scn_; // ddl kv refuse data larger than freeze log ts, freeze_log_ts >= max_log_ts
  int64_t pending_cnt_; // the amount of kvs that are replaying
  int64_t data_format_version_;
  ObBlockMetaTree block_meta_tree_;
};

从上述的代码中,大概可以猜到在执行 ddl 操作时,会记录 ddl 相关的元数据信息,后续会根据这些信息进行ddl kv 元数据合并,将这些信息对应的元数据信息,应用到 ddl 的对象。

  • BACKFILL_TX_MERGE = 7, 事物回填合并。当分布式事务提交后,不能只留存事务日志在磁盘中,事务回填会将事务日志中的操作应用到内存数据,后续写到SSTable中。
  • MDS_TABLE_MERGE = 8, 元数据服务表合并,如副本信息、租户配置信息或租户资源信息等。
  • MERGE_TYPE_MAX ,上限。

注:仅代表个人看法!

1 个赞

楼主厉害,理解得比较透彻

1 个赞