写在最后
还有一份JAVA核心知识点整理(PDF):JVM,JAVA集合,JAVA多线程并发,JAVA基础,Spring原理,微服务,Netty与RPC,网络,日志,Zookeeper,Kafka,RabbitMQ,Hbase,MongoDB,Cassandra,设计模式,负载均衡,数据库,一致性哈希,JAVA算法,数据结构,加密算法,分布式缓存,Hadoop,Spark,Storm,YARN,机器学习,云计算…
如果表中只有一列具有集合类型,您则可以将其建模为其他集群列。 例如:
CREATETABLE test.m1 (
id int PRIMARY KEY,
m map<int, text>
);
可以在没有映射列的情况下创建此表(对集合和列表使用相同的方法):
CREATETABLE test.m1 (
id int,
m_key int,
m_value text,
PRIMARY KEY(id, m_key)
);
您可以通过省略m_key的条件来为特定分区选择所有值,或者通过提供完整的主键来仅仅选择特定元素。相比于会被整体返回的集合类型的列,这是一个更大的优势。
4.5 检查清单类型的使用
上节中描述的所有内容也适用于列表类型。但是,列表类型还有其他限制:
-
按位置设置和删除元素,以及删除特定值的出现,会导致内部先读后写 (read-before-write)。
-
前置或追加操作不是幂等的(idempotent)。
万一失败,您不能简单地重试该操作,因为不知道该操作是否完成。重试会导致重复的元素;不重试则可能会丢失数据。有关更多信息,请参见列表字段(List fields)文档。
如果您不需要按特定顺序排列元素,也不必让元素具有重复的值,请使用集合类型而不是列表类型。 如果您仍然需要使用列表类型的列,请考虑使用其冻结版本。
4.6 检查用户定义类型的使用
Cassandra允许创建用户定义类型(UDT)。您可以使用UDT类型将相关信息分组在一起,把每个组合作为单个实体来使用。从数据模型分析的角度来看,您可以应用与集合相同的规则:
-
尽可能使用冻结的UDT。
-
对于非冻结的UDT,请不要指定太多字段。
但是,UDT仍然存在与UDT的序列化/反序列化有关的问题。从模式(schema)演变的角度来看,另一个问题是:虽然可以向UDT添加字段,但却无法删除它们。这意味着UDT仅应在非常必要的有限情况下使用。否则,最好将此数据定义为表的常规列。另一种选择是在应用程序内部执行UDT数据的序列化和反序列化,并将数据存储为Blob。
4.7 检查深度嵌套的UDT和UDT集合的使用
尽管UDT可以嵌套在其他UDT中或作为集合中的元素,您必须非常小心。如果集合中存在太多元素或嵌套的UDT太多,则将达到最大的写入值上限,导致操作失败。
4.8 检查元组类型的使用
CQL提供了元组数据类型,可以将不同数据类型的多个元素分组为一个实体。此类型的局限性是:
-
它的值总是冻结的,这意味着每次更新都会重写该列。
-
您必须按位置访问元素,这使得开发代码更加困难,因为您需要记住在哪个位置使用了哪种类型以及该位置的含义。
由于这些限制,DataStax建议不要使用此数据类型,而应使用UDT。
4.9 检查计数器数据类型的使用
计数器数据类型允许您执行递增和递减操作,这对某些应用程序很有用。从Cassandra 2.1开始,计数器的执行更加鲁棒,但仍存在局限性:
-
当节点发生故障,写入丢失、或类似情况时,计数器的值可能并不精确,因为计数器操作不是幂等的,并且无法重试:重试可能会导致计数过多;不重试,则可能计数不足。
-
表可能只包含针对计数器类型的常规列;不可能与其他数据类型混合使用。
4.10 检查Blob数据类型的使用
Cassandra通过提供blob类型来支持将二进制数据存储在数据库中。使用blob时,请确保您没有在Cassandra中存储大于几百KB的对象,否则从数据库中获取数据时可能会发生问题。例如,当获取的页面大小大于原生协议设置的限制(256MB)时,查询可能会失败。
4.11 定义集群列的排序顺序
定义表时,可以定义集群列的排序方向。执行查询时应用程序可以颠倒定义的排序方向,但效率不如相同排序(在表级别上定义的)方向上读取数据。DataStax建议在建表时定义正确的排序方向。同样,如果查询时排序颠倒了,它会影响到所有列,而不仅是一列,导致Cassandra沿着反方向读取数据。
5 每个分区的单元数
Cassandra文档经常使用术语“单元‘(cell)来描述常规列(非主键列)的存储值。除了实际值之外,每个单元还具有关联的元数据,例如时间戳,可选的TTL和复杂单元的其他数据。集合和用户定义的类型会更加复杂。
Cassandra每个分区的硬限制为20亿个单元。为了确保读取操作具有可预测性,DataStax建议限制分区中的单元数,以使分区小于100 MB。
您可以使用nodetool tablehistograms命令(旧版Cassandra中的cfhistograms)检查每个分区的单元数。较新版本的Cassandra和DSE可以输出系统中所有表的数据,而较旧版本则需要给出具体的keyspace和表名。
查看输出的“单元计数”列,并检查99%百分位和“最大”行中的值。如果您的值大于100,000,请考虑更改数据模型;这可能表明存在大的分区(在下一节中介绍),列过多,或在非冻结集合中的元素过多。
$ nodetool tablehistograms test widerows
test/widerows histograms
Percentile SSTables Write Latency Read Latency Partition Size Cell Count
(micros) (micros) (bytes)
50% 1.00 0.00 1310.72 545791 103
75% 1.00 0.00 1310.72 545791 124
95% 1.00 0.00 1310.72 545791 124
98% 1.00 0.00 1310.72 545791 124
99% 1.00 0.00 1310.72 545791 124
Min 1.00 0.00 1310.72 454827 87
Max 1.00 0.00 1572.86 545791 124
6 大分区
对于Cassandra,我们建议将分区大小保持在100MB以下。大分区的存在现象表明数据模型有错误,通常是由以下因素触发的:
-
分区键的基数低。 ——分区键的可能值太少。
-
分区之间的数据分布不均匀。
例如,如果用客户ID作分区键,则大客户的应用程序将比小客户写入更多的数据。结果导致,某些节点可能比其他节点拥有更多的数据。更多的数据会增加这些节点的负载,因为它们要处理更多的请求,需要更多的压实操作等。
-
表中的列和行太多,尤其是当每一行包含所有或大多数列的数据时。
-
在表中存储大blobs或长文本(long texts)。
-
大分区会对Cassandra造成额外的负担,例如分配额外的内存来保存分区索引。注意:在Cassandra 3.6版之前,读取大分区会对Java heap施加更大的压力,并经常导致节点崩溃。
-
当某些节点处理请求比其他节点多时,节点之间的数据分配不均会导致热点。
-
在读取整个分区时,大分区之间需要传输更多的数据。
-
Cassandra分区的大小会影响外部系统,例如Spark,因为Cassandra的分区是映射到Spark分区的最小对象。任何Cassandra中的不平衡都可能导致Spark处理数据时的不平衡。
6.1 查找有关分区的信息
使用以下工具查找分区的大小:
-
使用nodetool tablehistograms命令(旧版Cassandra中的cfhistograms)查找75、95、99和100%百分位数的分区大小。如果看到这些值之间有很大差异,则可能是分区键值的分布不均匀。用sstablepartitions命令也可以获得类似信息。
-
有关最大分区大小的信息可通过nodetool tablestats(旧版Cassandra中的cfstats)获得。检查“Compacted partition maximum bytes”行中的值是否大于建议的100 MB。
-
如果分区键值的分布不均匀,则可使用DataStax Bulk loader(dsbulk)来识别,找到拥有最大行数的分区键值。dsbulk的主要优点是,它可以与整个集群一起使用。例如,要在测试表中查找最大的分区:
$ dsbulk count -k test -t widerows --log.verbosity 0 --stats.mode partitions
'29' 106 51.71
'96' 99 48.29
- 您可以用sstablemetadata功能与-s命令行参数一起使用,来找到特定SSTables中最大的分区。-s标志在Cassandra 4.0和DSE 6.x中可用。对于Cassandra 3.x,请使用sstable-tools项目(这是sstablemetadata功能的灵感)。sstablemetadata的一个优点是,它提供有关最大分区的信息,包括行数和字节大小。缺点是它与单个SSTable文件一起使用,而一个分区可能被分割在几个不同的SSTable文件。 例如:
$ sstablemetadata -u -s path_to_file/mc-1-big-Data.db
SSTable: /Users/ott/var/dse-5.1/data/cassandra/data/datastax/vehicle-8311d7d14eb211e8b416e79c15bfa204/mc-1-big
Size: 58378400
Partitions: 800
Tombstones: 0
Cells: 2982161
WidestPartitions:
[266:20180425] 534
[202:20180425] 534
[232:20180425] 534
[187:20180425] 534
[228:20180425] 534
LargestPartitions:
[266:20180425] 134568 (131.4 kB)
[202:20180425] 134568 (131.4 kB)
[232:20180425] 134568 (131.4 kB)
[187:20180425] 134568 (131.4 kB)
[228:20180425] 134568 (131.4 kB)
...
通过查看tablestats / cfstats输出中分区的行数(估计)或通过执行SELECT DISTINCT partition_key_list,count(*)FROM table并检查输出的列数来检查分区键值的低基数。
对本节中描述的问题,唯一的解决方案是更改数据模型以选择正确的分区键和聚类键。在某些情况下,您也许可以把聚类键提升为分区键,或引入人工分组列(artificial bucketing column)作为分区键。
7 检查压实策略
一般情况下,优先使用默认的压实策略(SizeTieredCompactionStrategy,STCS),除非它会导致问题,或其他策略存在明显的优势。有关调整压实策略的更多信息,请参见单独的文档。
8 检查辅助索引的使用
通过利用非分区键列的数列,Cassandra和DSE提供了多种方法执行表的搜索,包括:
-
二级索引
-
物化视图
-
SASI 索引
-
DSE Search 索引 - 注意:DSE 6.8包括Beta版的存储附加索引(SAI)。
通过使用这些技术,用户可以不必将数据反范式化为其他表。但是,以上每个实现方法都有其自身的限制。
8.1 检查二级索引的使用
Cassandra中的原生二级索引是反向索引,它在内部建表,将每个列的特定值映射到一个完整的主键上,以此来索引每个节点上的数据。由于基表中定义的主键结构不允许数据访问,这个索引方法的目的是为绕过这个障碍来支持数据访问。
二级索引有一些限制:
-
每个索引只能索引单个常规列。
-
不支持非相等(non-equality)或范围条件。
-
可能会受到索引列基数的严重影响。如果低基数存在,则可能导致创建很宽的分区。如果是高基数,您可能会创建许多非常小的分区。两者都会对性能造成不利影响。
-
不能很好地处理删除。二级索引中的大量墓碑会严重降低其性能。
-
由于二级索引是在本地将数据索引到每个节点上的基表里,它们不能通过分区键得到正常放置。由于缺乏分区键的限制,它会导致查询时要向数据中心中所有节点发出分散收集的请求,从而导致性能欠佳。
由于这些原因,使用二级索引时必须非常谨慎,并在可能的情况下通过反范式化来避免使用二级索引。较小分区中的单个分区里,一些表是很少发生删除的,基本分区位于本节点上,该索引可以在以后被重复使用到 —— 如果满足所有这些条件,那么在筛选结果时,二级索引可能是一个合理的选择。即使在这些条件下,我们也强烈建议使用具有代表性的数据和负载,彻底测试使用二级索引的查询。
由于Cassandra需要消耗资源来构建和维护二级索引,才能使其保持一致的状态,因此DataStax建议,保持相对较低数量的二级索引,并删除所有未使用的二级索引。 您可以使用以下方法检查已定义的二级索引的数量:
$ grep 'CREATE INDEX' schema.cql|wc -l
8.2 检查物化视图(materialized views)的使用
Cassandra 3.0和DSE 5.0引入了对物化视图的支持,以使客户端应用程序更容易自动透明地对数据进行反范式化。物化视图是在模式(schema)级别上定义的指定基表的视图。这些视图包含基表(或其子集)的相同信息,但具有不同的主键,因此原始键结构下无法实现的读取模式可以通过视图变为可能。
将数据写入基表时,其所有物化视图都会相应地自动更新,以便在任何时候可以像常规表一样根据其键来读取它们。请注意,数据实际上存储在每个视图中,因此总占用量会根据视图的数量及其包含的信息而增加。
在表上使用物化视图时,请考虑以下因素:
-
物化视图的主键结构上的约束:
-
物化视图的键必须包含构成基表键的所有列。这是因为行的唯一性定义必须是相同的。
-
物化视图的键最多可以包含基表中的一个常规列,条件是该列永远不能为null。
-
-
在表上使用物化视图将对数据库造成额外的负担,因此请相应地计划资源。
-
为了在物化视图中构建行,Cassandra需要从基表中读取相应的行,这给IO系统增加了额外的负担并增加了延迟。
-
如果物化视图具有不同的分区键,则数据的插入需要与负责相应令牌范围的其他节点进行网络通信。
-
在某些情况下,物化视图可能与基表不同步。如果发生这种情况,请使用nodetool rebuild_view进行重建(常规修复不适用于物化视图)。
-
在现有数据的表上创建物化视图时,可能需要一些时间,具体取决于现有数据量的大小。可以使用nodetool viewbuildstatus命令检查已构建操作的状态。
-
在Cassandra中,物化视图仍标记为实验性质,不建议用于生产环境。
尽管从开发的角度来看,物化视图很方便,但是您可以通过创建一个或多个辅助表并写入所有辅助表来获得更好的性能。尽管它增加了应用程序代码的复杂性,但它也有其好处,例如在定义辅助表的主键时具有更大的灵活性,并且避免在将条目写入物化视图之前从磁盘读取数据。
如果仍然需要使用物化视图,请将其数量保持在较低水平。使用以下命令检查物化视图的数量:
$ grep 'CREATE MATERIALIZED VIEW' schema.cql|wc -l
8.3 检查SASI索引的使用
SASI(附有SSTable的二级索引)是二级索引的另一种实现方式,旨在使查询条件具有更大的灵活性并提高性能。SASI是由一位外部贡献者为Apache Cassandra贡献的,其最初的实现是针对一个非常特定的用例开发的,使用的是旧版本的Cassandra和已弃用的API。
此外,它只在非常有限的程度上得到了测试。进一步的测试和初步实验表明,SASI索引受众多错误(bugs)的影响。尽管进行了修复和改进,它仍然不可靠而且可能返回不一致的结果,因此我们认为它还不能用于生产环境。
DataStax建议避免对生产系统上的任何查询使用SASI索引。
您可以使用以下命令检查SASI索引的使用情况:
$ grep -e 'CREATE CUSTOM INDEX.*SASIIndex' schema.cql|wc -l
8.4 检查DSE Search索引的使用
DSE具备基于Apache Solr的自己的搜索索引实现,称为DSE Search。 此实现与核心Cassandra透明集成,并允许对存储的数据进行索引。它可以对表的任意列或列组合执行不同类型的搜索,例如全文搜索,范围搜索,精确搜索等。
尽管它非常灵活,但是需要考虑以下几点:
注意:Apache Lucene和Solr以及DSE Search有一些限制。DSE 6.8中的存储附加索引(SAI)改进了许多这些限制。
-
要在DSE Search索引中构建对象,DSE需要从基表中读取相应的行,这会增加IO。
-
带有DSE Search索引的表数
建议的最大索引数取决于DSE和硬件的版本。请参阅DSE Search的容量规划。
- 虚拟节点的使用和数量。
由于DSE Search针对所有令牌范围执行分散收集查询,因此发送的查询数量与令牌范围的数量成正比。对于DSE Search,请使用单令牌体系结构或将vnode的数量保持为8个或更少。
- 搜索索引的大小。
建议将单个索引端保持在250 GB的限制之下,所有搜索索引的大小不超过500 GB。
最后
针对最近很多人都在面试,我这边也整理了相当多的面试专题资料,也有其他大厂的面经。希望可以帮助到大家。
下面的面试题答案都整理成文档笔记。也还整理了一些面试资料&最新2021收集的一些大厂的面试真题(都整理成文档,小部分截图)
最新整理电子书
的查询数量与令牌范围的数量成正比。对于DSE Search,请使用单令牌体系结构或将vnode的数量保持为8个或更少。
- 搜索索引的大小。
建议将单个索引端保持在250 GB的限制之下,所有搜索索引的大小不超过500 GB。
最后
针对最近很多人都在面试,我这边也整理了相当多的面试专题资料,也有其他大厂的面经。希望可以帮助到大家。
下面的面试题答案都整理成文档笔记。也还整理了一些面试资料&最新2021收集的一些大厂的面试真题(都整理成文档,小部分截图)
[外链图片转存中…(img-sWCc0JJm-1715552479196)]
最新整理电子书
[外链图片转存中…(img-0YQuLptE-1715552479196)]