我目前在我的网站上遇到了性能问题。可以通过以下方式总结这种情况:
由于统计数据不像核心系统那么重要,我看到SQL Server经历了很多困难,我认为将这些统计表移到其他地方可能会很好。
主要问题是:处理以更新为主的统计数据的最佳方法是什么?这个想法也是只保留一台服务器。
我试着看看如何改善现状:
对于当前形势的任何相关想法将不胜感激 谢谢
以下是有关我所拥有的统计表的更多信息:
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用于获取和汇总用户的统计信息
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的数量^^。
答案 0 :(得分:2)
这是一个非常简单的好例子,适用于NoSql数据库。 NoSql是为" web-scale"诸如此类的应用程序,数据的速度和数量只会超过SQL数据库跟上的能力(关系型DBMS的一大弱点)。
实际上,常规SQL不适合您的场景。这有几个原因,包括:
现在,让我们看看你的特定用例:
大量更新事务,偶尔插入。大多数NoSql数据库平台使用Set
操作,根据需要更新或插入。每次都不需要运行两个语句。
单个主键。 NoSql数据库是键值存储,其中键(在本例中为UserId
)指向数据库中的单个记录。
简单的统计信息和索引。几个NoSql数据库提供内置的索引功能,有些甚至允许您对数据执行map-reduce以获取详细的统计信息。其他人自动进行数据聚合,您可以编写特殊查询来获取所需的数据。在这种情况下,您的" Stat Id"字段是无用的,可以删除(yay,更少的存储空间!)。
快速且可扩展。这是您无法与SQL数据库联系的内容。 NoSQL就是为此而设计的。
鉴于上述情况,您的方案是一个教科书示例,说明何时应用NoSql解决方案。我可以推荐Couchbase,这是一个非常快速的内存数据库,基于磁盘存储(一次性满足您的缓存和数据存储要求)。您也可以考虑将Elasticsearch用于统计信息存储,因为它可以开箱即用地执行一些非常好的数据聚合。无论您选择哪种NoSql解决方案,您都将获得灵活的可扩展性和易维护性。我敢说你成为一名全职DBA的日子将会结束。
答案 1 :(得分:1)
您能否确认ID是您的主键?如果是这样,那么这将是好的,因为它是单调增加的值并且对于插入物是有益的。我认为您的其他索引(出于更新目的)应为
INDEX [Select_UpdateUserStatNavigation] ON [dbo].[UserStat](
[UserId] ASC,[IP] ASC, [Date] ASC).
确保列在索引中从最具选择性到最少选择性排序。这可以加快更新,因为行可以更快地定位。我们稍后可以查看SELECT的索引。
BY 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)
答案 2 :(得分:0)
RavenDB在这种情况下非常容易启动和运行。您将获得快速写入和可能的快速读取。您也可以获得ACID或尽可能接近ACID。 RavenDB易于在MVC中连接。由于您拥有Mongo经验,因此文档的概念对您来说不应该是陌生的。在MVC应用程序中使用RavenDB C#客户端库,几个小时内就可以取得重大进展。请确保您了解这些限制。默认情况下,查询可能比更新后几微秒,并且像许多NoSql或CQRS解决方案一样,如果您清除查询的缓存,可能需要几分钟到几个小时才能完全重建缓存。
答案 3 :(得分:-1)
在遇到主要问题之前,必须进行一些更改:
您应该从Express版迁移到企业版或至少标准版(Express vs others)
由于您对数据进行了大量更新,因此应禁用索引(如果有)
尝试重新调整表格列的大小,这样您的记录单元格可以存储在较少的页面中,这将有助于您的更新过程加速(例如,如果您有一个包含20列的表格,并且您始终更新只修复了5个已知列,然后将这5列与其他15列分开。这可以帮助您在较少的页面中排列数据,当您的页面较少时,您可以更快地找到您的记录。粗略的这不是基于正常形式,但它可以帮助您的表现)
看看你的可用内存和CPU。这两者是表现的基石。
关于你的主要问题,我需要了解更多关于你的统计数据及其表格以及它的用法。你的意思是sql server statistics,还是你的意思?