Web照片库的合适NoSQL数据模式
我想为一个照片库建立合适的数据结构,使用NoSQL存储。在我的网页应用中,一张照片可以属于一个或多个相册。我之前有使用MySQL的经验,但对键值存储几乎没有了解。
如果是用MySQL,我会设置(3)个表,像这样:
photos (photo_id, title, date_uploaded, filename)
albums (album_id, title, photo_id)
album_photo_map (photo_id, album_id)
然后,为了获取最新的5张照片(连同相册信息),我会用这样的查询:
SELECT *
FROM albums, photos, album_photo_map
WHERE albums.album_id = album_photo_map.album_id AND
photos.photo_id = album_photo_map.photo_id
ORDER BY photos.date_uploaded DESC LIMIT 5;
那么,使用NoSQL的键值对数据库(特别是亚马逊的DynamoDB),我该如何实现类似的查询呢?存储结构会是什么样的?索引又是怎么工作的呢?
3 个回答
在使用DynamoDB的时候,照片表的“结构”可以是这样的:
相册_照片
- 相册ID(字符串,主键)
- 照片ID(数字,范围键)
- ... 其他字段
在我写的“其他字段”那里,你可以保存所有照片的数据,也可以单独请求一个合适的数据表来获取这些信息,但这样做会导致重复数据,因为同一张照片可能出现在多个相册里。
你可以在这个表里保存“主”相册的所有照片数据,而在其他相册中用一个列来标明哪个是主相册的ID。由于NoSQL数据库不需要严格的结构,所以在表中并不一定要有每一列。
如果照片ID有自动递增的特性,你可以很容易地获取某个相册的最新几张照片。如果没有,你可以用日期作为范围键,照片ID作为一列。还有一个好主意是把范围键反向使用,这样可以更方便地查询到最新的一行数据。
Redis可以处理这个问题。对于你提到的RMDBS表:
你可以用以下命令来设置照片的信息:
SET photos:photo_id:title "一些照片的标题"
SET photos:photo_id:date_uploaded "一些上传时间(比如2011-02-09 HH:MM:SS)"
SET photos:photo_id:filename "一些文件名"
对于相册的信息,你可以这样设置:
SET albums:album_id:title "一些相册的标题"
然后,用这个命令把照片和相册关联起来:
SADD album_photo_map:photo_id album_id
你还可以使用列表(Redis支持列表)来存储最近上传的照片,并在上传新照片时更新这个列表:
ret = r.lpush("upload:last_upload_times", photo_id) // 更新列表
ret = r.ltrim("upload:last_upload_times", 0, N-1) // 控制列表的长度
接下来,如果我们想获取最近上传的N张照片和它们的相册信息,可以这样做:
last_uploaded_photo_list = r.lrange("upload:last_upload_times", 0, N-1)
last_uploaded_photo_with_album_list = [(photo_id, album_id) for photo_id in last_uploaded_photo_list for album_id in r.smembers(photo_id)]
使用mongodb的术语,你的集合可能看起来像这样:
photos = [
{
_id: ObjectId(...),
title: "...",
date_uploaded: Date(...),
albums: [
ObjectId(...),
...
]
},
...
]
albums = [
{
_id: ObjectId(...),
title: "..."
}
]
要找到最新的5张照片,可以这样做:
> var latest = db.photos.find({}).sort({date_uploaded:1}).limit(5);
在mongo中没有服务器端的连接,所以你需要像这样获取所有最新的相册:
> var latest_albums = latest.find({}, {albums: 1});
当然,接下来你需要把这些数据整理成一个集合。
其实,如果你把相册嵌入到照片文档里会更简单,因为它们很小:
photos = [
{
_id: ObjectId(...),
title: "...",
date_uploaded: Date(...),
albums: [
{name: "family-vacation-2011", title: "My family vacation in 2010"},
...
]
},
...
]
这样查询就一样了,但你不需要连接。查找一个相册里的所有照片看起来是这样的:
> db.photos.find({albums:{$elemMatch:{name: "family-vacation-2011"}}});