Apache Cassandra Explore

1: Cassandra 介绍

它是一个高度可伸缩、分布式、开源的 NoSQL 数据库。

Cassandra 是一个分布式数据库系统,最初由 Facebook 开发并开源,现在由 Apache 维护。它旨在解决大规模数据存储和高可用性的挑战。Cassandra 已被广泛应用于诸如社交媒体、物联网、日志分析等需要高吞吐量和可伸缩性的领域。

2: Cassandra 架构

Cassandra 作为分布式数据库系统,专门设计用于解决大规模数据存储和高可用性的挑战。它采用了一系列的设计和架构策略来实现这些目标:

  1. 分布式架构:

    Cassandra 结合了 Amazon Dynamo 的分布式架构,由多个节点组成,这些节点可以位于不同的物理位置或数据中心,因此数据可以在全球范围内分布。这种分布式架构允许 Cassandra 轻松地扩展以处理大规模的数据,因为数据可以水平分片,即分布到多个节点中,而不是集中存储在一个地方。使得 Cassandra 具有高可用性,即使其中的某些节点失效,整个系统仍然可以继续运行。此外,Cassandra 的去中心化设计意味着没有单点故障,这对系统的可靠性至关重要。

    consistent-hashing

  2. 去中心化设计:

    Cassandra 采用去中心化的设计,没有单点故障。这意味着没有一个节点是整个系统的中心,因此即使某些节点出现故障,整个系统仍然可以继续工作。这有助于提高系统的可用性和容错性。

  3. 多数据中心支持:

    Cassandra 支持多数据中心部署,允许数据在不同地理位置之间复制和分布。这提供了地理冗余和容灾恢复的功能,确保即使某个数据中心发生故障,其他数据中心仍可以提供服务。

  4. 自动数据复制:

    Cassandra 自动复制数据以提高可用性。每个数据都可以在多个节点上进行复制,以防止数据丢失。当一个节点发生故障时,可以从其副本中恢复数据。管理员可以配置副本数量和复制策略以满足性能和一致性需求。

  5. 可伸缩性:

    Cassandra 具有良好的可伸缩性,可以轻松地扩展以满足不断增长的数据和负载。当需要更多容量时,只需添加新节点,Cassandra 会自动平衡数据。

  6. 高性能读写操作:

    Cassandra 支持高性能的读写操作。它使用一种称为 LSM 树(Log-Structured Merge-tree)的数据结构,可提供快速的写入操作。此外,Cassandra 的数据模型和查询语言(CQL)经过优化,支持复杂的查询,从而实现高性能读取操作。

总的来说,Cassandra 通过分布式架构、去中心化设计、多数据中心支持、自动数据复制以及高可伸缩性和高性能的特性,解决了大规模数据存储和高可用性的需求。这使得它成为许多大型应用和企业在处理大数据和高负载环境中的首选数据库系统。

2.1: 多数据中心支持和自动数据复制

Cassandra 实现多数据中心支持和自动数据复制的关键机制包括数据中心配置、复制策略以及基于 Gossip 协议(一种 P2P 协议,每秒与附近最多三个节点交换位置和状态信息)的通信。以下是关于 Cassandra 多数据中心支持和自动数据复制的详细内容:

  1. 数据中心配置:

    Cassandra 允许在不同的物理位置或数据中心部署节点。这意味着您可以将 Cassandra 集群分布在全球的多个数据中心中,以提供地理冗余和容灾恢复。 每个数据中心通常都会有其独立的配置,包括复制策略、网络设置和其他参数。这使得您可以根据各个数据中心的需求进行自定义配置。

  2. 复制策略:

    在 Cassandra 中,复制策略定义了数据如何在多个数据中心之间进行复制和分发。Cassandra 支持多种复制策略,包括 SimpleStrategyNetworkTopologyStrategy 。 SimpleStrategy 通常用于单数据中心部署,而 NetworkTopologyStrategy 适用于多数据中心环境。NetworkTopologyStrategy 允许您为每个数据中心配置不同数量的副本。

  3. 数据中心感知性:

    Cassandra 具有数据中心感知性,可以识别每个节点所属的数据中心。这对于在多数据中心环境中决定数据复制和查询路由非常重要。 当客户端执行读写操作时,Cassandra 会根据数据中心感知性来确定应从哪个数据中心读取或写入数据。

  4. 自动数据复制:

    Cassandra 使用自动数据复制来确保数据的冗余存储。每个写入的数据会根据复制策略自动复制到其他节点,以防止数据丢失。 数据复制是基于 Gossip 协议的,每个节点都会与其他节点通信以协调数据复制。这种协作机制确保了数据的一致性和可用性。

  5. 一致性级别:

    Cassandra 允许您根据需要配置一致性级别。一致性级别定义了读取和写入操作的一致性要求。您可以选择“一致性”、“全部”或“任何”等级别。 一致性级别允许您在不同数据中心之间权衡一致性和性能需求。较高一致性级别要求更多的节点确认操作,但会增加延迟,而较低一致性级别则提高了性能。

通过以上机制,Cassandra实现了多数据中心支持和自动数据复制。这使得Cassandra成为一个强大的分布式数据库系统,适用于需要高可用性和地理冗余的大规模应用和数据存储环境。

2.2: 分区器

Cassandra 中的分区器(Partitioner)是一个关键组件,它负责决定如何将数据分布到不同的节点。分区器定义了数据如何根据分区键的值进行分区和定位。Cassandra 支持多种不同类型的分区器,每种分区器都有其独特的数据分布和定位策略。

以下是Cassandra中常见的分区器以及它们的特点:

  • Murmur3Partitioner: Murmur3Partitioner 是 Cassandra 的默认分区器。 它使用 MurmurHash3 算法来生成哈希码,将数据均匀分布到不同的节点上。 Murmur3Partitioner 在数据分布均匀性和性能方面表现良好,适用于大多数用例。 Murmur3Partitioner 性能上是 RandomPartitioner 的 3-5 倍。

  • RandomPartitioner: RandomPartitioner 在早期版本的 Cassandra 中使用,采用 MD5 加密哈希值但已经不推荐使用。 它使用随机哈希函数,将数据随机分布到不同节点。 由于数据随机性,不适合范围查询,并且可能导致数据不均匀分布。

  • ByteOrderedPartitioner: ByteOrderedPartitioner 使用字节排序来分区数据。 它适用于特定的用例,如时间序列数据,可以支持范围查询。 但在大规模数据和高写入负载下,可能会导致性能问题。

  • 自定义分区器(Custom Partitioner): Cassandra 还允许用户创建自定义分区器,以满足特定需求。 自定义分区器可以根据应用程序的分区策略和数据模型来定义分布和定位策略。

分区器的选择取决于应用程序的需求和数据模型。通常情况下,Murmur3Partitioner 是一个不错的选择,因为它提供了良好的数据分布和性能。然而,在某些特定情况下,可以考虑其他分区器,如 ByteOrderedPartitioner,以满足特定的查询需求。

分区器是 Cassandra 中数据分布的核心,它确保数据均匀分布在集群中,从而实现高可用性和性能。在设计数据模型时,需要考虑分区器的选择,以支持查询和数据访问的需求。

3: 数据模型

Cassandra 的数据模型是一种分布式、高度可伸缩的键值存储模型,结合了 Google 的 BigTable 数据模型。它具有一些独特的特性和概念,让我们来详细介绍:

1. 键值存储模型

Cassandra 采用了键值存储模型,其中数据通过键(Key)进行检索。每个数据项都与一个唯一 的主键关联。这个主键在Cassandra中非常重要,因为它决定了数据如何分布在集群中、如何排序以及如何进行查询。

2. 列族(Column Family)

数据在 Cassandra 中被组织成列族,类似于关系型数据库中的表。每个列族包含多个行(Row),每行由一组列(Column)组成。列族是逻辑数据组织的单位。

在Cassandra中,“列族”(Column Family)和"表"(Table)是相关但略有不同的概念,它们与数据组织和存储有关。以下是它们之间的区别以及如何进行修改:

  1. 列族(Column Family):

    列族是 Cassandra 中数据组织的基本单元。它类似于关系数据库中的表(Table),但有一些重要的区别。 列族包含多个行(Row),每一行都由一组列(Column)组成。这些列可以视为键值对(Key-Value Pair),其中键是列名,值是实际的数据。 列族通常具有相似的数据结构和用途。例如,您可以创建一个用户信息列族,用于存储用户的姓名、电子邮件地址等信息。

  2. 表(Table):

    表是 Cassandra 中数据模型的高级概念。表可以包含一个或多个列族。表定义了数据的结构、主键以及如何在列族之间组织数据。 表的主要作用是为列族提供命名空间,并定义主键。主键由分区键(Partition Key)和排序键(Clustering Key)组成,它决定了数据如何分布在集群中和如何排序。 表是创建和修改列族的上层概念,它决定了表中应该包含哪些列族,以及每个列族的配置。

列族和表之间的关系是,列族是表的组成部分,表则是逻辑上将多个列族组合在一起的概念。要修改列族或表,您可以创建新的列族或表,进行数据迁移,并在必要时删除旧的列族或表。

示例:

在 Cassandra 中,创建一个包含多列族的表非常简单。以下是一个示例 CQL 语句,用于创建一个表,该表包含两个列族:personal_infocontact_info。每个列族包含不同的列,模拟存储用户的个人信息和联系信息。

CREATE TABLE user_profile (
    user_id UUID PRIMARY KEY,
    personal_info frozen<map<text, text>>,
    contact_info frozen<map<text, text>>
);

让我们详细解释这个示例:

CREATE TABLE user_profile: 这部分定义了表的名称,这里是 user_profile。

(user_id UUID PRIMARY KEY): 这部分定义了主键,其中包括一个分区键(user_id)作为主键。user_id是 UUID 类型的主键,用于唯一标识每个用户。

personal_info frozen<map<text, text>>: 这一行定义了名为 personal_info 的列族,它的数据类型是 map<text, text>。map 数据类型表示一个键值对映射,这里用于存储用户的个人信息,如姓名、性别等。frozen 关键字表示这是一个不可变的数据结构。

contact_info frozen<map<text, text>>: 这一行定义了名为 contact_info 的列族,它也是map<text, text> 类型,用于存储用户的联系信息,如电子邮件地址、电话号码等。

这个示例表 user_profile 包含两个列族,每个列族都包含一个 map 类型的列,以存储不同类型的用户信息。主键为 user_id,用于唯一标识每个用户。您可以在查询和更新数据时使用主键来识别特定用户的数据。

UDT 示例:

当在 Cassandra 表中包含可变列时,通常使用用户定义类型(User Defined Type,UDT)。UDT 允许您定义包含多个字段的数据结构,其中某些字段可以是可变的。以下是如何在上述示例中添加一个可变列的方式:

CREATE TABLE user_profile (
    user_id UUID PRIMARY KEY,
    personal_info frozen<map<text, text>>,
    contact_info frozen<map<text, text>>,
    custom_data frozen<map<text, text>>
);

在这个示例中,我们添加了一个名为 custom_data 的列族,它使用了 frozen<map<text, text» 数据类型。这允许您在 custom_data 列族中存储键值对映射,其中键和值都是文本类型。由于我们使用了 frozen 关键字,这意味着 custom_data 列族包含的数据是不可变的。

如果您希望 custom_data 列族包含可变数据,您可以使用自定义用户定义类型(UDT)来定义数据结构,其中一些字段可以是可变的。例如,如果您希望 custom_data 列族中的某些字段具有可变性,您可以创建一个 UDT,如下所示:


CREATE TYPE custom_field (
    field_name text,
    field_value text
);

CREATE TABLE user_profile (
    user_id UUID PRIMARY KEY,
    personal_info frozen<map<text, text>>,
    contact_info frozen<map<text, text>>,
    custom_data frozen<list<custom_field>>
);

在这个示例中,我们定义了一个 UDT custom_field,它包括两个字段:field_name 和 field_value,两者都是文本类型。然后,我们在表 user_profile 中使用 frozen<list<custom_field» 数据类型,以存储一系列 custom_field 对象,其中 custom_field 对象的字段可以包含可变数据。

通过这种方式,您可以在 Cassandra 表中包含包含可变列的列族,同时使用 UDT 来定义数据结构,以适应不同字段的可变性。这种灵活性允许您存储各种类型的数据,包括具有不同结构和可变性的数据。

3. 超列族

在Apache Cassandra中,“超列族”(Super Column Family)是一种数据模型,用于存储具有层次结构的数据。与普通的列族(Column Family)不同,超列族允许在一个列族中存储多个层次的数据,其中每个层次都包含多个子列。以下是对超列族的详细解释,以及如何使用它:

  1. 超列族的定义:

    超列族是一种列族的子类,它允许在列族中创建多个层次(super column),每个层次下包含多个子列(sub column)。超列族的结构类似于嵌套的字典,其中主要有两个层次:超列和子列。

  2. 超列(Super Column):

    超列是超列族的顶层单位,它类似于一个子列族(column family)的名称。每个超列可以包含一组子列。超列通常用于将数据按照某种方式进行分类或组织,例如,将用户信息按照不同的用户类型分类。

  3. 子列(Sub Column):

    子列是超列的下层单位,每个子列都由一个键-值对(key-value pair)组成。子列包含实际的数据。例如,对于超列"用户信息",子列可以包含用户的姓名、年龄、电子邮件地址等信息。

  4. 创建超列族:

    要创建超列族,您需要在CQL中定义列族的结构,并明确指定哪些列族是超列族。以下是一个示例:

    
    CREATE TABLE user_profiles (
        user_id UUID PRIMARY KEY,
        user_info map<text, frozen<map<text, text>>>
    );
    

    在这个示例中,我们创建了一个名为 “user_profiles” 的表,其中包含一个名为 “user_info” 的超列族。user_info 超列包含一个 map<text, text>,其中键是超列名称,值是包含子列的 map<text, text>。这种结构允许在 user_info 超列中创建多个超列,每个超列可以包含不同的用户信息数据。

  5. 插入数据:

    插入数据到超列族时,您需要指定超列名称、子列名称和对应的值。以下是一个插入数据的示例:

    
    INSERT INTO user_profiles (user_id, user_info['basic_info']['name']) VALUES (<user_id>, 'John Doe');
    INSERT INTO user_profiles (user_id, user_info['contact_info']['email']) VALUES (<user_id>, '[email protected]');
    

    在这个示例中,我们插入了用户"John Doe"的姓名和电子邮件地址,使用超列"basic_info"和"contact_info"来分类这些信息。

超列族适用于需要将数据按照层次结构组织的场景,但需要注意,超列族在较新版本的 Cassandra 中已被建议不再使用,而建议使用集合(collections)数据类型(如 map 和 list)来替代超列族,以提供更好的性能和可维护性。因此,在新的 Cassandra 应用中,更常见的方法是使用嵌套的集合数据类型来代替超列族。

4. 主键

在 Cassandra中,主键(Primary Key)是一个关键概念,用于确定数据如何分布和访问。主键由分区键(Partition Key)和排序键(Clustering Key)组成,主键是唯一的,它决定了数据项的唯一性。

  • 主键的组成部分:

    • 分区键(Partition Key):

      分区键是主键的第一部分,它用于将数据分布到Cassandra集群中的不同节点。分区键的选择对于数据分布和负载均衡至关重要。相同分区键的数据将存储在同一节点上。

    • 排序键(Clustering Key):

      在每个分区内,数据按照排序键进行排序。这意味着数据在分区内以有序的方式存储,便于范围查询和按排序顺序检索数据。排序键也决定了数据在 SSTable(不可变的存储文件)中的物理存储顺序。

  • 主键的作用:

    • 数据分布:

      分区键的选择影响数据在集群中的分布。合理选择分区键可以确保数据均匀分布在不同的节点上,避免热点数据和不均匀的负载。

    • 数据访问:

      主键允许您快速定位和访问特定数据。您可以使用主键来执行读取和写入操作,以访问特定分区中的数据。

以下是一个示例,展示如何创建一个包含分区键和排序键的 employee 表:


CREATE TABLE employee (
    department_id UUID,
    employee_id UUID,
    employee_name TEXT,
    employee_age INT,
    PRIMARY KEY (department_id, employee_id)
);

在这个示例中,我们创建了一个名为"employee"的表,它包含两部分主键:

分区键: department_id 是分区键,它用于确定数据在不同分区之间的分布。每个不同的department_id值将被散列到不同的节点上。

排序键: employee_id 是排序键,它用于在同一分区内对数据进行排序。排序键决定了数据在同一分区内的存储顺序。

这个表的主键定义指定了两个部分:department_id 和 employee_id。这意味着数据将首先根据 department_id 进行分区,然后在同一分区内,数据将根据 employee_id 进行排序。

这个数据模型适用于存储员工信息,其中员工属于不同的部门,每个部门都有唯一的 department_id,而每个员工也具有唯一的 employee_id。这使得查询特定部门中的员工或按照员工 ID 进行范围查询变得非常高效。

实际应用中可以根据需要添加更多的列来存储员工的其他信息。主键的选择应根据应用程序的需求和查询模式进行优化,以确保数据的高效分布和访问。

5. 数据分布

Cassandra 使用主键的分区键来将数据均匀分布到不同的节点中。这有助于实现数据的分布式存储和负载均衡。当您查询数据时,Cassandra 会根据主键的分区键来确定在哪个节点上查找数据。

Cassandra 中的数据均匀分布是通过分区键和散列函数(Hash Function)相结合来实现的。下面是详细说明如何确保数据均匀分布在各个节点上的过程:

  • 选择分区键:

    在 Cassandra 中,您需要选择一个适当的分区键作为表的一部分。分区键的选择对于数据的均匀分布非常重要。分区键通常是表中的一个列,它的值将用于确定数据应该存储在哪个分区。

  • 分区键的值:

    每个分区键值经过散列函数处理,这通常是一个散列算法,例如 Murmur3 或 SHA-1。散列函数将分区键值转换为一个散列码或哈希值。这个哈希值是一个数字,通常是一个很大的整数。

  • 分布到节点:

    通过哈希值,Cassandra 确定了数据应该存储在哪个节点。Cassandra 集群中的每个节点被分配了一定的哈希范围。数据的哈希值将与节点的哈希范围进行比较,从而确定数据应该存储在哪个节点上。

  • 数据均匀分布:

    由于哈希函数的性质,假设哈希函数是均匀分布的,那么不同分区键值的哈希值应该在哈希范围内均匀分布。这确保了数据均匀分布在不同的节点上。

  • 负载均衡:

    Cassandra 还包括一种自动负载均衡机制,确保数据均匀分布并避免节点过载。当新的节点加入集群或节点失效时,数据会自动重新分布,以确保负载均衡。

6. 灵活的模式设计

Cassandra 允许根据应用程序的需求创建不同的列族和数据模式。您可以根据数据的特性来设计不同的列族和表。这种灵活性允许 Cassandra 适应各种不同类型的数据。

7. CQL(Cassandra查询语言)

为了简化数据的操作和查询,Cassandra 引入了 CQL,这是一种与 SQL 语法类似的查询语言,但专门针对 Cassandra 的分布式、键值存储数据模型进行了优化。CQL 允许用户执行各种查询操作,包括范围查询、过滤和聚合。

8. 数据复制

Cassandra 自动将数据进行复制,以提高数据的冗余存储和高可用性。您可以配置数据复制策略来确定数据的副本数量和复制到的节点。

9. 一致性级别

Cassandra允许您配置读取和写入操作的一致性级别。您可以根据应用程序的需求选择不同的一致性级别,以权衡性能和数据一致性。

Cassandra的一致性级别包括以下几种:

  • ONE (一):

    在读取或写入操作完成后,只需要一个副本节点确认操作成功。这是最低的一致性级别,通常用于需要最低延迟的读取操作。

  • TWO (两):

    在读取或写入操作完成后,需要两个副本节点确认操作成功。这提供了更高的一致性保证,适用于对数据一致性要求较高的场景。

  • THREE (三):

    在读取或写入操作完成后,需要三个副本节点确认操作成功。这提供了更高的一致性保证,通常用于需要强一致性的应用程序。

  • QUORUM (多数):

    在读取或写入操作完成后,需要大多数副本节点(N/2 + 1,其中N是副本总数)确认操作成功。QUORUM提供了更强的一致性,通常用于需要较高一致性的应用程序。

  • ALL (所有):

    在读取或写入操作完成后,需要所有副本节点确认操作成功。这提供了最高级别的一致性,但可能会导致较高的延迟。

  • LOCAL_ONE (本地一):

    类似于ONE级别,但只需要本地数据中心的一个副本节点确认操作成功。适用于多数据中心设置。

  • LOCAL_QUORUM (本地多数):

    类似于QUORUM级别,但只需要本地数据中心的大多数副本节点确认操作成功。适用于多数据中心设置。

  • EACH_QUORUM (每个多数):

    在多数据中心设置中,需要每个数据中心的QUORUM级别确认。这提供了多数据中心设置下的一致性保证。

通过选择适当的一致性级别,应用程序可以权衡读取性能和一致性需求。较低的一致性级别(如ONE或LOCAL_ONE)可以提供更低的延迟,但可能导致较低的一致性保证。较高的一致性级别(如ALL或QUORUM)提供了更高的一致性,但可能会增加延迟。

参考: https://cassandra.apache.org/doc/3.11/ https://docs.datastax.com/en/archived/cassandra/3.0/index.html

Infee Fang
Infee Fang
互联网二手搬砖工