Web照片库的合适NoSQL数据模式

21 投票
3 回答
2089 浏览
提问于 2025-04-17 10:44

我想为一个照片库建立合适的数据结构,使用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 个回答

1

在使用DynamoDB的时候,照片表的“结构”可以是这样的:

相册_照片

  • 相册ID(字符串,主键)
  • 照片ID(数字,范围键)
  • ... 其他字段

在我写的“其他字段”那里,你可以保存所有照片的数据,也可以单独请求一个合适的数据表来获取这些信息,但这样做会导致重复数据,因为同一张照片可能出现在多个相册里。

你可以在这个表里保存“主”相册的所有照片数据,而在其他相册中用一个列来标明哪个是主相册的ID。由于NoSQL数据库不需要严格的结构,所以在表中并不一定要有每一列。

如果照片ID有自动递增的特性,你可以很容易地获取某个相册的最新几张照片。如果没有,你可以用日期作为范围键,照片ID作为一列。还有一个好主意是把范围键反向使用,这样可以更方便地查询到最新的一行数据。

3

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)]

13

使用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"}}});

撰写回答