所有答案(在撰写本文时)都假设 Redis、MongoDB 以及可能基于 SQL 的关系数据库本质上是相同的工具:“存储数据”。他们根本不考虑数据模型。
MongoDB:复杂数据
MongoDB 是一个文档存储。与 SQL 驱动的关系数据库进行比较:关系数据库简化为索引 CSV 文件,每个文件都是一个表;文档存储简化为带索引的 JSON 文件,每个文件都是一个文档,多个文件组合在一起。
JSON 文件在结构上与 XML 和 YAML 文件相似,在 Python 中也与字典相似,因此请在这种层次结构中考虑您的数据。索引时,结构是键:文档包含命名键,其中包含更多文档、数组或标量值。请考虑以下文档。
{
_id: 0x194f38dc491a,
Name: "John Smith",
PhoneNumber:
Home: "555 999-1234",
Work: "555 999-9876",
Mobile: "555 634-5789"
Accounts:
- "379-1111"
- "379-2574"
- "414-6731"
}
上面的文档有一个键PhoneNumber.Mobile,它的值是555 634-5789。您可以搜索关键字PhoneNumber.Mobile 具有一定价值的文档集合;它们已编入索引。
它还有一个包含多个索引的Accounts 数组。可以查询Accounts 包含恰好一些值子集、所有一些值子集或任何个值的文档一些值的子集。这意味着您可以搜索Accounts = ["379-1111", "379-2574"],但找不到上述内容;可以搜索Accounts includes ["379-1111"],找到上面的文档;并且您可以搜索Accounts includes any of ["974-3785","414-6731"] 并找到上述内容以及任何包含帐户“974-3785”的文档(如果有)。
文档的深度随心所欲。 PhoneNumber.Mobile 可以保存一个数组,甚至是一个子文档(PhoneNumber.Mobile.Work 和PhoneNumber.Mobile.Personal)。如果您的数据是高度结构化的,那么文档是关系数据库的一大进步。
如果您的数据大多是扁平的、关系型的且结构严格,那么最好使用关系型数据库。同样,最重要的标志是您的数据模型是最适合一组相互关联的 CSV 文件还是一组 XML/JSON/YAML 文件。
对于大多数项目,您必须妥协,在 SQL 或文档存储不适合的一些小范围内接受较小的解决方法;对于一些存储广泛数据(许多列;行无关)的大型复杂项目,将一些数据存储在一个模型中并将其他数据存储在另一个模型中是有意义的。 Facebook 使用 SQL 和图形数据库(数据被放入节点,节点连接到其他节点); Craigslist 曾经使用 MySQL 和 MongoDB,但一直在考虑完全转向 MongoDB。如果放在一个模型下,这些地方的数据跨度和关系将面临重大障碍。
Redis:键值对
Redis 基本上是一个键值存储。 Redis 允许您给它一个键并查找单个值。 Redis 本身可以存储字符串、列表、哈希和其他一些东西;但是,它只能按名称查找。
缓存失效是计算机科学的难题之一;另一个是命名事物。这意味着当您想避免对后端进行数百次过度查找时,您将使用 Redis,但您必须弄清楚何时需要进行新的查找。
最明显的失效情况是写入时更新:如果您读取user:Simon:lingots = NOTFOUND,您可能会SELECT Lingots FROM Store s INNER JOIN UserProfile u ON s.UserID = u.UserID WHERE u.Username = Simon 并将结果100 存储为SET user:Simon:lingots = 100。然后,当您授予西蒙 5 个 lingots 时,您会阅读 user:Simon:lingots = 100、SET user:Simon:lingots = 105 和 UPDATE Store s INNER JOIN UserProfile u ON s.UserID = u.UserID SET s.Lingots = 105 WHERE u.Username = Simon。现在您的数据库和 Redis 中有 105 个,并且无需查询数据库即可获得 user:Simon:lingots。
第二种情况是更新依赖信息。假设您生成页面的块并缓存它们的输出。表头显示玩家的经验、等级和金额;玩家的个人资料页面有一个显示其统计数据的块;等等。玩家获得一些经验。好吧,现在您有几个templates:Header:Simon、templates:StatsBox:Simon、templates:GrowthGraph:Simon 等字段,其中缓存了通过模板引擎运行的六个数据库查询的输出。通常,当您显示这些页面时,您会说:
$t = GetStringFromRedis("templates:StatsBox:" + $playerName);
if ($t == null) {
$t = BuildTemplate("StatsBox.tmpl",
GetStatsFromDatabase($playerName));
SetStringInRedis("Templates:StatsBox:" + $playerName, $t);
}
print $t;
因为您刚刚更新了GetStatsFromDatabase("Simon") 的结果,所以您必须将templates:*:Simon 从您的键值缓存中删除。当您尝试渲染这些模板中的任何一个时,您的应用程序将从您的数据库(PostgreSQL、MongoDB)中获取数据并将其插入到您的模板中;然后它将结果存储在 Redis 中,并且希望下次显示该输出块时不必费心进行数据库查询和渲染模板。
Redis 还允许您执行发布者订阅消息队列等。那完全是另一个话题。这里的重点是 Redis 是一个键值缓存,它不同于关系数据库或文档存储。
结论
根据您的需要选择您的工具。最大的需求通常是数据模型,因为它决定了你的代码有多复杂和容易出错。专门的应用程序将依赖于性能,您可以在其中混合使用 C 和汇编编写所有内容;大多数应用程序只会处理一般情况并使用缓存系统,例如 Redis 或 Memcached,这比高性能 SQL 数据库或文档存储快得多。