每天 1000 万次统计更新 - 我应该使用哪种数据库/缓存系统?

10 millions updates per day for stats - What DB / Cache system should I use?

我目前在我的网站上遇到性能问题。情况可以概括为以下几点:

由于统计数据不如核心系统重要,而且我看到 SQL 服务器很吃力,我认为将这些统计数据 table 移到其他地方可能会很好。

主要问题是:处理更新占主导地位的统计数据的最佳方法是什么?这个想法也是只保留一台服务器。

我试着看看有什么办法可以改善这种情况:

任何与当前情况相关的想法都将不胜感激。
谢谢

这里有更多关于统计的信息 table 我有:

TABLE [dbo].[UserStat](
[Id] [int] IDENTITY(1,1) NOT NULL,
[UserId] [int] NOT NULL,
[Hits] [int] NOT NULL,
[Points] [decimal](38, 6) NOT NULL,
[Date] [datetime] NOT NULL,
[LastHit] [datetime] NOT NULL,
[Ip] [varchar](256) NULL,
)

我这样更新统计数据:

UPDATE [UserStat] SET Hits = Hits + 1, Points = Points + @Points, LastHit = @Last WHERE UserId = @Id AND [Ip] = @Ip AND [Date] = @Date

如果当前用户和日期的行不存在,我创建它:

INSERT INTO [UserStat] ([UserId],[Hits],[Points],[Date],[LastHit],[Ip]) VALUES (@UserId,@Hits,@Points,@Date,@LastHit,@Ip)

有两个索引:

服务器是VPS。日志和数据文件在同一个磁盘上。 table.

上没有涉及外键

这是我发现的所有 SELECT 查询 table:

SELECT Points, Hits, [Date] FROM [UserStat] WHERE UserId = @UId AND [Date] >= @date

SELECT Ip FROM [UserStat] WHERE UserId = @UId AND LastHit >= DATEADD(MINUTE,-15,getdate())

SELECT COUNT(Id) FROM [UserStat] WHERE [LastHit] >= DATEADD(MINUTE,-15,getdate())

但我并不是真的担心 SELECT,更多的是更新的数量 ^^。

在进入主要问题之前,必须进行一些更改:

您应该从 Express 版迁移到 Enterprise 版或至少迁移到 Standard 版(Express vs others)

由于您的数据有大量更新,您应该禁用索引(如果有的话)

尝试调整您的 table 列的大小,这样您的记录的单元格可以存储在更少的页面中,这将有助于加快更新过程(例如,如果您有 table 有 20 列,你总是只更新固定的 5 个已知列,然后将这 5 列与其他 15 列分开。这可能有助于你将数据安排在较少的页面中,当你的页面较少时,你可以以更快的速度找到您的记录。当然,这不是基于正常形式,但它可以帮助您提高性能)

看看您的可用内存和 CPU。这两个是性能的基石。

关于你的主要问题,我需要了解更多关于你的统计信息和它的 table 以及它的用法。您是指 sql 服务器统计信息,还是其他意思?

你能确认ID是你的主键吗?如果是这样,那就没问题了,因为它是一个单调递增的值并且对插入有好处。我认为您的其他索引(用于更新目的)应该是

INDEX [Select_UpdateUserStatNavigation] ON [dbo].[UserStat](
[UserId] ASC,[IP] ASC, [Date] ASC). 

确保列在索引中从最多 select 到最少 select 排序。这应该会加快更新速度,因为可以更快地定位行。稍后我们可以查看 SELECT 的索引。

BY VPS,你的意思是它是一个虚拟服务器?我会查看您的 IO 统计信息以检查 IO 是否是瓶颈。 SQL分配了多少内存?那可能是另一个问题。内存不足可能导致分页到磁盘 - IO 子系统中最慢的部分。

如果可能的话,我会考虑将您的日志和数据磁盘拆分到不同的磁盘上。将它们放在同一个磁盘上会导致磁盘争用 - 再次出现在您的 IO 子系统中最慢的部分。

你能post使用的select查询吗?如果需要,我可以提供建议的索引。

此外,您可能希望将单独的插入和更新过程替换为 MERGE,如下所示。

MERGE UserStat AS TargetTable
USING (SELECT @UserId UserID,@Hits Hits,@Points Points,@Date [Date],@LastHit LastHit,@Ip Ip) AS SourceData
ON SourceData.UserID = TargetTable.UserID 
    AND SourceData.IP = TargetTable.IP 
    AND SourceData.[Date] = TargetTable.[Date])
WHEN MATCHED THEN UPDATE SET Hits = Hits + 1, Points = Points + SourceData.Points, LastHit = SourceData.LastHit 
WHEN NOT MATCHED THEN INSERT (UserID,Hits,Points,[Date],LastHit,Ip)
                 VALUES(SourceData.UserID,SourceData.Hits,SourceData.Points,SourceData.[Date],SourceData.LastHit,SourceData.Ip)

这确实是一个非常简单且很好的示例,说明了何时适用 NoSql 数据库。 NoSql 是为 "web-scale" 应用程序创建的,例如这个,其中数据的速度和数量完全超过了 SQL 数据库跟上的能力(关系 DBMS 的一大弱点)。

事实上,常规 SQL 不适合您的情况。这有几个原因,包括:

  1. SQL 对于处理关系数据很有用。这里的数据没有真正的关系或依赖关系(至少,不是你所描述的),实际上,即使是适度复杂的真实数据集也可以更好地去规范化并放入 NoSql 平台。
  2. SQL 引入了大量开销。由于查询解释、查找索引、查询索引、提取值等,简单地 运行 查询从数据库中获取单个值的成本至少是在 NoSql 数据存储中的 3-5 倍。 ,其中 NoSql 数据存储一步即可为您获取记录。
  3. SQL 数据库主要设计为高度一致。这意味着它们通常不能驻留在一台以上的机器上(尽管这不再完全正确)并且它们有额外的开销来确保数据保持一致。

现在,让我们看看您的特定用例:

  1. 大量更新事务,偶尔插入。 大多数 NoSql 数据库平台使用 Set 操作,根据需要更新或插入。无需 运行 每次都执行两个语句。

  2. 单个主键。NoSql 数据库是键值存储,其中您的键(在本例中为 UserId)指向单个主键记录在数据库中。

  3. 简单的统计和索引。一些 NoSql 数据库提供内置索引功能,有些甚至允许您对数据进行 map-reduce 以获取详细统计出来。其他人会自动进行数据聚合,您可以编写特殊查询来获取所需的数据。在这种情况下,您的 "Stat Id" 字段是无用的,可以删除(是的,占用更少的存储空间!)。

  4. 快速且可扩展。 这是您不会用 SQL 数据库接触的东西。没有SQL就是为此设计的。

鉴于以上情况,您的场景是何时应用 NoSql 解决方案的教科书示例。我可以为您的统计存储推荐 Couchbase, which is an extremely fast in-memory database with disk-based storage (this fulfills your cache and data storage requirement in one shot). You might also consider Elasticsearch,因为它可以执行一些开箱即用的非常好的数据聚合。无论您选择哪种 NoSql 解决方案,您都将获得灵活的可扩展性和易维护性。我敢说你当全职DBA的日子就要结束了。

RavenDB 非常容易上手,运行 适合这种情况。您将获得快速写入和潜在的快速读取。您还会得到 ACID 或尽可能接近。 RavenDB 很容易在 MVC 中连接起来。由于您有 Mongo 经验,文档的概念对您来说应该不陌生。在您的 MVC 应用程序中使用 RavenDB C# 客户端库,在几个小时内您应该能够取得重大进展。只要确保您了解这些限制即可。默认情况下,查询可能比更新晚几微秒,并且像许多 NoSql 或 CQRS 解决方案一样,如果您清除查询的缓存,则可能需要几分钟到几小时才能完全重建缓存。