基于列存储的开源分布式NoSQL数据库Apache Cassandra入门分享
@
概述
定义
Apache Cassandra 官网地址 https://cassandra.apache.org/ 最新版本4.1.1
Apache Cassandra 官网文档地址 https://cassandra.apache.org/doc/latest/
Apache Cassandra 源码地址 https://github.com/apache/cassandra
Apache Cassandra 是一个开源的分布式、高可用、最终一致性的NoSQL数据库,可以快速、可扩展可靠的管理海量数据,无单点故障。
NoSQL,泛指非关系型的数据库,NoSQL去掉关系数据库的关系型特性,数据之间无关系,非常容易扩展。
- 易扩展:NoSQL数据库种类繁多,但是一个共同的特点都是去掉关系数据库的关系型特性。数据之间无关系,这样就非常容易扩展,在架构的层面上带来了可扩展的能力。
- 大数据量,高性能:NoSQL数据库都具有非常高的读写性能,尤其在大数据量下。一般MySQL使用Query Cache。NoSQL的Cache是记录级的,是一种细粒度的Cache,所以NoSQL在这个层面上来说性能就要高很多。
- 灵活的数据模型:NoSQL无须事先为要存储的数据建立字段,随时可以存储自定义的数据格式。而在关系数据库里,增删字段是一件非常麻烦的事情。
- 高可用:NoSQL在不太影响性能的情况,就可以方便地实现高可用的架构。比如Cassandra、HBase模型,通过复制模型也能实现高可用。
特性
- 弹性可扩展性:允许添加更多的硬件以适应更多的客户和更多的数据根据要求。
- 无中心化架构:没有单点故障,它可以连续用于不能承担故障的关键业务应用程序。
- 快速线性性能:增加集群中的节点数量增加你的吞吐量。因此,保持一个快速的响应时间。
- 灵活的数据存储:包括:结构化,半结构化和非结构化。它可以根据您的需要动态地适应变化的数据结构。
- 便捷的数据分发:通过在多个数据中心之间复制数据,可以灵活地在需要时分发数据。
- 事务支持:支持属性,如原子性,一致性,隔离和持久性(ACID)。
- 快速写入
与Hbase对比
- 架构:HBASE起源于Google BigTable,几乎遵从了BigTable论文的大多数架构设计。Cassandra则是采纳了BigTable的数据模型,同时吸收了Amazon Dynamo的分布式设计。
- 从存储结构模型的微观上看,HBASE和Cassandra在单点存储数据的机理是类似的,但是从分布式架构的宏观上看,两者则大相径庭。、
- 从单点存储模型上看起来两者较为相似,有日志追加(WAL VS CommitLog),有内存写入缓冲区(MemStore VS MemTable),也都刷盘(flush)到LSM-Tree结构的持久化文件(StoreFile VS SSTable File),都用Bloomfilter和Row Index的组合模式进行行键的索引,它们也都是利用BigTable的数据模型结构实现高速的写入和热点数据的查找。
- 从分布式架构上HBASE是中心化架构并满足分布式CAP定理中的CP(分布式一致性),强调数据写入的强一致性;Cassandra是去中心化架构并满足分布式CAP定理中的AP(分布式高可用),适应数据在读取过程中完成最终一致性。
- 关键特性
- 由内看结构: 在查询方面Cassandra还支持二级索引,内置CQL(MySQL的SQL语法接近),SSTable分层结构也侧重定位与查找;但HBase没有二级索引,只强调列簇的行键scan,Region中的Store与HDFS密切配合,StoreFile中KV以顺序排列,存储强调整体的时间写入顺序。因此Cassandra就非常适合通过列字段为条件来查找,而HBase更擅长通过行扫描做列集分析。
- 本质原因在于Cassandra的数据是基于一致性哈希算法,按照HASH范围划分,实现记录根据哈希值在整个集群节点的随机分布以及复本冗余,那么查找起来更适合在整个集群中对任何记录进行大范围的定位和查询,充分利用集群的整体算力;
- 但是HBase是顺序的写入同一个Region,在数据量足够大后再分裂,那么HBase就不适合频繁大范围的对数据定位与查找,更适合按行键做顺序扫描的集合分析。查询主要体现在就近和热点数据上的高性能。
- 由外看分布式: Cassandra的集群去中心化主要利用一致性哈希环机制实现数据的分布和扩容缩容的数据迁移,利用gossip协议在对等节点的网络传播下保存集群状态一致性,利用anti-entropy(反熵)机制实现数据读取过程中节点之间的比对,保证数据一致性,这些都是集群在对等条件下基于机制而达成状态上的共识,那么Cassandra的这些特性,就使得集群不能太大,太大就不好管理,也容易导致网络通讯过于密集。Cassandra这种去中心化架构表现出来的优点就是集群无单点故障隐患,集群健壮性高,可用性极高,运维很省事。HBASE以及所依赖的Hadoop HDFS都是基于中心化集中式管理,存在HMaster的集群单点故障风险,因此一般HBASE的HMaster可以有一个或多个HA热备,引入HA后的HBASE集群依然很健壮,只是必然引入更高的部署复杂度,底层依赖的HDFS NameNode HA在服务部署复杂性方面则更甚之。
- 由内看结构: 在查询方面Cassandra还支持二级索引,内置CQL(MySQL的SQL语法接近),SSTable分层结构也侧重定位与查找;但HBase没有二级索引,只强调列簇的行键scan,Region中的Store与HDFS密切配合,StoreFile中KV以顺序排列,存储强调整体的时间写入顺序。因此Cassandra就非常适合通过列字段为条件来查找,而HBase更擅长通过行扫描做列集分析。
- 适用场景:HBASE更侧重分析,Cassandra则胜于查询
- Cassandra更适合在数据大吞吐的情况下,借助数据分布优势,高速写入,并通过二级索引实现SQL语法丰富的字段级查找,以及支持在线应用实时产生的超大规模数据的存储,可以在大规模数据写入与查询的都比较适合的场景下替代MySQL,在事务和一致性要求不严格的环境下,为每天并发与写入量惊人的在线业务系统,提供数据库支撑。因此其面向服务的领域偏重oltp。
- HBASE更适合管理着大规模集群,并在超大规模数据之上进行实时的,结构化的海量数据支撑,而且满足强一致性要求,达到行级事务要求,可以使其对接一些关键性业务在可靠性要求高的环境下支撑在线实时分析,例如电子商务交易,金融交易等等。但并不适合随机性很强的查询,更适合大吞吐的数据写入,热点数据的行级查找以及大规模的扫描分析。并且具有Hadoop生态的数仓工具支撑。因此HBASE更面向olap。
Cassandra使用场景
- 特征
- 数据写入操作密集
- 数据修改操作很少
- 通过主键查询
- 需要对数据进行分区存储
- 场景举例
- 存储日志型数据
- 类似物联网的海量数据
- 对数据进行跟踪
术语
- cluster:使用gossip协议交换消息的两个或多个数据库实例。
- commitlog:数据库将更改后的数据追加到文件,以便在故障后恢复。
- datacenter:在集群中一起配置的一组相关节点,用于复制和工作负载隔离。不一定是单独的位置或物理数据中心。数据中心名称区分大小写,不能修改。
- gossip:用于在节点之间交换位置和状态信息的点对点通信协议。
- hint:除了read-repair和full/incremental anti-entropy repair之外,Cassandra实现的三种方法之一,也是最终一致性保证,最终确认所有更新最终被所有副本接收。
- listen address:要绑定到的地址或接口,并告诉要连接到的其他Cassandra节点。
- seed node:种子节点用于引导加入集群的新节点。
- snitch:节点IP地址到物理位置和虚拟位置(如机架和数据中心)的映射。
- SSTable:提供了从键到值的持久的、有序的不可变映射,其中键和值都是任意的字节字符串。
架构
概览
Cassandra是具有最终一致语义的分区宽列存储模型,最初是由Facebook设计的,它使用了一种分阶段事件驱动架构(SEDA)来实现Amazon的Dynamo分布式存储和复制技术以及谷歌的Bigtable数据和存储引擎模型的组合。
Cassandra提供了Cassandra查询语言(CQL),这是一种类似sql的语言,用于创建和更新数据库模式以及访问数据。
- Keyspace:定义每个数据中心如何复制数据集。复制是指每个集群保存的副本数量。键空间包含表。
- Table:定义分区集合的类型化模式。表包含分区,分区包含行,行包含列。Cassandra表可以灵活地向表中添加新列,并且零停机时间。
- Partition:定义主键的强制部分,Cassandra中的所有行都必须识别存储该行的集群中的节点。所有性能查询都在查询中提供分区键。
- Row:包含由唯一主键标识的列集合,该主键由分区键和可选的其他集群键组成。
- Column:具有属于行类型的单个数据。
CQL在分区数据集上支持许多高级特性,例如:
- 具有原子比较和设置语义的单分区轻量级事务。
- 用户定义的类型、函数和聚合
- 集合类型包括集合、映射和列表。
- 本地二级索引
- 物化视图(实验阶段)
Dynamo
Apache Cassandra依赖于Amazon的Dynamo分布式存储键值系统的许多技术。Dynamo系统中的每个节点都有三个主要组件
- 请求分区数据集上的协调
- 环成员和故障检测
- 本地持久性(存储)引擎
Cassandra主要利用前两个集群组件,同时使用基于日志结构化合并树(Log Structured Merge Tree, LSM)的存储引擎。特别是Cassandra依赖于Dynamo风格:
- 使用一致哈希进行数据集分区
- 使用版本化数据和可调一致性的多主复制
- 通过gossip协议进行分布式集群成员和故障检测
- 在商用硬件上的增量扩展
Cassandra满足大规模(PiB+)业务关键型存储需求。特别是由于应用程序要求对pb级数据集进行完整的全球复制,同时还需要始终可用的低延迟读写。
数据集分区使用令牌环的一致性哈希
使用令牌环的一致性哈希:Cassandra使用一种称为一致散列的特殊散列形式在存储节点上划分数据。在朴素数据哈希中,通常通过对键对桶数取模的哈希来将键分配给桶。例如,如果您希望使用朴素哈希将数据分布到100个节点,您可以将每个节点分配到0到100之间的存储桶,对输入键取模100进行哈希,并将数据存储在相关的存储桶中。然而,在这个简单的方案中,添加一个节点可能会使几乎所有映射失效。
相反,Cassandra将每个节点映射到一个连续哈希环上的一个或多个令牌,并通过将一个键哈希到环上,然后沿一个方向“行走”环来定义所有权,类似于Chord算法。一致哈希与普通数据哈希的主要区别在于,当要哈希的节点(桶)数量发生变化时,一致哈希只需要移动一小部分键。
- 令牌:发电机样式哈希环上的单个位置。
- 端点:网络上的单个物理IP和端口。
- 主机ID:单个“物理”节点的唯一标识符,通常出现在一个gEndpoint上,并包含一个或多个gtoken。
- 虚拟节点(或vnode):由相同物理节点拥有的哈希环上的gToken,具有相同的gHost ID。
存储引擎
- CommitLog:提交日志是Cassandra节点本地所有突变的附加日志。写入Cassandra的任何数据都将首先写入提交日志,然后再写入记忆表。这在意外停机的情况下提供了持久性。在启动时,提交日志中的任何变化都将应用于memtables。
- Memtables:memtable是Cassandra缓冲写入的内存结构;通常,每个表都有一个活动的memtable;最终memtable被刷新到磁盘上,成为不可变的sstable。
- SSTables :SSTables 是Cassandra用于在磁盘上持久化数据的不可变数据文件。当SSTables 从memtable被刷新到磁盘或从其他节点被流式传输时,Cassandra触发压缩,将多个SSTables 合并为一个。一旦编写了新的SSTables ,就可以删除旧的SSTables 。
部署
单实例部署
# 通过官网下载下载最新的版本
wget https://dlcdn.apache.org/cassandra/4.1.1/apache-cassandra-4.1.1-bin.tar.gz
# 解压
tar -xvf apache-cassandra-4.1.1-bin.tar.gz
# 进入目录
cd apache-cassandra-4.1.1
# 启动,如果是root用户启动bin/cassandra -R
bin/cassandra
# 查看日志
tail -f logs/system.log
# 检查Cassandra的状态
bin/nodetool status
集群部署
准备三台192.168.2.100、192.168.2.101、192.168.2.102,选择两台作为种子节点,分别在每台上修改配置文件vim conf/cassandra.yaml
# 同一个集群名称一样
cluster_name: 'Test Cluster'
# 种子地址,配置多个示例,用逗号隔开
seed_provider:
- class_name: org.apache.cassandra.locator.SimpleSeedProvider
parameters:
- seeds: "192.168.2.100,192.168.2.101"
listen_address: 192.168.2.100
rpc_address: 192.168.2.100
# 下面文件路径按需修改,建议多个盘分开提高读写性能
# 数据文件(如sstable)所在的一个或多个目录,默认$CASSANDRA_HOME/data/data
data_file_directories: /data1/cassandra/data
# commitlog文件所在的目录
commitlog_directory: /data2/cassandra/commitlog
# 保存缓存的目录
saved_caches_directory: /data3/cassandra/saved_caches
# hints所在的目录
hints_directory: /data4/cassandra/hints
每台的listen_address和rpc_address修改为本机对应的,修改配置分别启动cassandra
# 需要先杀死原来的cassandra进程,也可以自己准备脚本
ps -ef | grep cassandra
# 检查Cassandra的状态
bin/nodetool status
# 查询关于令牌环的信息
bin/nodetool ring
CQL
概述
Cassandra Query Language(CQL),CQL提供了一个类似于SQL的模型;数据存储在包含行和列的表中。因此在本文档中使用时,这些术语(表、行和列)具有与SQL中相同的定义。
- Data types
- Data definition language
- Data manipulation language
- Operators
- Secondary indexes
- Materialized views
- Functions
- JSON
- CQL security
- Triggers
- Appendices
- Changes
数据模型
- 列(Column):列是Cassandra的基本数据结构单元,具有三个值:名称,值、时间戳。在Cassandra中不需要预先定义列(Column),只需要在KeySpace里定义列族,然后就可以开始写数据了。
- 列族( Column Family):列族不严谨来说相当于关系数据库的表(Table),是包含了多行(Row)的容器。
- 类型
- 静态column family(static column family):字段名是固定的,比较适合对于这些column都有预定义的元数据。
- 动态column family(dynamic column family):字段名是应用程序计算出来并且提供的,所以column family只能定义这些字段的类型,无法不可以指定这些字段的名字和值,这些名字和值是由应用程序插入某字段才得出的。
- Row key:ColumnFamily 中的每一行都用Row Key(行键)来标识,这个相当于关系数据库表中的主键,并且总是被索引的
- 主键:Cassandra可以使用PRIMARY KEY 关键字创建主键,分为2种
- Single column Primary Key:如果 Primary Key 由一列组成称为 Single column Primary Key。
- Composite Primary Key:如果 Primary Key 由多列组成,那么这种情况称为 Compound Primary Key 或 Composite Primary Key。
- 列族具有的属性
- keys_cached - 它表示每个SSTable保持缓存的位置数。
- rows_cached - 它表示其整个内容将在内存中缓存的行数。
- preload_row_cache -它指定是否要预先填充行缓存。
- 类型
- 键空间 (KeySpace)
- Cassandra的键空间(KeySpace)不严谨相当于关系型数据库的数据库,创建一个键空间就是创建了一个数据库。键空间包含一个或多个列族(Column Family)。一般将有关联的数据放到同一个 KeySpace 下面,键空间 (KeySpace) 创建的时候可以指定一些属性:副本因子,副本策略,Durable_writes(是否启用 CommitLog 机制)
- 所有的副本都同样重要,没有主从之分。可以为每个数据中心定义副本因子。副本策略设置应大于1,但是不能超过集群中的节点数。
- 副本 (Replication):副本就是把数据存储到多个节点,来提高容错性。
- 节点(Node):存储数据的机器。
- 数据中心(DateCenter):数据中心指集群中所有的机器,组成了一个数据中心。
- 集群(Cluster):Cassandra数据库是为跨越多条主机共同工作,对用户呈现为一个整体的分布式系统设计的。Cassandra最外层容器被称为群集。Cassandra将集群中的节点组织成一个环(ring),然后把数据分配到集群中的节点(Node)上。
- 超级列:超级列是一个特殊列,因此它也是一个键值对。但是超级列存储了子列的地图。
数据类型
CQL是一种类型化语言,支持丰富的数据类型集,包括Native类型、集合类型、用户定义类型、元组类型和自定义类型
- Native类型:ASCII | BIGINT | BLOB | BOOLEAN | COUNTER | DATE | DECIMAL | DOUBLE | DURATION | FLOAT | INET | INT | SMALLINT | TEXT | TIME | TIMESTAMP | TIMEUUID | TINYINT | UUID | VARCHAR | VARINT
- 集合类型:maps(使用大括号+键值)、sets(使用大括号)、lists(使用中括号)
- 用户定义类型:CQL支持定义用户定义类型(udt)。可以使用create_type_statement、alter_type_statement和drop_type_statement来创建、修改和删除这样的类型。
- 元组类型:CQL还支持元组和元组类型(其中元素可以是不同的类型)。从功能上讲,元组可以作为具有匿名字段的匿名UDT。
# CQL Shell 简称cqlsh,是一个可以和Cassandra数据库通信的客户端,使用这个cqlsh客户端可以执行Cassandra查询语言(CQL)。指定IP和端口连接cql
./bin/cqlsh 192.168.2.100 9042
describe cluster;
describe keyspaces;
describe tables;
基础操作
键空间
# 将输出内容捕获到名为outputfile的文件
capture 'outputfile';
# 创建键空间
CREATE KEYSPACE school WITH replication = {'class':'SimpleStrategy', 'replication_factor' : 3};
# 查看捕获内容
cat outputfile
# 查看键空间
DESCRIBE school;
# 使用键空间
use school;
# 修改键空间
ALTER KEYSPACE school WITH replication = {'class':'SimpleStrategy', 'replication_factor' : 1};
DESCRIBE school;
# 删除键空间
DROP KEYSPACE school;
表
CREATE TABLE student(
id int PRIMARY KEY,
name text,
age int,
gender tinyint,
address text ,
interest set<text>,
phone list<text>,
education map<text, text>
);
# 列出当前键空间下所有表
DESCRIBE Tables;
# 查看表信息
DESCRIBE TABLE student;
# 添加字段
ALTER TABLE student ADD email text;
select * from student;
# 删除字段
ALTER table student DROP email;
select * from student
# 删除表
DROP TABLE student;
# 清空表
TRUNCATE student;
key索引
- Primary Key 主键:是用来获取某一行的数据, 可以是单一列(Single column Primary Key)或者多列(Composite Primary Key)。在 Single column Primary Key 决定这一条记录放在哪个节点。
create table test1 ( id int PRIMARY KEY, name text);
- Partition Key 分区Key:在组合主键的情况下(上面的例子),第一部分称作Partition Key(key_one就是partition key),第二部分是CLUSTERING KEY(key_two)Cassandra会对Partition key 做一个hash计算,并自己决定将这一条记录放在哪个节点。如果 Partition key 由多个字段组成,称之为 Composite Partition key
create table test2 ( key_part_one int, key_part_two int, key_clust_one int, key_clust_two int, key_clust_three uuid, name text, PRIMARY KEY((key_part_one,key_part_two), key_clust_one, key_clust_two, key_clust_three));
- Composite Primary Key 复合Key:如果 Primary Key 由多列组成,那么这种情况称为 Compound Primary Key 或 Composite Primary Key。执行创建表后,查询testTab,会发现key_one和key_two 的颜色与其他列不一样
create table test3 ( key_one int, key_two int, name text, PRIMARY KEY(key_one, key_two));
- Clustering Key 集群:决定同一个分区内相同 Partition Key 数据的排序,默认为升序,可以在建表语句里面手动设置排序的方式。
索引
- 普通列创建索引
# 为student的 name 添加索引,索引的名字为:sname, 命令:CREATE INDEX sname ON student (name);# 为student 的age添加索引,不设置索引名字,命令:CREATE INDEX ON student (age);DESCRIBE student;
可以发现 对age创建索引,没有指定索引名字,会提供一个默认的索引名:student_age_idx。
索引原理:Cassandra之中的索引的实现相对MySQL的索引来说就要简单粗暴很多了。Cassandra自动新创建了一张表格,同时将原始表格之中的索引字段作为新索引表的Primary Key!并且存储的值为原始数据的Primary Key。
- 集合列创建索引
# set集合添加索引CREATE INDEX ON student(interest); # map映射添加索引CREATE INDEX mymap ON student(KEYS(education)); # 删除索引drop index sname;
操作数据
# 插入数据INSERT INTO student (id,address,age,gender,name) VALUES (1011,'南京路',10,2,'mike') ;# 修改数据UPDATE student set age = 11 where id= 1011;# 批量操作BEGIN BATCH INSERT INTO student (id,address,age,gender,name) VALUES (1012,'长沙路',12,1,'joo') ; INSERT INTO student (id,address,age,gender,name) VALUES (1015,'上海路',20,1,'Jack') ; UPDATE student set age = 15 where id= 1012; DELETE FROM student WHERE id=1011;APPLY BATCH;# 删除数据DELETE FROM student WHERE student_id=1011;
# 更新map类型数据UPDATE student SET education={'中学': '城市第五中学', '小学': '城市第五小学'} WHERE id = 1012;# 更新list类型数据UPDATE student SET phone = ['020-66666666', '13666666666'] WHERE id = 1012;# 更新set类型数据UPDATE student SET interest = interest + {'游戏'} WHERE id = 1012;
# 查询条件
select * from student where interest CONTAINS '电影';
# 设置数据的ttl
INSERT INTO student (id,address,age,gender,name,interest, phone,education) VALUES (1030,'朝阳路30号',20,1,'Cary',{'运动', '游戏'},['020-7777888','139876667556'],{'小学' :'第30小学','中学':'第30中学'}) USING TTL 20;
- 本人博客网站IT小神 www.itxiaoshen.com