一般来说设计数据库需要考虑一对一或一对多的问题,我们看一下下面的数据:
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。这样每个类别下就可以存多个电话了。