cassandra数据做为一个很是相似于关系型数据库的nosql,其在数据表的设计上于关系型数据库有着一些不一样的地方。本文尽我的所理解的内容作简单的剖析。html
Primary Keymysql
关系型数据的Primary Key主要是做为数据的惟一性标识用的,对具体业务并无太大的帮助,好比大量使用的自增id做为惟一主键,而这个id实际上并非业务的数据,只是做为一个技术上保证惟一性标识的手段而已。sql
cassandra的Primary key也要保证数据的惟一性标识,可是其更大的做用是服务于查询,也就是说cassandra的主键更可能是面向于业务来设计的,值得注意的是cassandra的表的primary key并不能动态改变,因此不少设计者犯得一个错误就是要查询的字段并无在primary key中致使只能开启全表扫描查询,这是一种不可取的办法,因此在设计之初就应该避免这种状况。数据库
复合主键nosql
cassandra主键包含了两个部分,partition key和cluster key。在说明这两个key以前须要简单的介绍一下cassandra的一些基本特性,在关系数据库的结构中当数据量增多的时候回致使单个表的数据查询性能降低,设计者也缺乏好的策略去自动分割这些大量的数据。函数
cassandra则是采用partition来对于数据进行划分保存,在良好设计的状况下cassandra就会按照定义的策略自动对数据进行分区,而partition key就是cassandra分区的依据,经过一个一致性hash函数计算这个partition key的token进而直接找到其所在的分区。因此须要注意的是数据一旦写入分区就不能update partition key了,当你能够经过删除而后再添加来实现。性能
表设计spa
在这里以datastax的一个demo来展开说明。设计
race_year int, #比赛的年份htm
race_name text, #比赛的名称
cyclist_name text, #选手名称
rank int, #名次
若是是在mysql中这是一个很是简单表,直接添加一个自增的主键id,而后把这四个字段加进去就能够了。
在cassandra中这个表的设计则变得有点复杂了,下面咱们将参照不一样的设计来分析所能应付的不一样的业务。
在这以前咱们要首先确保数据的惟一性,在某一年的某一场比赛中的第几名应该是能够肯定到惟一的一条数据的。
假如表数据以下
CREATE TABLE rank_by_year_and_name (
race_year int,
race_name text,
cyclist_name text,
rank int,
PRIMARY KEY ((race_year),race_name,rank));
race_year做为惟一的partition key,race_name和rank做为cluster key,咱们能够说这个表将会按照每个年份自动切分数据,而每个分区内都会按照比赛名和名称作排序。
接下来咱们作一些简单的练习。
INSERT INTO rank_by_year_and_name(race_year, race_name, rank, cyclist_name)VALUES (2018, '青海自行车赛', 2,'李明');
INSERT INTO rank_by_year_and_name(race_year, race_name, rank, cyclist_name)VALUES (2018, '青海自行车赛', 1,'李明');
INSERT INTO rank_by_year_and_name(race_year, race_name, rank, cyclist_name)VALUES (2018, '青海自行车赛', 2,'李明');
INSERT INTO rank_by_year_and_name(race_year, race_name, rank, cyclist_name)VALUES (2018, '青海自行车赛', 1,'李明');
INSERT INTO rank_by_year_and_name(race_year, race_name, rank, cyclist_name)VALUES (2019, '青海自行车赛', 1,'李明');
UPDATE rank_by_year_and_name SETcyclist_name = '博尔特'WHERErace_year = 223 ANDrace_name = '环法自行车赛' AND rank = 1 IF EXISTS;
select * from rank_by_year_and_name;
select * from rank_by_year_and_name where race_year=2018;
select * from rank_by_year_and_name where race_name='青海自行车赛';
select * from rank_by_year_and_name where rank=1;
select * from rank_by_year_and_name where race_name='青海自行车赛' and rank=1 and race_year=2018;
select * from rank_by_year_and_name where race_name='青海自行车赛' and rank=1 and race_year>=2018;
select * from rank_by_year_and_name where race_year=2018 and race_name='青海自行车赛' and rank>0;
select * from rank_by_year_and_name where race_year=2018 and rank>0;
select * from rank_by_year_and_name where race_name='青海自行车赛' and race_year in (2018,2019);
下面选择几个特别的查询作特殊说明
第二句INSERT INTO rank_by_year_and_name(race_year, race_name, rank, cyclist_name)VALUES (2018, '青海自行车赛', 2,'李明');
能够看到当数据重复的时候cassandra不作任何提示.
UPDATE rank_by_year_and_name SETcyclist_name = '博尔特'WHERErace_year = 222 ANDrace_name = '环法自行车赛' ANDrank = 1;
使用if exists能够防止update的时候变成插入
UPDATE rank_by_year_and_name SETcyclist_name = '博尔特'WHERErace_year = 223 ANDrace_name = '环法自行车赛' ANDrank = 1 IF EXISTS;
select * from rank_by_year_and_name where race_name='青海自行车赛';
select * from rank_by_year_and_name where rank=1;
这样就意味着说咱们查询只能在指定的partition key上操做,某种程度上这就是直接避免了关系型数据库中的全表扫描。
select * from rank_by_year_and_name where race_name='青海自行车赛' and rank=1 and race_year>=2018;
select * from rank_by_year_and_name where race_name='青海自行车赛' and race_year in (2018,2019);
若是须要对多个分区进行操做,cassandra partition key不支持 >=这操做,尽管是int类型,而只能使用in查询。缘由很简单,partition key是用于一致性hash计算获得token从而获取分区的,而hash是分散的因此不会随着partition key有大于小于的关系,可是in固然是能够了。
select * from rank_by_year_and_name where race_year=2018 and rank>0;
select * from rank_by_year_and_name where race_year=2018 and race_name='青海自行车赛' ;
第二句能够执行,第一句错误,缘由在cluster key是排序的,是有层级的,因此只能一层层的搜索。
总结
Cassandra在查询的时候尽量避免不明确的查询,查询必须指定partition key,若是想要在多个partition key查询,就要使用in来匹配多个,对于cluster key,层级关系也是很明确。Cassandra这样设计目的在于减小大面积的不肯定的查询,在tb甚至更多的数据中作模糊查询可能会到来很是恶劣的效果。
很是差表设计
CREATE TABLE rank_by_year_and_name ( race_year int, race_name text, cyclist_name text, rank int, PRIMARY KEY ((race_year)));
INSERT INTO rank_by_year_and_name(race_year, race_name, rank, cyclist_name)VALUES (2018, '青海自行车赛', 2,'李明');INSERT INTO rank_by_year_and_name(race_year, race_name, rank, cyclist_name)VALUES (2018, '青海自行车赛', 1,'李明');
上面的表问题在没法保证数据的惟一,数据会被覆盖,因此存在明显缺陷。
CREATE TABLE rank_by_year_and_name ( race_year int, race_name text, cyclist_name text, rank int, id int,PRIMARY KEY ((id)));
这个表几乎是直接挪用关系型数据库的表。
可是结果是因为要查询的字段都不在primary key,致使只能按照主键id查询,几乎等同于废表一张。
接下来可能你会天真说,那好我把全部的字段都放到primary key里。
CREATE TABLE rank_by_year_and_name ( race_year int, race_name text, cyclist_name text, rank int, id int,PRIMARY KEY ((id)),race_year,race_name,cyclist_name,rank);
可是别着急当你须要查询的须要制定一个partition key,而这个上面的表是一个id,当你按照某一个年份查询的时候你只能使用in对全部的partition key作扫描。这很明显又是一个很是差的选择。
并且经过上面两个反面的例子,你们应该更能理解cassandra和关系型数据库在表设计上的差别,虽然不是十万百千里那么用,五万四千里仍是有的,若是你直接套路关系型数据库的设计思路,那么获得的结果就是比用关系型数据库还要差。