cassandra作为海量数据处理的DB,为了提升性能,则先将数据写入到内存表memtable中,然后当memtable达到一定容量条件时,再将memtable中数据持久化到硬盘上。但是如果系统宕机,或者重启,那么内存的数据就会丢失,所以需要一个保护功能——commitlog,来恢复原来存在于内存表中的数据。
1、commitlog触发的条件
条件1:CF所在的Keyspace的属性durableWrites,如果durableWrites为true,则写入数据的时候,同时会写commitlog。
条件2:写入的数据序列化以后写入commitlog文件中的长度(序列化长度+4(长度)+8(校验码的长度)+8(校验码))必须小于等于commitlog文件的大小(cassandra.yaml文件中的配置项:commitlog_segment_size_in_mb:32)。才会写commitlog,否者告警,但是不写。
long totalSize = RowMutation.serializer.serializedSize(rm, MessagingService.current_version) + CommitLogSegment.ENTRY_OVERHEAD_SIZE;
if (totalSize > DatabaseDescriptor.getCommitLogSegmentSize())
{
logger.warn("Skipping commitlog append of extremely large mutation ({} bytes)", totalSize);
return;
}
2、commitlog记录数据的流程
2.1、执行器
如果一旦需要写commitlog,cassandra也不会每次每条数据都进行持久化到磁盘commitlog文件中,因为这样会严重影响数据写入的性能。所以cassandra会将每次数据提交给另外一个执行器进行数据的持久化。
执行器有两种。分别为:BatchCommitLogExecutorService和PeriodicCommitLogExecutorService,是有cassandra.yaml文件中配置项commitlog_sync来决定的。
执行器1:BatchCommitLogExecutorService
配置项:
commitlog_sync: batch
commitlog_sync_batch_window_in_ms: 50
执行器BatchCommitLogExecutorService,将50ms内的数据作为一个批次进行持久化到硬盘上去。
执行器1:PeriodicCommitLogExecutorService
配置项:
commitlog_sync: periodic
commitlog_sync_period_in_ms: 10000
有commitlog的写入的任务,则将数据直接写入到CommitLogSegment,但是没10s(commitlog_sync_period_in_ms)中才会将CommitLogSegment中的数据持久化到硬盘中。
2.2、分配器CommitLogAllocator
需要写入commitlog记录的数据,首先会写入到CommitlogSegment中,而一个CommitlogSegment就相当于commitlog文件的内存映射一样,记录着每个CF对应写入commitlog文件的记录的起始位置,该信息存放在属性private final HashMap
2.3、commitlog数据的序列化
数据写入commitlog中的序列化格式和sstable中的格式是一致的,但是多了一重校验数据完整性的验证码。使用的是CRC32校验。
Checksum checksum = new PureJavaCrc32();
byte[] serializedRow = FBUtilities.serialize(rowMutation, RowMutation.serializer, MessagingService.current_version);
checksum.update(serializedRow.length);
buffer.putInt(serializedRow.length);
buffer.putLong(checksum.getValue());
buffer.put(serializedRow);
checksum.update(serializedRow, 0, serializedRow.length);
buffer.putLong(checksum.getValue());
3、commitlog恢复数据的流程