【问题标题】:10 millions updates per day for stats - What DB / Cache system should I use?统计数据每天更新 1000 万次 - 我应该使用什么数据库/缓存系统?
【发布时间】:2015-02-02 10:13:51
【问题描述】:

我目前在我的网站上遇到性能问题。情况可以总结如下:

  • 这是一个使用 SQL Server 2012 Express 的 .Net Mvc 网站。它托管在 Windows Server 2012 上。
  • 核心系统每天大约有 1000 万次 UPDATE 查询(500 万在 2 个不同的表上),另外 1000 万次用于自定义统计目的(同样是 2*500 万)。
  • INSERT 查询要少得多。
  • MVC 网站没有性能问题,大部分请求由返回纯内容(不是 html)的 1 种方法处理。

由于统计信息不如核心系统重要,而且我看到 SQL Server 遇到了很多困难,我认为将这些统计信息表移到其他地方可能会更好。

主要问题是:在更新占主导地位的情况下,处理 Stats 的最佳方式是什么?这个想法也是只保留一台服务器。

我试图看看可以采取哪些措施来改善这种情况:

  • 有一个单独的 SQL Server 数据库用于另一个硬盘上的统计信息?也许 SQL Server 可以更好地呼吸,但我不确定。
  • 使用 NoSQL 数据库?我只是对 MongoDb 有一点经验(但不是针对数百万个请求),我很想尝试使用 RavenDB。
  • 使用缓存系统? Redis 看起来很棒,但我不确定在 Windows 上运行它是否是个好主意。 AppFabric 是一个可行的选择吗?

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

以下是有关我拥有的统计表的更多信息:

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)

有两个索引:

  • 1 表示主键
  • 1 用于获取和汇总用户的统计信息

    INDEX [Select_UpdateUserStatNavigation] ON [dbo].[UserStat](
    [UserId] ASC,[Date] ASC) INCLUDE ([Id],[Hits],[Points], [LastHit],[Ip])
    

服务器是 VPS。日志和数据文件在同一个磁盘上。表中不涉及外键。

以下是我发现查询此表的所有 SELECT 查询:

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,更关心 UPDATE 的数量^^。

【问题讨论】:

  • 是批量更新还是每行更新类型。如果INSERT查询较少,那么更新查询怎么来更多。相同的记录是否更新了多次。是否有任何并发​​问题。至少您需要企业版。表上有索引吗?
  • 我们确实需要您的表的 DDL 来帮助解决这个问题。您还使用物理服务器还是虚拟服务器?您是否将日志和数据文件放在不同的磁盘上?您的表是否涉及任何外键关系?你有什么索引?
  • 感谢您的cmets,我在原始问题下方添加了一些信息。关于将日志和数据文件放在不同的磁盘上,当我开始遇到性能问题时,我才发现该信息:/。如果我没记错的话,我需要停止数据库来移动日志文件。有需要我会看看的。

标签: sql-server caching windows-server-2012 nosql


【解决方案1】:

这确实是一个非常简单且很好的示例,说明何时适合使用 NoSql 数据库。 NoSql 是为诸如此类的“网络规模”应用程序而创建的,其中数据的速度和数量远远超过了 SQL 数据库跟上的能力(关系 DBMS 的一大弱点)。

事实上,常规 SQL 不是您的方案的好选择。这有几个原因,包括:

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

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

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

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

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

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

鉴于上述情况,您的场景是何时应用 NoSql 解决方案的教科书示例。我可以推荐 Couchbase,这是一个非常快速的内存数据库,具有基于磁盘的存储(这一次性满足您的缓存和数据存储要求)。您也可以考虑将Elasticsearch 用于您的统计存储,因为它可以执行一些非常好的开箱即用的数据聚合。无论您选择哪种 NoSql 解决方案,您都将获得灵活的可扩展性和易于维护。我敢说你成为一名全职 DBA 的日子已经结束了。

【讨论】:

    【解决方案2】:

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

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

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

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

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

    您可以发布使用的选择查询吗?如果需要,我可以给出建议的索引。

    此外,您可能希望将单独的插入和更新过程替换为如下所示的 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)
    

    【讨论】:

    • 您好 DB101,是的,Id 是主键。是的,它是一个虚拟服务器。磁盘似乎也被 SQL Server 请求了。服务器有 8gb 的内存,SQL Server 消耗通常在 2.5 到 3gb 之间。大约有 1/2gb 的可用内存。我不知道索引中列的顺序有影响。如果我通常根据关闭日期 ([Date] >= @date) 获取最后一行,我应该将 [Date] DESC 放在索引声明中吗?
    • 如果日期是最具选择性的值,那么它应该放在索引的开头。我看到您正在使用 Datetime 列 - 因此,如果您也存储时间,即 dd mm yy hh mm ss 而不是 dd mm yy 00 00 00,那么您可以将 Date 作为索引的前导列这将是高度选择性的。
    • 我不明白你所说的“磁盘似乎也被 SQL Server 请求了”是什么意思。
    • 我通常查看资源监视器。对于磁盘,请求最多的文件是日志文件。凭记忆(我今晚可以确认数字),磁盘队列长度约为 0.20,每 5 秒,由于一些写入操作,它会跳到 1(如果我没记错的话,它在 mdf 文件上)。
    • OK - 这表明磁盘争用不是问题。您是否在同一台服务器上运行 IIS?还有什么是表和数据库大小。 @Vahid Farahmandian 可能是正确的,因为您可能需要升级版本。但是,在为您提供需要花钱的解决方案之前,我宁愿消除其他可能性。您没有在 SQL 上运行病毒扫描程序吗?
    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

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

      您应该从快捷版迁移到企业版或至少标准版(Express vs others)

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

      尝试重新调整表格列的大小,以便记录的单元格可以存储在更少的页面中,这将有助于加快更新过程(例如,如果您有一个包含 20 列的表格,并且您总是更新只需修复 5 个已知列,然后将这 5 个列与其他 15 个列分开。这可以帮助您将数据排列在更少的页面中,并且当您的页面更少时,您可以更快地找到您的记录。粗略这不是基于范式,但它可以帮助你的表现)

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

      关于您的主要问题,我需要了解更多关于您的 Stats 及其表格及其用法的信息。你的意思是sql server statistics,还是别的意思?

      【讨论】:

      • 我不同意。没有足够的信息供您提供这些建议。
      • 您指的是哪些建议?我还提供了一些一般提示,每个人都应该考虑,但对于他的主要问题,我要求他提供一些更详细的信息
      • 您从 Express 迁移到另一个版本的建议在许可条款方面可能会很昂贵,因为这甚至没有被确认为问题的原因。此外,禁用索引不会加快插入速度,因为它会导致表扫描。
      • @DB101 感谢您的 cmets。插入受索引影响。在每次插入之前,应该重新排列索引,以便将记录放在适当的位置,并且在某些情况下可能需要分页。当执行向表中插入大量记录时,这些过程可能会很昂贵。关于 Express,如您所知,Express 的功能有限,它不适合如此繁重的场景,因为它使用有限的内存,而且它不支持一些有用的功能等。这就是我在评论中建议它们的原因
      • @Tom741 是的,您可以通过多种方式找到内存瓶颈。检查此链接:mssqltips.com/sqlservertip/2304/… 也检查此链接:msdn.microsoft.com/en-us/library/ms190994.aspx
      猜你喜欢
      • 2011-03-19
      • 2018-02-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-19
      • 2018-05-28
      • 1970-01-01
      • 2011-11-14
      相关资源
      最近更新 更多