cassandra数据库存储结构_ cassandra数据库数据的写入,读取和删除

cassandra数据库存储结构_ cassandra数据库数据的写入,读取和删除,第1张

前言


      本文主要介绍 Cassandra 中数据的存储格式,包括在内存中的数据和磁盘中数据。Cassandra 的写的性能表现非常好,为什么写的性能这么好?和它的数据结构有没有关系,以及和它的写的机制又有多大的关系。同时也将分析哪些因素会影响读的性能 Cassandra 又做了哪些改进。

Cassandra 的数据存储结构主要分为三种:

1、CommitLog:主要记录下客户端提交过来的数据以及 *** 作。这个数据将被持久化到磁盘中,以便数据没有被持久化到磁盘时可以用来恢复。

2、Memtable:用户写的数据在内存中的形式,它的对象结构在后面详细介绍。其实还有另外一种形式是 BinaryMemtable 这个格式目前 Cassandra 并没有使用,这里不再介绍了。

3、SSTable:数据被持久化到磁盘,这又分为 Data、Index 和 Filter 三种数据格式。

CommitLog 数据格式

CommitLog 的数据只有一种,那就是按照一定格式组成 byte 组数,写到 IO 缓冲区中定时的被刷到磁盘中持久化,在上一篇的配置文件详解中已经有说到 CommitLog 的持久化方式有两种,一个是 Periodic 一个是 Batch,它们的数据格式都是一样的,只是前者是异步的,后者是同步的,数据被刷到磁盘的频繁度不一样。关于 CommitLog 的相关的类结构图如下:

图 1. CommitLog 的相关的类结构图

cassandra数据库存储结构_ cassandra数据库数据的写入,读取和删除,cassandra数据库存储结构_ cassandra数据库数据的写入,读取和删除,第2张

它持久化的策略也很简单,就是首先将用户提交的数据所在的对象 RowMutaTIon 序列化成 byte 数组,然后把这个对象和 byte 数组传给 LogRecordAdder 对象,由 LogRecordAdder 对象调用 CommitLogSegment 的 write 方法去完成写 *** 作,这个 write 方法的代码如下:

清单 1. CommitLogSegment. write

public CommitLogSegment.CommitLogContext write(RowMutaTIon rowMutaTIon,

Object serializedRow){

long currentPosiTIon = -1L;

...

Checksum checkum = new CRC32();

if (serializedRow instanceof DataOutputBuffer){

DataOutputBuffer buffer = (DataOutputBuffer) serializedRow;

logWriter.writeLong(buffer.getLength());

logWriter.write(buffer.getData(), 0, buffer.getLength());

checkum.update(buffer.getData(), 0, buffer.getLength());

}

else{

assert serializedRow instanceof byte[];

byte[] bytes = (byte[]) serializedRow;

logWriter.writeLong(bytes.length);

logWriter.write(bytes);

checkum.update(bytes, 0, bytes.length);

}

logWriter.writeLong(checkum.getValue());

...

}

这个代码的主要作用就是如果当前这个根据 columnFamily 的 id 还没有被序列化过,将会根据这个 id 生成一个 CommitLogHeader 对象,记录下在当前的 CommitLog 文件中的位置,并将这个 header 序列化,覆盖以前的 header。这个 header 中可能包含多个没有被序列化到磁盘中的 RowMutation 对应的 columnFamily 的 id。如果已经存在,直接把 RowMutation 对象的序列化结果写到 CommitLog 的文件缓存区中后面再加一个 CRC32 校验码。Byte 数组的格式如下:

图 2. CommitLog 文件数组结构

cassandra数据库存储结构_ cassandra数据库数据的写入,读取和删除,cassandra数据库存储结构_ cassandra数据库数据的写入,读取和删除,第3张

上图中每个不同的 columnFamily 的 id 都包含在 header 中,这样做的目的是更容易的判断那些数据没有被序列化。

CommitLog 的作用是为恢复没有被写到磁盘中的数据,那如何根据 CommitLog 文件中存储的数据恢复呢?这段代码在 recover 方法中:

清单 2. CommitLog.recover

public static void recover(File[] clogs) throws IOException{

...

final CommitLogHeader clHeader = CommitLogHeader.readCommitLogHeader(reader);

int lowPos = CommitLogHeader.getLowestPosition(clHeader);

if (lowPos == 0) break;

reader.seek(lowPos);

while (!reader.isEOF()){

try{

bytes = new byte[(int) reader.readLong()];

reader.readFully(bytes);

claimedCRC32 = reader.readLong();

}

...

ByteArrayInputStream bufIn = new ByteArrayInputStream(bytes);

Checksum checksum = new CRC32();

checksum.update(bytes, 0, bytes.length);

if (claimedCRC32 != checksum.getValue()){continue;}

final RowMutation rm =

RowMutation.serializer().deserialize(new DataInputStream(bufIn));

}

...

}

这段代码的思路是:反序列化 CommitLog 文件的 header 为 CommitLogHeader 对象,寻找 header 对象中没有被回写的最小 RowMutation 位置,然后根据这个位置取出这个 RowMutation 对象的序列化数据,然后反序列化为 RowMutation 对象,然后取出 RowMutation 对象中的数据重新保存到 Memtable 中,而不是直接写到磁盘中。CommitLog 的 *** 作过程可以用下图来清楚的表示:

图 3. CommitLog 数据格式的变化过程

cassandra数据库存储结构_ cassandra数据库数据的写入,读取和删除,cassandra数据库存储结构_ cassandra数据库数据的写入,读取和删除,第4张

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/dianzi/2718084.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-08-17
下一篇 2022-08-17

发表评论

登录后才能评论

评论列表(0条)

保存