Cassandra

数据库介绍和基础应用

陶佳元 技术分享

分享内容概要


  • Cassandra 是什么
  • 与传统关系型数据库比较
  • 与其他 NoSQL 比较
  • 安装运行和基本配置
  • 数据类型和存储结构
  • CQL 操作语法介绍
  • 编程语言对接

Cassandra 是什么


Cassandra,卡珊德拉,为希腊、罗马神话中特洛伊的公主,阿波罗的祭司。

因神蛇以舌为她洗耳,获阿波罗的赐予而有了预言能力。

又因抗拒阿波罗,预言不被人相信,成为一名不被人所听信的女先知。

特洛伊战争后被俘虏,并遭杀害。

Cassandra 数据库


Cassandra 是一套开源 分布式 NoSQL 数据库系统。

它是一种 列式存储 数据库。

最初由 Facebook 开发,2010 年时成为 Apache 顶级项目。

 

传统关系型数据库


传统的数据库是关系型的,且是按行来存储的

其中只有张三把一行数据填满了,李四、王五、赵六的行都没有填满

造成了一定程度的空间浪费

列式存储数据库


列式存储的非关系型数据库

原来张三的六列数据变成了现在的六行,所以原来的主键(即张三)重复了六次

造成了一定程度的数据冗余,但是没有空间的浪费

传统关系型数据库 VS 列式存储


世界由非结构化数据构成


我们生活在一个数据越来越丰富的世界里,但是这些数据都不能整齐的展示在一个 RDBMS 的行和列中

根据估计,世界上 90% 的数据是在过去两年中被创造 (2015),以及 80% 的商业数据是非结构化的。更重要的是,非结构化数据的增长速度是结构化数据的两倍

Cassandra VS Redis


  • Redis 注重的是高速内存缓存
  • Cassandra 专注于大量数据的存储和查询

Cassandra VS MongoDB


  • MongoDB 为文档型存储。无法保证数据有效性和完整性,官方建议不要使用 MongoDB 保存重要的不可丢失的信息
  • Cassandra 为列式存储,通常以集群形式部署,多点备份保证数据完整性

Cassandra VS HBase


  • HBase 只负责数据管理,它需要配合 HDFS 和 zookeeper 来搭建集群,主从结构,有单点失效问题的可能
  • Cassandra 是一个数据存储和管理系统,p2p 架构,无单点失效问题
  • 相对来说数据的写入效率 Cassandra 更高,而数据的查询效率 HBase 较好

为什么使用 Cassandra


  • 拓展和复制在企业规模小的时候,不是一个大问题。而在业务和规模扩大的过程中,它很迅速地成为大问题
  • Cassandra 特点是从一开始设计就解决这个问题,在集群规模管理方面非常出色
  • 在机器拓展部署上,表现也特别出色,自带的备份机制,保证各个数据中心的数据安全
  • 增加集群容量时,你只需启动一台新机器,并告诉 Cassandra 那里的新节点,然后,它完成其他剩下的事情

安装



# 安装 docker
wget -qO- https://get.docker.com/ | sh

# 拉取 Cassandra 镜像
docker pull cassandra:3

运行



# 启动一个节点
docker run --name cass -p 9042:9042 -e CASSANDRA_BROADCAST_ADDRESS=x.x.x.x -d cassandra:3

# 启动第二个节点
docker run --name cass2 -d -e CASSANDRA_SEEDS=x.x.x.x cassandra:3

# 数据持久化
docker run --name cass -p 9042:9042 -v /d/share/cassandra:/var/lib/cassandra -d cassandra:3

配置


配置文件位于 /etc/cassandra/cassandra.yaml,里面有非常多的可配置项,这里列出比较重要和常用的配置项:


cluster_name: 'Test Cluster'
seeds: "172.17.0.2"
broadcast_address: 172.17.0.2
native_transport_port: 9042

saved_caches_directory: /var/lib/cassandra/saved_caches
commitlog_directory: /var/lib/cassandra/commitlog
data_file_directories:
    - /var/lib/cassandra/data

详见 官方配置说明

CQL


Cassandra 可以使用 cqlsh 进行管理, 使用的语法格式为 CQL。

docker exec -it cass cqlsh

CQL 数据表示方式


string   ::=  '\'' (any character where ' can appear if doubled)+ '\''
              '$$' (any character other than '$$') '$$'
integer  ::=  re('-?[0-9]+')
float    ::=  re('-?[0-9]+(\.[0-9]*)?([eE][+-]?[0-9+])?') | NAN | INFINITY
boolean  ::=  TRUE | FALSE
uuid     ::=  hex{8}-hex{4}-hex{4}-hex{4}-hex{12}
hex      ::=  re("[0-9a-fA-F]")
blob     ::=  '0' ('x' | 'X') hex+
string   ::=  'a''bC'
              $$a'bC$$
integer  ::=  2048
float    ::=  256.1
boolean  ::=  TRUE
uuid     ::=  fc9a5d89-584d-4665-874e-632530525c6c
hex      ::=  3f6a29
blob     ::=  0x5622af8881

数据类型


类型表示方式
asciistring
bigintinteger
blobblob
booleanboolean
counterinteger
dateinteger, string
decimalinteger, float
类型表示方式
doubleinteger float
durationduration
floatinteger, float
inetstring
intinteger
smallintinteger
textstring
类型表示方式
timeinteger, string
timestampinteger, string
timeuuiduuid
tinyintinteger
uuiduuid
varcharstring
varintinteger

常用类型


类型表示方式
textstring
intinteger
floatinteger, float
booleanboolean
dateinteger, string
timestampinteger, string

自定义类型


CREATE TYPE phone (
    area_code text,
    number text,
)

CREATE TYPE address (
    city text,
    zip int,
    street text,
    phones map<text, phone>
)
{
    "city": "Shanghai",
    "zip": 201906,
    "street": "No.999 Dangui Rd",
    "phones": { 
        "mobile" : { "area_code": "021", "number": "456-1111" },
        "landline" : { "area_code": "0598", "number": "456-2222" } 
    }
}


Keyspace


键空间(Keyspace)是用于保存列族,用户定义类型的对象,Keyspace 就像 RDBMS 中的数据库

CREATE KEYSPACE examplekey
    WITH replication = {'class': 'SimpleStrategy', 'replication_factor' : 3};

CREATE KEYSPACE examplekey
    WITH replication = {'class': 'NetworkTopologyStrategy', 'DC1': '3', 'DC2': '2'};
  • class 复制策略,包含 SimpleStrategyNetworkTopologyStrategy
  • replication_factor 复制因子,同一份数据复制的数量

Column Family


列族(Column Family)是多个列(Column)的集合,Column Family 就像 RDBMS 中的数据库 table,并且在 CQL 中可以用 TABLE 关键词代替 COLUMN FAMILY

CREATE COLUMN FAMILY user (
    id int,
    name text,
    age int,
    address address,
    register_at time,
    PRIMARY KEY (id, name)
) 
  • 通过 PRIMARY KEY 定义主键索引,只有建立了索引的数据才能被快速高效地查询
  • PRIMARY KEY 中的第一个字段为 partition_key,之后的字段为 clustering_key

PRIMARY KEY 内部储存原理


  • 以上例子中的表 partition_key id , clustering_key name
+------+--------------+-------------------+----------------------+------------------------+
|      | name = tomy: |  name=tomy:age    |  name=tomy:address   |  name=tomy:register_at |
| id 1 | value =      |  value=6e616d6531 |  value=73636f726531  |  value=73636f726531    |
|      | timestamp    |     timestamp     |       timestamp      |       timestamp        |
+------+--------------+-------------------+----------------------+------------------------+
|      | name = john: |  name=john:age    |  name=john:address   |  name=john:register_at |
| id 2 | value =      |  value=6e616d6532 |  value=73636f726532  |  value=73636f726532    |
|      | timestamp    |     timestamp     |       timestamp      |       timestamp        |
+------+--------------+-------------------+----------------------+------------------------+
|      | name = zhan: |  name=zhan:age    |  name=zhan:address   |  name=zhan:register_at |
|      | value =      |  value=6e616d6533 |  value=73636f726533  |  value=73636f726533    |
|      | timestamp    |     timestamp     |       timestamp      |       timestamp        |
| id 3 +--------------+-------------------+----------------------+------------------------+
|      | name = lily: |  name=lily:age    |  name=lily:address   |  name=lily:register_at |
|      | value=       |  value=6e616d6534 |  value=73636f726534  |  value=73636f726534    |
|      | timestamp    |     timestamp     |       timestamp      |       timestamp        |
+------+--------------+-------------------+----------------------+------------------------+

Secondary Index


二级索引(Secondary Index)是用于为数据添加额外的索引


CREATE INDEX ON user(age);

CREATE INDEX index_name ON user(age);

添加了 Secondary Index 后,age 字段也可进行高效率的查询了

数据操作


  • CQL 的语法和 SQL 极为相似,基本可以像操作 MySQL 一样来操作 Cassandra
SELECT name, occupation FROM users WHERE userid = 199;
SELECT COUNT (*) AS user_count FROM users;

INSERT INTO NerdMovies (movie, director, main_actor, year) 
       VALUES ('Serenity', 'Joss Whedon', 'Nathan Fillion', 2005);

UPDATE NerdMovies SET director='Joss Whedon', main_actor='Nathan Fillion', year=2005 
       WHERE movie = 'Serenity';

DELETE FROM NerdMovies WHERE movie = 'Serenity';

数据操作


  • 注意 CQL 只是为了降低新手的学习成本才将语法设计的与 SQL 雷同,其背后核心的处理逻辑是完全不同的
  • 所以 在掌握基本语法的基础上必须深入理解 Cassandra 的存储原理才能设计和写出高效 CQL
SELECT JSON name, occupation FROM users WHERE userid IN (199, 200, 207);
SELECT * FROM users WHERE birth_year = 1981 AND country = 'FR' ALLOW FILTERING;

INSERT INTO NerdMovies
       JSON $${"movie": "Serenity", "director": "Joss Whedon", "year": 2005}$$;
       USING TTL 10000

BEGIN BATCH
   INSERT INTO users (userid, password, name) VALUES ('user2', 'ch@ngem3b', 'second user');
   UPDATE users SET password = 'ps22dhds' WHERE userid = 'user3';
   INSERT INTO users (userid, password) VALUES ('user4', 'ch@ngem3c');
   DELETE name FROM users WHERE userid = 'user1';
APPLY BATCH;

对接编程语言


Cassandra 支持多种语言的客户端连接,下面以 Python 为例

pip install cassandra-driver

from cassandra.cluster import Cluster
cluster = Cluster(['106.14.160.252'], 9042)

session = cluster.connect()
session.set_keyspace('keyspace_name')
cql = "INSERT INTO users (userid, password, name) VALUES ('user2', 'ch@ngem3b', 'second user');"
session.execute(cql)