Cassandra 3.x官方文档_数据库内部

存储引擎

Cassandra使用了一种类似于日志结构合并树的存储结构,而不是像传统关系型数据库那样使用B-TreeCassandra避免写之前读。写前读,尤其是大型的分布式系统中,可能导致读取性能的延迟和其他问题。例如,两个客户端同时读。一个以覆盖的方式更新A,另一个以覆盖的方式更新B,同时删除A的更新。这种竞争条件会导致模棱两可的查询结果虽然更新都是对的。

 

Cassandra中为了避免写前读,存储引擎组在内存中进行插入和更新操作,然后在间隔的时候,依次将数据以追加的模式写入磁盘。一旦写入磁盘,数据就是不可变的,而且永远不会覆盖。读取数据需要结合这些不可变的、顺序写入的数据,以此发现正确的查询结果。你可以在写之前使用轻量级事务(LWT)来检查数据的状态。然而,此功能只被推荐用于有限的场合。

 

日志结构引擎避免了覆盖,使用了顺序IO来更新数据,对于写数据到固态硬盘和普通硬盘来说,是至关重要的。在HDD上,随机写比顺序写涉及到更多的查找操作。所导致的害处是实在的。由于Cassandra顺序写不可改变的文件,因此避免了写放大和磁盘故障,数据库容纳便宜的,消费者固态硬盘尤其好。在很多其他数据库上,写入放大是在SSD上是一个问题。

 

Cassandra如何读和写数据

Understanding howCassandra stores data.

要想管理和访问Cassandra的数据,理解Cassandra如何存储数据是非常重要的。hinted handoff的特性增加了Cassandra的性能,而且ACID(原子性,一致性,隔离性,持久性)的非一致性的数据库属性是理解读和写的关键概念。在Cassandra里,一致性的意思是如何保持最新,和在所有副本上的行的数据保持同步。

 

用于开发数据存储和索引的应用程序的客户端工具和应用编程接口(API)都是可用的。

 

数据如何写?

Understand how Cassandrawrites and stores data.

Cassandra在写路径上处理数据分为好几个阶段,以立即写日志开始,以写数据到磁盘结束。

写数据日志至commit log

写数据至memtable

memtable刷新数据

存储数据值磁盘的SSTables

 

日志写入和memtable存储

当一个写发生时,Cassandra把数据存储到内存中的一个叫做memtable的结构里,而且为了提供可配置的持久化,它也将写入到磁盘的commit log里。Commit log接收Cassandra节点的每一个写操作,这些持久化的写操作永久存在,即使节点出现故障。Memtable是一个数据分区的写回缓存,Cassandra通过key查找。Memtable存储已排好序的写操作,直到达到配置的限制,然后再刷新。

 

memtable刷新数据

刷新数据,Cassandra会按照memtable排序的顺序把数据写入磁盘。同时一个分区索引也会在磁盘上被创建,可以把token值映射到磁盘的某个位置上。当memtable的内容超过了配置好的阈值或者commit log的空间超过了commitlog_total_space_in_mb,这时memtable会进入一个会刷新到磁盘的队列中。该队列可以使用cassandra.yaml文件里的memtable_heap_space_in_mb或者memtable_offheap_space_in_mb来配置。如果将要刷新的数据超过了memtable_cleanup_threshold的值,Cassandra会阻塞写操作,直到下一个刷新成功。你可以使用

nodetool flush或者nodetooldrain(不需要监听其他节点,刷新memtables)直手动刷新一个表。为了减少commit log的重播时间,推荐最佳实践是在你启动节点之前刷新memtables。如果一个节点停止工作,在它停止之前重新提交commit log恢复memtable写。

 

commit log里的数据在相应的memtable里的数据刷新到磁盘的SSTable里以后,会进行合并。

 

Storingdata on disk in SSTables

每一个表都维护着一个MemtablesSSTablescommit log是所有的表共享的。SSTables是不可变的,在memtable刷新过后不会再重新写入。因此,一个分区通常保存在多个SSTable文件里。大量其他的SSTable结构存在着,以协助读操作。


对于每一个SSTableCassandra会创建这些结构:

数据 (Data.db)

SSTable的数据。

主要索引 (Index.db)

行键的索引,有指针指向他们在数据文件中的位置

Bloom过滤器(Filter.db)

内存中存储的一个结构,在数据进入磁盘的SSTables之前检查数据是否存在于memtable里。

Compression信息(CompressionInfo.db)

一个文件,包含未压缩的数据长度,块偏移量和其他的压缩信息。

统计 (Statistics.db)

统计SSTable的内容元数据。

文摘 (Digest.crc32, Digest.adler32, Digest.sha1)

包含数据文件的adler32校验和的文件

CRC(CRC.db)

包含在一个未压缩文件里的块的CRC32的文件。

SSTable索引摘要(SUMMARY.db)

保存在内存中的分区索引的示例。

SSTable表的内容 (TOC.txt)

存储sstable表的内容所有组件列表的文件。

第二索引 (SI_.*.db)

内置第二索引,一个SSTable里可存在多个第二索引。

 

 

SSTables是存储在磁盘上的文件。SSTable文件的命名规则在Cassandra2.2和后来的版本中发生了改变,为了缩短文件路径。数据文件存储的位置随着安装方式的不同而不同。对于每一个键空间,数据目录内的目录存储每一个表。例如,/data/data/ks1/cf1-5be396077b811e3a3ab9dc4b9ac088d/la-1-big-Data.db代表一个数据文件。开始表示键空间的名称,以区分流空间或者批量加载数据。这个例子中的十六进制字符串5be396077b811e3a3ab9dc4b9ac088d,追加到表名称后面,来表示唯一的表ID

 

Cassandra为每一个表创建一个子目录,这样你可以使用一个表来选择物理驱动器或数据量。这样就提供了把表转移到更快的媒介上的能力来获得更好的性能,比如SSD,而且在存储层上在所有附加的存储设备上划分表以获得更好的I/O平衡。

 

数据是如何保持的?

Cassandra processes dataat several stages on the write path. Compaction to maintain healthy SSTables isthe last step in the write path process.

Cassandra写过程把数据存储到一个叫做SSTables的结构里。SSTables是不可变的。Cassandra为新插入的或者更新的数据写入一个新时间戳的版本到新的SSTables里,而不是使用插入或者更新的数据去覆盖已存在的行。同样,Cassandra也不删除空间:代替的是,Cassandra把需要删除的数据标记为tombstone(墓碑)

 

随着时间的推移,Cassandra可能为每一行写了很多的版本,每一个都在不同的SSTable里。每一个版本都有一个不同的时间戳作为唯一的标志存储。这意味着Cassandra必须有越来越多的SSTables来检索数据的一整排。

 

为了保持数据库的健壮性,Cassandra周期性的合并SSTables和丢弃旧的数据。这个过程叫做compaction

Compaction

Compaction通过分区键来合并每一个SSTable里的数据,使用最近的时间戳来选择数据的版本。合并的过程是永久的。因为每一个SSTable里的行是通过分区键排序的,因此合并过程并不使用随机I/O。在移除已删除的列和行以后,compaction过程把SSTables合并到一个单一的SSTables里。旧的SSTables文件只要等待使用文件完成读取以后就会被删除。


Compaction会导致一个临时的磁盘占用和磁盘I/O,这是旧的和新的SSTables同时存在。当它完成以后,compaction释放旧的SSTables占用的磁盘空间。它通过压缩过的SSTables替换旧SSTables来提高了读性能。Cassandra甚至可以在完成写之前直接从新的SSTable里读取数据,而不是等待整个compaction过程完成以后。

 

Cassandra处理读和写的时候,它在页缓存里使用新SSTables替换掉旧的SSTables。缓存新的SSTables,把读操作导向新的SSTables是一个渐进的过程-它不会导致戏剧性的缓存丢失。Cassandra提供了可预见的高性能,即使在高负载下。

 

Compaction策略

Cassandra支持不同的compaction策略。每一个都有自己的优势。理解每一种策略是如果工作的,对于选择适合你自己应用程序工作负载的策略是至关重要的。虽然下面的每一种选择都是以广义的建议开始的,但是有很多复杂的因素影响了compaction策略的选择。有关更多关于每一种策略相对的优势,和指定使用用例的讨论的更多信息,参考:哪一种compaction策略是最好的?

 

 

SizeTieredCompactionStrategy(STCS)

推荐用于写密集工作负载

 

当一组(默认4)相似大小的SSTables累积起来的时候,SizeTieredCompactionStrategy(STCS)初始化compaction。该compaction策略合并这些SSTables成一个更大的SSTable。然后这个更大的SSTables依次合并成一个更大的SSTables。在任何给定的时间里,几个大小不等的SSTables同时存在。


当这个策略在一个写密集的工作负载下运行的很好的时候,它使读变得更慢,因为按大小合并的过程并不倾向于按行分组数据。因为这个原因,它更可能是一个特殊行的数据可能分布在很多的SSTables里。同样,移除已被删除的数据的发生是不可预知的,因为SSTable的大小是compaction过程的触发器,而且SSTables增长的速度可能还不能达到合并和移除旧数据的条件。由于最大的SSTables大小会增长,磁盘compaction空间数量需要同时既容纳新的,也要容纳旧的SSTables,可能超过一个典型的节点的磁盘空间大小。

Pros:压缩写密集工作负载很不错

Cons:可以保持过时的数据太长。需要的内存量随着时间的推移而增加。

 

LeveledCompactionStrategy(LCS)

推荐用于读密集工作负载

 

LeveledCompactionStrategy(LCS)致力于减轻STCS的一些读操作的问题。该策略有一系列的工作级别。第一,memtables在第一个级别(L0)被刷新到SSTables里。然后在级别L1compaction把这些SSTables合并成一个更大的SSTables

 

图:分级的compaction------添加SSTables


L1级别更高的SSTables会合并到一个大小比sstable_size_in_mb(默认:160MB)更大或者相等的SSTables里。如果一个L1级别SSTable存储的分区数据比L2的还大,LCS会把SSTable跨过L2到下一个级别。

图:经过很多次插入后的分级compaction


在每一个L0以上的级别,LCS创建大小大概相同的SSTables。每一个级别上是上一个级别的10倍大小,因此L1L0的十倍大,级别L2100倍大小。如果compaction的结果超过了L1SSTables10倍,多余的SSTables会被转移到L2级别。

 

LCS compaction过程保证了以L1开始的每个级别的SSTables没有重叠的数据。对于很多的读操作,这个保证了Cassandra可以只从一个或者两个Cassandra里检索所需的数据。实际上,90%的读,一个SSTable就可以满足。虽然LCS不会影响L0表,然而包含很多L0 SStables的资源密集型的读仍有可能发生。

 

级别超过L0的,LCS需要更少的空间来compacting----一般来说,10倍的固定SSTable大小。过时的数据删除的更频繁,因此删除的数据在磁盘上使用更小的一部分SSTables。然而,LCS compaction操作会更频繁的发生,而且在节点上有更多的I/O负担。对于写密集的负载,使用该策略的收益比不上I/O操作损失的性能。在很多情况下,测试LCS配置表显示了在写和压缩上的I/O饱和度。

注:Cassandra 2.2和后来的版本中,当使用LCS引导一个新节点到一个集群中去的时候,通过compaction操作提高性能。原始数据直接移动到正确的级别,因为没有现成的数据,所以每个级别没有重叠分区存在。更多信息,参考Apache Cassandra 2.2 – Leveled Compaction引导性能提升。

Apache Cassandra 2.2 -Bootstrapping Performance Improvements for Leveled Compaction.

Pros:磁盘需求更容易预测。读操作延迟更能预测。旧的数据更新的更加频繁。

Cons:更高的I/O利用,影响操作延迟

 

DateTieredCompactionStrategy(DTCS)

推荐用于时间序列和过期TTL负载。

DateTieredCompactionStrategy(DTCS)STCS类似。但是他不是基于SSTable大小来压缩的。DTCS基于SSTable的年龄来压缩的。配置时间窗口来保证新的和旧的数据不会在合并的SSTables里混在一起。事实上,使用生存时间戳(TTL)DTCS可以删除包含过期数据的整个SSTables。该策略通常生成相似大小的SSTables,如果时间序列以一个稳定的速率在获取。当系统在配置的一个时间间隔内达到SSTables的最小阈值的时候,DTCS合并SSTables。当在一个时间间隔内所需要的SSTables 数量下降时,DTCS合并SSTables成一个更大的表。然而,DTCS在达到配置的年龄的时候会跳过压缩SSTables。这减少了重写数据的次数。在DTCS-compacted SSTables里,查询最近一小时(或者一些其他的时间间隔)有价值的数据会执行的很有效率。一种可能导致该策略出问题的情况是无序的写---例如,一个操作使用以前的时间戳来写时间戳记录。读修复可能会删除无序的时间戳,因此当使用DTCS的时候,确保读修复是关闭的。

Pros:专为时间序列数据设计。

Cons: 无序的数据注入可能导致错误。使用DTCS,必须关闭读修复(Read repair),而且TIMESTAMP选项必须使用BATCH, DELETE, INSERT UPDATE CQL 命令。.

 

哪个compaction策略是最好的?

要想实现最好的compaction策略:

1.检查你的应用程序的需求。

2.使用最合适的策略来配置表

3.用你的数据测试compaction策略。

以下的问题基于Cassandra开发者和用户使用以上描述的策略的经验

 

你的表处理时间序列数据吗?

如果是,DTCS也许是你最好的选择。为了避免出问题,检查以上的描叙。如果你的表并不关注时间序列数据,选择会变得更复杂。下面的介绍其他考虑因素的问题也许可以引导你做出选择。

你的表处理读比写多,还是写比读多?

如果你的表处理的读是写的两倍或者更多,LCS是一个不错的选择---尤其是随机读。如果读和写的比率很接近,LCS性能的损失可能大于收益。注意,LCS可以迅速被大量的写淹没。

你的表中的数据改变频繁吗?

LCS其中的一个优点是,它把有关联的数据放在一个小的SSTables里。如果你的数据是不可变的或者不是频繁的更新和插入,STCS完成相同的类型分组而不会有性能损失。

你需要读和写活动的可预测级别吗?

LCS在可预测大小和数量上维持SSTables。比如,如果你的表的读/写比率小,而且预计符合读的服务级别协议(SLAs),它也许值得损失写性能来保持读速率和延迟在一个可预见的水平。而且你可以通过水平缩放来克服这个写性能的损失-----添加更多节点。

你的表会被批处理过程填充吗?

On both batch readsand batch writes, STCS performs better than LCS. The batch process causes littleor no fragmentation, so the benefits of LCS are not realized. And batchprocesses can overwhelm LCS--configured tables.

不管是批量读还是批量写,STCS都比LCS表现要好。批处理过程产生很少或者没有碎片,因此LCS的收益没有体现出来。而且批处理过程可以压倒LCS配置的表。

你的系统有磁盘空间限制吗?

LCS处理磁盘空间比STCS更有效率,除了被处理的数据占据之外,它需要大概10%的净空间。STCS 通常 DTCS需要更多-------在某些情况下,超过数据空间的50%

你的系统达到了I/O的限制吗?

LCS明显的比DTCS或者STCS更加的I/O密集型。切换到LCS可能引入额外的I/O负载,抵消了优点。

 

测试compaction策略

决定使用哪种compaction策略对于你的系统是最好的建议:

创建三个节点的集群,使用其中的一种compaction策略,使用cassandra-stress来压力测试集群,衡量结果。

在你已存在的集群上启动一个节点,使用Cassandra的写样本实时数据的调查。参考:

Cassandra 1.1新特性:现场流量采集

配置和运行compaction

使用CREATE TABLE 或者ALTER TABLE命令,为表设置compaction策略。详细信息,参见Table properties

 

你可以通过nodetool compact 命令手动的启动compaction

关于compaction的更多信息

下面开发者的博客测试compaction策略提供了更多信息:

何时使用Leveled Compaction

Apache Cassandra 中的Leveled compaction

DateTieredCompactionStrategy:字段的注释

CassandraDate-Tiered Compaction

DateTieredCompactionStrategy: 为时间序列数据的Compaction

Cassandra中使用LCS的时候什么延误了tombstone的清理

 

 

数据如何更新?

A brief description ofthe write path of an update.

插入一个主键重复的数据会被视为一个更新插入。如果数据不存在,一个更新插入就写一条新纪录到数据库里。如果数据的主键已经存在,新的记录就会写一条最近的时间戳进去。这时候如果读这条数据,只有最新的记录才会被检索。旧的时间戳数据会被标记为删除。就类似于使用新的值来覆盖旧的值,虽然Cassandra并不会覆盖数据。最终,这些更新会使用顺序I/O写入磁盘里,保存在一个新的SSTable里。在更新过程中,Cassandra使用写路径写列到磁盘。如果一个用户多次写入同一个数据,只有最新版本会存入memtable然后刷新到磁盘,就像Compaction选项描述的那样。

 

数据如何删除

How Cassandra deletesdata and why deleted data can reappear.

Cassandra删除数据的过程设计成了提高性能,与Cassandra的数据分布和容错的内置属性兼容。

 

Cassandra把删除当成一个插入或者更新插入。这些数据被添加到带有一个删除标记(墓碑)的删除命令行里面。墓碑经过Cassandra's的写路径,然后写入一个或多个节点的SSTables里。主要的不同:墓碑有一个内置的过期日期/时间。在过期时间结束的时候,墓碑才有资格作为Cassandra的正常compaction 过程被删除。

 

你也可以使用time-to-live(生成时间)来标记一条Cassandra的记录。当这个时间结束,Cassandra使用墓碑标记这条记录,然后像其他的墓碑记录那样来处理它。

 

在分布式系统中的删除

在一个多节点集群中,Cassandra把相同数据的副本存储到两个或者更多节点上。这样有助于防止数据丢失,但是使删除过程复杂化了。如果一个节点收到一个删除本地数据的命令,该节点把指定的记录变成墓碑,然后尝试把墓碑传递到包含有该记录的其他节点上。但是如果那个时候一个副本节点刚好没有响应,它不会立即接收到墓碑,因此它依然保存着记录删除之前的版本。如果这个时候,这个墓碑记录已经从集群中其他节点删除了,这个节点突然恢复了,Cassandra就会把恢复的节点的记录当成新的数据,然后传播给集群的其他节点。这种已经删除的,但是仍然存在的记录称作僵尸。

 

为了防止僵尸再现,Cassandra给每一个墓碑一个宽限期。目的是给未响应的节点时间来恢复,正常的处理墓碑。如果客户端在宽限期内写一个新的更新到墓碑上,Cassandra覆盖该墓碑。如果客户端在宽限期内发送一条读那条记录的请求,Cassandra会无视墓碑,然后检索其他副本上的记录。

 

如果一个未响应的节点恢复了,Cassandra使用hinted handoff 来重现节点宕机期间数据库在节点上的改变。Cassadra在宽限期内不会重现墓碑的改变。但是如果节点在宽限期结束的时候还没有恢复,Cassandra可能错过删除。

 

在墓碑宽限期结束以后,Cassandracompaction期间删除墓碑。可以为每个独立的表设置gc_grace_period属性。默认值是86400(24小时)

 

更多关于Cassandra的删除

细节:

• T一个墓碑的过期日期/时间是它的创建日期/时间加上表的gc_grace_seconds属性的值。

• Cassandra也支持数据批量插入和修改。这个过程引入了同样的危险,当记录已经从集群剩下的节点删除了,但是重新插入的时候。Cassandra不会重播还在宽限期内的墓碑的改变。

在单节点集群中,你可以把gc_grace_seconds设置成0

为了防止僵尸记录的重现,在节点恢复一直运行nodetool repair命令,在每个表的每个gc_grace_seconds时间内

如果一个表里的所有记录在创建的时候都有一个TTL,所有的都允许过期而不用手动删除,就没有必要为那张表运行nodetool repair

如果你使用SizeTieredCompactionStrategyor DateTieredCompactionStrategy,你可以手动启动compaction进程来立即删除墓碑

注意: 如果你强制compactionCassandra可能会从所有的数据创建一个非常大的SSTableCassandra在很长一段时间内不会触发另外一个compaction。在强制compaction期间创建的SSTable的数据会在非compaction的很长一段时间内保持很陈旧。

• Cassandra允许你为整张表设置一个default_time_to_live(默认过期时间)属性。列和行就像上面描述的那样被标记成常规的TTL,但是如果一条记录超过了表的TTLCassandra会直接删除它,而不会标记成墓碑或者compaction

• Cassandra支持通过DROP KEYSPACE DROP TABLE声明来立即删除。

 

索引是如何保存和更新的?

A brief description ofhow Cassandra stores and distributes indexes.

二级索引是用来过滤存储在一个表中的非主键的列的数据。例如,一张存储有骑行者的名字和年龄的表,使用骑行者的姓氏作为主键,可能有一个年龄的二级索引来允许通过年龄查询。查询匹配非主键列是一个反模式,因为查询总是会导致从表中检索的数据的连续切片.


如果表的行是基于姓氏来存储的,这张表可能会分散到很多不同节点上多个分区来存储。基于姓氏1的特定范围的查询,比如所有姓氏为Matthews的所有骑行者,会检索到一个有序的行,但是基于年龄的查询,比如所有28岁的骑行者,会检索所有节点来获取所需要的值。非主键在存储数据中不起作用,因此查询非主键列的特定值会导致扫描所有分区。扫描所有分区通常会导致过高的读取延迟,并且不允许。

 

第二索引可以为一张表的列创建。这些索引存储在每个节点的本地一个隐藏的表里面,在后台进程创建的。如果一个二级索引用来查询数据,而没有限定一个特定的分区键,该查询会有很高的读延迟,因为所有节点都会被查询到。只有查询选项里ALLOW FILTERING有使用到的时候,才允许这样查询。该选项(ALLOW FILTERING)在生产环境中不适合。如果一个查询既包含分区键,也包含二级索引列,这个查询就会很成功因为可以直接定位到单节点的分区。

 

然而,这种技术并不保证无故障索引,因此要知道何时使用和合适不适用索引。在上面例子中,可以用年龄创建索引,但是更好的解决方案是创建物化视图,或者根据有序的年龄创建一个冗余表。

 

与关系型数据库一样,需要使用处理时间和资源保持索引是最新的,因此应该避免不必要的索引。当一个列更新了,索引也要更新。如果在MemTable中仍然存在旧的列的值,这通常发生在一个小的行集更新多次,Cassandra删除去相应的过时的索引项;否则,旧的条目会被compaction清除。如果一个读在compaction清除它之前见到旧的索引,读进程会使他无效。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值