逖靖寒的世界

每天进步一点点

基于Cassandra搭建简单Blog程序后台

在上一篇博客《大话Cassandra数据模型》中,我们讲解了Cassandra的数据模型。在这篇博客中,我们将基于Cassandra搭建一个简单的Blog程序后台。

需求

Blog程序的需求如下:

1 允许不同的用户写Blog。
2 Blog内容包括:标题,内容,ID,发布日期。
3 每一篇Blog可以打上任意多个Tag。
4 人们可以在Blog上留言,内容包括:留言内容,留言人的名字,留言时间。

设计

针对上面的4点需求,我们可以相应地建立4个ColumnFamily(以后用CF简写)来保存数据。

Author ColumnFamily

作者CF用于保存用户的相关信息,如下所示:

Authors : { // CF
        逖靖寒: { // row key
            numPosts: 11,
            homepage:
http://gpcuster.cnblogs.com
            email: gpcuster@gmail.com,
            sex: male
        },
        // and the other authors
        Author 2 {
            ...
        }
    }

Author CF属于standard类型,Key为用户的ID,每一个Col代表了用户的一个属性。每一个用户相关联的属性不需要预先定义好,这也是面向列存储的好处之一:)

BlogEntries ColumnFamily

博客CF用与保存每一篇Blog的相关信息,如内容,作者信息,tag信息等等:

BlogEntries : { // CF
        1222212 : { // row key - 博客文章的ID,这个是唯一的.
            title: 基于Cassandra搭建简单Blog程序后台
            body: lalalalalala
            author: 逖靖寒  // 这个col的value对应着Authors CF中的key
            tags: DB,NoSQL  // 我们使用逗号来分隔多个tag信息
            pubDate: 1250558004      // 发布时间
            ID: 1222212
        },
        // 另外一篇博客
        1222213 : {
            ...
        }
    }

BlogEntries CF属于standard类型,Key为每一篇博客的ID。注意这里的author col,通过这个col,我们可以直接查询到author的信息。

Tag ColumnFamily

Tag CF用户保存用户给每一篇博客打上的Tag信息:

Tag : { // CF
        NoSQL : {  // 我们使用Tag的内容作为Key。
            // column的名称为博客发布的时间,内容为博客的ID
            timeuuid_1 : 1222212,
            timeuuid_2 : 1222214,
        },
        // 这个另一个Tag信息
        Life : { 
            …         
        }

Tag CF属于standard类型,我们可以通过博客的ID找到对应的博客内容。

Comments ColumnFamily

用户评论CF设计如下:

Comments : {
        1222212 : { // 这里的key就是BlogEntry的key:博客的ID
            timeuuid_1 : { // 这个是Super Column的名称
                // 评论的信息
                commenter: 逖靖寒,
                email:
gpcuster@gmail.com,
                comment: hello
                commentTime: 1250438004
            },
            // 这篇博客的另一个评论
            timeuuid_2 : {
                commenter: Some Dude,
                email: sd@example.com,
                comment: be nice Joe Blow this isnt youtube
                commentTime: 1250557004
            },           
        },
        // 另一篇博客的评论
        12222343 : {
            …    
        }

Comments CF属于super类型。这里key的设置我们可以非常方便的找到一篇Blog对应的所有评论信息。

配置信息

根据上面设计的各个CF的信息,我们需要在storage-conf.xml文件中配置各个CF的信息,配置情况如下:

<Keyspace Name="BloggyAppy">
        <!-- CF definitions -->
        <ColumnFamily CompareWith="BytesType" Name="Authors"/>
        <ColumnFamily CompareWith="BytesType" Name="BlogEntries"/>
        <ColumnFamily CompareWith="TimeUUIDType" Name="TaggedPosts"/>
        <ColumnFamily CompareWith="TimeUUIDType" Name="Comments"
            CompareSubcolumnsWith="BytesType" type="Super"/>
    </Keyspace>

使用

现在假设我们要根据一个tag找到相关博客,并且打开这篇博客后能够看到作者的信息和相关的评论。

假设选择的tag的名称为NoSQL:

返回tag为NoSQL的所有blog的ID:get BloggyAppy.Tag[‘NoSQL’]

假设选择的blog ID为1222212:

放回Blog的内容:get BloggyAppy.BlogEntries[‘1222212’]

查阅这篇Blog的所有评论信息:get BloggyAppy.Commnets[‘1222212’]

查阅作者信息也是类似的。

 

这样我们就完成一个简单的Blog程序的后台。

参考

WTF is a SuperColumn? An Intro to the Cassandra Data Model

 

更多关于Cassandra的文章:http://www.cnblogs.com/gpcuster/tag/Cassandra/

标签: Cassandra, NoSQL

posted on 2010-03-16 23:20 逖靖寒 阅读(3553) 评论(21) 编辑 收藏

Feedback

#1楼 2010-03-17 01:04 九色迷离      

LZ
能不能讲讲在.net下如何配置呢
国内资料实在少啊。都怪咱e文不好。哎。。
 回复 引用 查看   

#2楼 2010-03-17 08:38 真见      

友情帮顶。。  回复 引用 查看   

#3楼 2010-03-17 09:11 1-2-3      

虽然暂时用不上,但是看到楼主一直这样认真的写,也得支持一下。  回复 引用 查看   

#4楼[楼主] 2010-03-17 09:55 逖靖寒      

@1-2-3
@真见
谢谢,欢迎一起讨论。
 回复 引用 查看   

#5楼[楼主] 2010-03-17 09:56 逖靖寒      

@九色迷离
Cassandra可以直接在windows上面运行,你看看下载文件包里面的README就知道了,然后在Linux环境下装一个thrift,生成一个C#版本的Client端就可以使用了。
 回复 引用 查看   

#6楼 2010-03-18 09:47 mx1700      

问lz一个问题
调用 get BloggyAppy.Tag[‘NoSQL’] 获取拥有NoSQL标签的所有文章ID
如果我要现实20篇文章,是不是就得调用20次get BloggyAppy.BlogEntries
这样效率会比Sql关连查询快吗?
 回复 引用 查看   

#7楼[楼主] 2010-03-18 10:21 逖靖寒      

@mx1700
是的,需要调用20次get BloggyAppy.BlogEntries
当数量级变大以后,和传统的数据库还是有区别的。但是具体区别有多大,还得根据你实际的情况来去衡量。
 回复 引用 查看   

#8楼[楼主] 2010-03-30 10:58 逖靖寒      

@mx1700

引用mx1700:
问lz一个问题
调用 get BloggyAppy.Tag[‘NoSQL’] 获取拥有NoSQL标签的所有文章ID
如果我要现实20篇文章,是不是就得调用20次get BloggyAppy.BlogEntries
这样效率会比Sql关连查询快吗?

你也可以使用Cassandra的批量操作的API。  回复 引用 查看   

#9楼 2010-05-13 11:31 chen eric      

学习了,兄弟对nosql数据库还有分布式研究得蛮多的嘛,希望兄弟能加入我们的讨论QQ群:109222590 (nosql数据库技术群)  回复 引用 查看   

#10楼 2010-08-24 15:19 Ray Sun      

请教一下,如果我对自己的好友分组,分组的CF可以这么定义么:
Group:{
GroupID1://Key
{
Onwer:UserID,//表示组的拥有者
timeuuid_1:friendID1,//第一个好友
timeuuid_2:friendID2
}
GroupID2:
{
...}
}
初学,对Key/Value和Name/Value理解还不是很好...
 回复 引用 查看   

#11楼[楼主] 2010-08-25 08:55 逖靖寒      

@Ray Sun
可以的。
不过把SuperColumn拿来存大量的好友不是很合适。
因为Cassandra里面只对Standard CF下的Column有索引,如果是Super CF,那么只有SuperColumn有索引。所以当你的好友个数很多的时候,查找速度就会大大降低。
当量大的时候,就要考虑使用Standard CF了:)
 回复 引用 查看   

#12楼 2010-09-10 16:01 undead      

请教一下。
c#。
当前我的nameColumnPath为Authors。那么我添加的时候如何才能把几个column中的value加进去。发现只能加一个column。如下。
client.insert("Keyspace1",iKey.ToString(),nameColumnPath,utf8Encoding.GetBytes("JoeBloggs" ),timeStamp,ConsistencyLevel.ONE);

还有分页。
 回复 引用 查看   

#13楼[楼主] 2010-09-10 16:24 逖靖寒      

@undead
我理解你是希望一次性为某一个key添加多个Column,你可以使用这个接口:
client.batch_mutation
 回复 引用 查看   

#14楼 2010-09-10 17:50 undead      

3Q。。还有一个问题。我想实现blog的列表分页。那么我需要知道列表的ID。有什么接口可以获得么。还有排序的功能有接口么。  回复 引用 查看   

#15楼 2010-09-10 17:52 undead      

对了。。忘了问了。。您的资料是从哪里了解的。。网上的例子我找了但都不是很合适。并没有过多的解释。  回复 引用 查看   

#16楼[楼主] 2010-09-11 10:32 逖靖寒      

@undead

引用undead:3Q。。还有一个问题。我想实现blog的列表分页。那么我需要知道列表的ID。有什么接口可以获得么。还有排序的功能有接口么。

我们说有一个专门的CF来保存用户的Blog,这个CF的key就是用户的ID,key对应的value就是这个用户的所有Blog。
因为排序,所以我们可以在value上面(也就是Column),指定Column Name的排序规则,我们可以使用TimeUUID的规则(在CF的配置信息中指定CompareWith字段),这样我们就能够保证一个用户的所有博客按照时间顺序排序。
所以在整个程序中,我们通过用户的ID就能够查询到这个用户所有的博客信息,并且是按照时间前后顺序排序的。
最后说到分页,Cassandra只支持排行后结果的前N条或者后N条,接收到原始数据以后,你需要另外处理。  回复 引用 查看   

#17楼[楼主] 2010-09-11 10:45 逖靖寒      

@undead

引用undead:对了。。忘了问了。。您的资料是从哪里了解的。。网上的例子我找了但都不是很合适。并没有过多的解释。

全部都是在官网上面找的。  回复 引用 查看   

#18楼 2010-09-11 13:23 undead      

谢谢。明白了。  回复 引用 查看   

#19楼 2010-09-14 19:56 小小虫      

您好!我按照您的提示实验了一下。只是在windows下,修改了一个配置文件和属性文件,结果出了问题,不知道怎么处理,您看看会是什么原因呢?
D:\apache-cassandra\bin>cassandra.bat
Starting Cassandra Server
Listening for transport dt_socket at address: 8888
10/09/14 19:49:23 INFO utils.CLibrary: JNA not found. Native methods will be disabled.
ERROR 19:49:23,875 Exception encountered during Startup.
java.lang.ExceptionInInitializerError at org.apache.cassandra.thrift.CassandraDaemon.setup(CassandraDaemon.java:72) at org.apache.cassandra.thrift.CassandraDaemon.main(CassandraDaemon.java:214)
Caused by: java.lang.RuntimeException: com.sun.org.apache.xerces.internal.impl.i
o.MalformedByteSequenceException: Invalid byte 1 of 1-byte UTF-8 sequence. at org.apache.cassandra.config.DatabaseDescriptor.<clinit>(DatabaseDescriptor.java:541)
... 2 more
Caused by: com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: Invalid byte 1 of 1-byte UTF-8 sequence.
at com.sun.org.apache.xerces.internal.impl.io.UTF8Reader.invalidByte(UTF8Reader.java:674)
at com.sun.org.apache.xerces.internal.impl.io.UTF8Reader.read(UTF8Reader
.java:547)
at com.sun.org.apache.xerces.internal.impl.XMLEntityScanner.load(XMLEnti
tyScanner.java:1742)
at com.sun.org.apache.xerces.internal.impl.XMLEntityScanner.scanData(XML
EntityScanner.java:1242)
at com.sun.org.apache.xerces.internal.impl.XMLScanner.scanComment(XMLSca
nner.java:756)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImp
l.scanComment(XMLDocumentFragmentScannerImpl.java:1036)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImp
l$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:2945)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(X
MLDocumentScannerImpl.java:648)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImp
l.scanDocument(XMLDocumentFragmentScannerImpl.java:510)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(X
ML11Configuration.java:807)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(X
ML11Configuration.java:737)
at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.
java:107)
at com.sun.org.apache.xerces.internal.parsers.DOMParser.parse(DOMParser.
java:225)
at com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl.parse(Doc
umentBuilderImpl.java:283)
at javax.xml.parsers.DocumentBuilder.parse(DocumentBuilder.java:208)
at org.apache.cassandra.utils.XMLUtils.<init>(XMLUtils.java:43)
at org.apache.cassandra.config.DatabaseDescriptor.<clinit>(DatabaseDescr
iptor.java:166)
... 2 more
Exception encountered during startup.
java.lang.ExceptionInInitializerError
at org.apache.cassandra.thrift.CassandraDaemon.setup(CassandraDaemon.jav
a:72)
at org.apache.cassandra.thrift.CassandraDaemon.main(CassandraDaemon.java
:214)
Caused by: java.lang.RuntimeException: com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: Invalid byte 1 of 1-byte UTF-8 sequence.
at org.apache.cassandra.config.DatabaseDescriptor.<clinit>(DatabaseDescriptor.java:541)
... 2 more
Caused by: com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: Invalid byte 1 of 1-byte UTF-8 sequence.
at com.sun.org.apache.xerces.internal.impl.io.UTF8Reader.read(UTF8Reader.java:547)
这里还有类似的错误,就不一一列举了
at javax.xml.parsers.DocumentBuilder.parse(DocumentBuilder.java:208)
at org.apache.cassandra.utils.XMLUtils.<init>(XMLUtils.java:43)
at org.apache.cassandra.config.DatabaseDescriptor.<clinit>(DatabaseDescr
iptor.java:166)
... 2 more
后来我用原来没修改配置文件和属性文件的那个版本也不能用了,很奇怪,您能不能指教下?谢谢
 回复 引用 查看   

#20楼[楼主] 2010-09-14 23:29 逖靖寒      

@小小虫
hi, 你的配置文件中有不能识别的字符,建议不要在配置文件中出现中文。
 回复 引用 查看   

#21楼 2011-06-08 10:12 brockhong      

和上面的同学一样 不过我在windows 下成功的



INFO 10:04:20,235 JNA not found. Native methods will be disabled.
ERROR 10:04:20,291 Exception encountered during startup.
java.lang.ExceptionInInitializerError
at org.apache.cassandra.thrift.CassandraDaemon.setup(CassandraDaemon.java:74)
at org.apache.cassandra.thrift.CassandraDaemon.main(CassandraDaemon.java:226)
Caused by: java.lang.RuntimeException: java.lang.RuntimeException: XPathFactory#newInstance() failed to create an XPathFactory for the default object model: http://java.sun.com/jaxp/xpath/dom with the XPathFactoryConfigurationException: javax.xml.xpath.XPathFactoryConfigurationException: No XPathFctory implementation found for the object model: http://java.sun.com/jaxp/xpath/dom
at org.apache.cassandra.config.DatabaseDescriptor.<clinit>(DatabaseDescriptor.java:604)
... 2 more
Caused by: java.lang.RuntimeException: XPathFactory#newInstance() failed to create an XPathFactory for the default object model: http://java.sun.com/jaxp/xpath/dom with the XPathFactoryConfigurationException: javax.xml.xpath.XPathFactoryConfigurationException: No XPathFctory implementation found for the object model: http://java.sun.com/jaxp/xpath/dom
at javax.xml.xpath.XPathFactory.newInstance(Unknown Source)
at org.apache.cassandra.utils.XMLUtils.<init>(XMLUtils.java:60)
at org.apache.cassandra.config.DatabaseDescriptor.<clinit>(DatabaseDescriptor.java:210)
... 2 more
Exception encountered during startup.
java.lang.ExceptionInInitializerError
at org.apache.cassandra.thrift.CassandraDaemon.setup(CassandraDaemon.java:74)
at org.apache.cassandra.thrift.CassandraDaemon.main(CassandraDaemon.java:226)
Caused by: java.lang.RuntimeException: java.lang.RuntimeException: XPathFactory#newInstance() failed to create an XPathFactory for the default object model: http://java.sun.com/jaxp/xpath/dom with the XPathFactoryConfigurationException: javax.xml.xpath.XPathFactoryConfigurationException: No XPathFctory implementation found for the object model: http://java.sun.com/jaxp/xpath/dom
at org.apache.cassandra.config.DatabaseDescriptor.<clinit>(DatabaseDescriptor.java:604)
... 2 more
Caused by: java.lang.RuntimeException: XPathFactory#newInstance() failed to create an XPathFactory for the default object model: http://java.sun.com/jaxp/xpath/dom with the XPathFactoryConfigurationException: javax.xml.xpath.XPathFactoryConfigurationException: No XPathFctory implementation found for the object model: http://java.sun.com/jaxp/xpath/dom
at javax.xml.xpath.XPathFactory.newInstance(Unknown Source)
at org.apache.cassandra.utils.XMLUtils.<init>(XMLUtils.java:60)
at org.apache.cassandra.config.DatabaseDescriptor.<clinit>(DatabaseDescriptor.java:210)
... 2 more
 回复 引用 查看