一般来说设计数据库需要考虑一对一或一对多的问题,我们看一下下面的数据:
Contact: {
ian: {
name: "Ian Wu"
phone: "3939889"
birthday: "1983/01/01"
email: "ianwu@example.com"
}
john: {
name: "John Li"
phone: "4499888"
birthday: "1984/02/02"
email: "johnli@example.com"
}
}
从上面的数据结构上我们可以看出,如果联系人有两个电话怎么办?在 cassandra 的数据模型里面column name是不可以重复的,所以上面的数据结构是无法存储两个电话号码的,总不能用phone1、phone2去解决吧。
按照关系型数据库的设计原则,当需要一对多的时候,就建立一个多方表格来存储多方的数据,同时利用主键来连接两张表,而在cassandra里面我们也可以把增加一个 column family 来存多方数据,同时再用row key来维持两个 column family 的关系。
我们需要把Phone拉出来再建立一个 column family 来存储联系人多个电话。
<!--
Phone: {
ian: {
work: "3939889"
home: "4499588"
mobile: "0919919919"
}
john: {
work: "4499888"
work-fax: "4499999"
}
}
-->
<ColumnFamily CompareWith="UTF8Type" Name="Phone"/>
细心的朋友会发现这个又有一个新的问题产生,解决了Phone的问题之后,那Email是不是也照搬呢,那万一联系人有许多的属性都要用到一对多解决的话,那不就是要建立很多个 column family。当然,除了上面的解法之外,我们也可以建立一个 super column family 叫做MoreInformation來把联系人所有的多方数据都存储在一起,像下面这样。
<!--
MoreInformation: {
ian: {
phone: {
work: "3939889"
home: "4499588"
mobile: "0919919919"
}
email: {
work: "ianwu@example.com"
home: "ianwu@yahoo.com.tw"
}
}
john: {
phone: {
work: "4499888"
work-fax: "4499999"
}
email: {
work: "johnli@example.com"
}
}
}
-->
<ColumnFamily ColumnType="Super"
CompareWith="UTF8Type"
CompareSubcolumnsWith="UTF8Type"
Name="MoreInformation"/>
当然,所有的设计都不是完美的,上面的存储方式又有一个问题,如果ian有多个work的电话怎么办呢?像下面一这样来存储么?
MoreInformation: {
ian: {
phone: {
work: {
lexicalUUID1: "3939889"
lexicalUUID2: ""
}
}
}
}
这样做是肯定不行的因为super column已经是极限了
如果有这样的需求,我们可以修改之前的Phone column family 来满足多个work的电话。
<!--
Phone: {
ian: {
work: {
UUID1: "3939889"
UUID2: "3939889#1234"
}
home: {
UUID3: "4499588"
}
mobile: {
UUID4: "0919919919"
UUID5: "0933123123"
}
}
john: {
work: {
UUID6: "4499888"
}
work-fax: {
UUID7: "4499999"
}
}
}
-->
<ColumnFamily ColumnType="Super"
CompareWith="UTF8Type"
CompareSubcolumnsWith="LexicalUUIDType"
Name="Phone"/>
这里我们使用UUID来生成work、home里column name。这样每个类别下就可以存多个电话了。