Cassandra:基本概念
数据模型
Cassandra的数据模型与常见的关系型数据库的数据模型有很大的不同
列(Column)
列是Cassandra的基本数据结构单元,具有三个值:名称,值、时间戳
名称 | 值 | 时间戳 |
---|---|---|
name:byte[] | value:byte[] | clock:byte[] |
在Cassandra中不需要预先定义列(Column),只需要在KeySpace里定义列族,然后就可以开始写数据了。
列族( Column Family)
列族相当于关系数据库的表(Table),是包含了多行(Row)的容器。
ColumnFamily的结构举例,如图:
可以理解为Java结构 Map
1)ColumnFamily 的2种类型
-
静态column family(static column family)
静态的column family,字段名是固定的,比较适合对于这些column都有预定义的元数据
-
动态column family(dynamic column family)
动态的column family,字段名是应用程序计算出来并且提供的,所以column family只能定义这些字段的类型,无法不可以指定这些字段的名字和值,这些名字和值是由应用程序插入某字段才得出的。
2)Row key
ColumnFamily 中的每一行都用Row Key(行键)来标识,这个相当于关系数据库表中的主键,并且总是被索引的。
3)主键
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 机制)
副本因子(Replication Factor)
副本因子决定数据有几份副本。例如:
副本因子为1表示每一行只有一个副,。副本因子为2表示每一行有两个副本,每个副本位于不同的节点上。在实际应用中为了避免单点故障,会配置为3以上。
注意:所有的副本都同样重要,没有主从之分。可以为每个数据中心定义副本因子。副本策略设置应大于1,但是不能超过集群中的节点数。
副本放置策略 (Replica placement strategy)
描述的是副本放在集群中的策略
目前有2种策略,内容如下:
策略名 | 中文名 | 描述 |
---|---|---|
SimpleStrategy | 简单策略 | 适用于只有一个数据中心。为集群指定简单的副本因子(有几个副本) |
NetworkTopologyStrategy | 网络拓扑策略 | 推荐方式,因为可以扩展到多数据中心,可以单独为每个数据中心设置复制因子 |
Durable_writes
否对当前KeySpace的更新使用commitlog,默认为true
副本 (Replication)
副本就是把数据存储到多个节点,来提高容错性
节点(Node)
存储数据的机器
集群(Cluster)
Cassandra数据库是为跨越多条主机共同工作,对用户呈现为一个整体的分布式系统设计的。Cassandra最外层容器被称为群集。Cassandra将集群中的节点组织成一个环(ring)(一致性hash处理),然后把数据分配到集群中的节点(Node)上。
超级列
超级列是一个特殊列,因此,它也是一个键值对。但是超级列存储了子列的地图。
通常列族被存储在磁盘上的单个文件中。因此,为了优化性能,重要的是保持您可能在同一列族中一起查询的列,并且超级列在此可以有所帮助。下面是超级列的结构。
数据类型
CQL提供了一组丰富的内置数据类型,用户还可以创建自己的自定义数据类型。
CQL是Cassandra提供的一套查询语言
数值类型
数据类型 | 含义 | 描述 |
---|---|---|
int | 32位有符号整型 | 和 Java 中的 int 类似 |
bigint | 64位长整型 | 和 Java 中的 long 类似 |
smallint | 16位有符号整型 | 和 Java 中的 short 类似 |
tinyint | 8位有符号整型 | 和 Java 中的 tinyint 类似 |
varint | 可变精度有符号整数 | 和 Java 中的 java.math.BigInteger 类似 |
float | 32位 IEEE-754 浮点型 | 和 Java 中的 float 类似 |
double | 64位 IEEE-754 浮点型 | 和 Java 中的 double 类似 |
decimal | 可变精度的 decimal | 和 Java 中的 java.math.BigDecimal 类似 |
文本类型
CQL提供2种类型存放文本类型,text和varchar基本一致
数据类型 | 含义 | 描述 |
---|---|---|
ascii | 文本 | 表示ASCII字符串 |
text | 文本 | 表示UTF8编码的字符串 |
varchar | 文本 | 表示UTF8编码的字符串 |
时间类型
数据类型 | 含义 | 描述 |
---|---|---|
timestamp | 时间 | 包含了日期和时间,使用64位有符号的整数表示 |
date | 日期 | |
time | 时间 |
标识符类型
类型 | 含义 | 描述 |
---|---|---|
uuid | 128位数据类型 | 通用唯一识别码 CQL 中的 uuid 实现是 Type 4 UUID,其实现完全是基于随机数的 |
timeuuid | Type 1 UUID |
集合类型
1)set
集合数据类型,set 里面的元素存储是无序的。
set 里面可以存储前面介绍的数据类型,也可以是用户自定义数据类型,甚至是其他集合类型。
2)list
list 包含了有序的列表数据,默认情况下,数据是按照插入顺序保存的。
3)map
map 数据类型包含了 key/value 键值对。key 和 value 可以是任何类型,除了 counter 类型
使用集合类型要注意:
1、集合的每一项最大是64K。
2、保持集合内的数据不要太大,免得Cassandra 查询延时过长,Cassandra 查询时会读出整个集合内的数据,集合在内部不会进行分页,集合的目的是存储小量数据。
3、不要向集合插入大于64K的数据,否则只有查询到前64K数据,其它部分会丢失。
其他基本类型
类型 | 含义 | 描述 |
---|---|---|
boolean | 布尔类型 | 值只能为 true/false |
blob | 二进制大对象 | 存储媒体或者其他二进制数据类型时很有用 |
inet | IPv4 或 IPv6 网络地址 | cqlsh 接受用于定义 IPv4 地址的任何合法格式,包括包含十进制,八进制或十六进制值的点或非点式表示 CQL 会输出为 0.0.0.0 这种 地址形式。 |
counter | 计数器类型 | 值不能直接设置,而只能递增或递减 不能用作主键的一部分;如果使用计数器,则除primary key 列之外的所有列都必须是计数器 |
用户自定义类型
如果内置的数据类型无法满足需求,可以使用自定义数据类型。
Cassandra的数据存储
Cassandra的数据包括在内存中的和磁盘中的数据。
CommitLog:主要记录客户端提交过来的数据以及操作。这种数据被持久化到磁盘中,方便数据没有被持久化到磁盘时可以用来恢复。
Memtable:用户写的数据在内存中的形式,它的对象结构在后面详细介绍。其实还有另外一种形式是BinaryMemtable 这个格式目前 Cassandra 并没有使用,这里不再介绍了。
SSTable:数据被持久化到磁盘,这又分为 Data、Index 和 Filter 三种数据格式。
CommitLog 数据格式
Cassandra在写数据之前,需要先记录日志,保证Cassandra在任何情况下宕机都不会丢失数据,这就是CommitLog日志。要写入的数据按照一定格式组成 byte 组数,写到 IO 缓冲区中定时的被刷到磁盘中持久化。Commitlog是server级别的。每个Commitlog文件的大小是固定的,称之为一个CommitlogSegment
。
当一个Commitlog文件写满以后,会新建一个的文件。当旧的Commitlog文件不再需要时,会自动清除。
Memtable 内存中数据结构
数据写入的第二个阶段,MemTable是一种内存结构,当数据量达到块大小时,将批量flush到磁盘上,存储为SSTable。优势在于将随机IO写变成顺序IO写,降低大量的写操作对于存储系统的压力。每一个columnfamily对应一个memtable。也就是每一张表对应一个。用户写的数据在内存中的形式。
SSTable 数据格式
SSTable是Read Only的,且一般情况下,一个ColumnFamily会对应多个SSTable,当用户检索数据时,Cassandra使用了Bloom Filter,即通过多个hash函数将key映射到一个位图中,来快速判断这个key属于哪个SSTable。
为了减少大量SSTable带来的开销,Cassandra会定期进行compaction,简单的说,compaction就是将同一个ColumnFamily的多个SSTable合并成一个SSTable。
在Cassandra中,compaction主要完成的任务是:
1) 垃圾回收: cassandra并不直接删除数据,因此磁盘空间会消耗得越来越多,compaction 会把标记未删除的数据真正删除;
2) 合并SSTable:compaction 将多个 SSTable 合并为一个(合并的文件包括索引文件,数据文件,bloom filter文件),以提高读操作的效率;
3) 生成 MerkleTree:在合并的过程中会生成关于这个ColumnFamily中数据的 MerkleTree,用于与其他存储节点对比以及修复数据。
Cassandra的重要知识点
Cassandra的集群中每一台机器都是对等的,不存在主、从节点的区分,集群中任何一台机器出现故障是,整个集群系统不会受到影响。
一致性哈希是Cassandra搭建集群的基础,一致性哈希可以降低分布式系统中,数据重新分布的影响。
在Cassandra中,每个表有Primary Key外,还有一个叫做Partition Key,Partition Key列的Value会通过Cassandra一致性算法得出一个哈希值,这个哈希值将决定这行数据该放到哪个节点上。
每个节点拥有一段数字区间,这个区间的含义是:如果某行记录的Partition Key的哈希值落在这个区间范围之内,那么该行记录就该被存储到这个节点上。
如果简单的使用哈希值,可能会引起数据分布不均匀的问题,为了解决这个问题,一致性哈希提出虚拟节点的概念,简单的理解就是:将某个节点根据一个映射算法,映射出若干个虚拟子节点出来,再把这些节点分布在哈希环上面,保存数据时,如果通过一致性哈希计算落到某个虚拟子节点上,这条记录就会被存在这个虚拟子节点的母节点上。
Token:在Cassandra,每个节点都对应一个token,相当于hash环中的一个节点地址。在Cassandra的配置文件中有一项配置叫做:num_tokens,这个配置项可以控制一个节点映射出来的虚拟节点的个数。
Range:在Cassandra中,每一个节点负责处理hash环的一段数据,范围是从上一个节点的Token到本节点Token,这就是Range
在健康的集群中,可以通过自带的工具nodetool查看集群的哈希环具体情况,命令为:nodetool ring。
这里我们使用cassandra官方文档中一张图来说明:
Gossip内部通信协议
Cassandra使用Gossip的协议维护集群的状态,这是个端对端的通信协议。通过Gossip,每个节点都能知道集群中包含哪些节点,以及这些节点的状态,
Gossip进程每秒运行一次,与最多3个其他节点交换信息,这样所有节点可很快了解集群中的其他节点信息。