带有 SQL 后端的高性能仪表板

Posted

技术标签:

【中文标题】带有 SQL 后端的高性能仪表板【英文标题】:High Performance Dashboard with SQL Backend 【发布时间】:2017-08-16 12:00:36 【问题描述】:

要求: 要在 ERP 主屏幕中显示的仪表板集。根据当前用户权限过滤数据。

截至目前:

高图表用于数据可视化。背景页面位于 C# .Net

问题:

每次用户更改过滤器时,它都会访问 Live DB 并获取数据。 每天早上,用户几乎在同一时间登录,所以不会有大量的请求同时发送到 SQL Server。这将导致性能问题。还有更多图表即将推出。

我们计划为数据实施 SQL 分析仪表板 Qubes。 有人可以建议这是否是正确的方法或建议任何其他更好的方法。? 更好的架构。

谢谢。

【问题讨论】:

即使有整个 SO 的经验,恐怕也不是那么容易回答的。您必须根据您的预算、商店中已有的技术、预期结果(例如报告数据是否需要可用于实时报告)以及您/您的团队围绕报告的技能进行更具体的说明/DW解决方案,有没有业务限制? 这么多因素。有多少用户?当前对加载时间有何影响?可接受的加载时间是多少?等等。查询是什么样的 - 是否有很多处理会减慢它的速度?不能说 SQL Analysis Dashboard Qubes 的有效性,但某种形式的预处理和/或缓存数据肯定是一个方向。 @BartoszX 我们商店没有可用的支持软件现在接受 Highcharts。建议使用实时数据,如果不是几个小时的旧数据也可以。我是这份工作的所有者。我真的是 .net 的初学者,并且在 SQL 方面有 4 年的经验。此外,我还有高级数据库管理员来帮助我进行性能调优,而且他擅长 DW 解决方案(SSAS)。限制:转到具有不同服务器配置的客户端。有些非常好,有些非常糟糕。 @jlbriggs 预计,最多 50 个用户可能会同时使用此页面。负载测试未完成。假设它会减慢 DB。因为我们从实际的事务表中获取数据。可接受的加载时间应该在 10 秒左右。很少有很多处理,很少是由于数据。我们真的很想知道是否有任何技术/方法。您能否详细说明推荐的确切预处理和/或缓存? 您是否已经分析过您的等待统计信息?您对哪些等待类型有疑问? (试图了解它是行锁的硬件) 【参考方案1】:

所以首先要做的事情是——你需要知道真正的问题是什么,如果资源的话——只需根据需要添加更多资源并尝试优化你的代码。但根据我的经验,情况并非如此——我相信你有一个经典的行锁示例,而你有并发事务试图访问相同的数据。如果您对deadlocks 有疑问,您可能想尝试使用snapshot transaction isolation level,这只是并发读取,您可能想要创建一个虚拟replication 并将最重的访问对象复制到单独的只读数据库中,如果您已经使用log shipping 获取log backups 并在可能的情况下从副本中读取,这对我来说也很划算。

如果您愿意为正确修复它付出一些努力,我建议您考虑使用Data Warehouse 解决方案并将您的应用程序/报告链接到它。

关于 data cubes 和/或 SSAS 解决方案,这很有帮助,但要做到这一点,您会意识到无论如何您都需要 DW,并且要从中看到真正的优势,您的客户将不得不在很多方面聚合不仅仅是一个简单的“刷新报告以下载今天的数据”。

您需要做很多工作,我建议您先分析等待统计信息,以了解您现在的确切位置以及真正的问题是什么。作为礼物,请找到以下代码以获取这些统计信息:

DECLARE @Wait_Types_Excluded TABLE([wait_type] nvarchar(60) PRIMARY KEY);

INSERT INTO @Wait_Types_Excluded([wait_type]) VALUES

 (N'BROKER_EVENTHANDLER'), (N'BROKER_RECEIVE_WAITFOR'), (N'BROKER_TASK_STOP'), (N'BROKER_TO_FLUSH'), (N'BROKER_TRANSMITTER')
,(N'CHECKPOINT_QUEUE'), (N'CHKPT'), (N'CLR_AUTO_EVENT'), (N'CLR_MANUAL_EVENT'), (N'CLR_SEMAPHORE') ,(N'DIRTY_PAGE_POLL')
,(N'DISPATCHER_QUEUE_SEMAPHORE'), (N'EXECSYNC'), (N'FSAGENT'), (N'FT_IFTS_SCHEDULER_IDLE_WAIT'), (N'FT_IFTSHC_MUTEX')
,(N'KSOURCE_WAKEUP'), (N'LAZYWRITER_SLEEP'), (N'LOGMGR_QUEUE'), (N'MEMORY_ALLOCATION_EXT'), (N'ONDEMAND_TASK_QUEUE')
,(N'PREEMPTIVE_XE_GETTARGETSTATE'), (N'PWAIT_ALL_COMPONENTS_INITIALIZED'), (N'PWAIT_DIRECTLOGCONSUMER_GETNEXT')
,(N'QDS_PERSIST_TASK_MAIN_LOOP_SLEEP'), (N'QDS_ASYNC_QUEUE'), (N'QDS_CLEANUP_STALE_QUERIES_TASK_MAIN_LOOP_SLEEP')
,(N'QDS_SHUTDOWN_QUEUE'), (N'REDO_THREAD_PENDING_WORK'), (N'REQUEST_FOR_DEADLOCK_SEARCH'), (N'RESOURCE_QUEUE')
,(N'SERVER_IDLE_CHECK'), (N'SLEEP_BPOOL_FLUSH'), (N'SLEEP_DBSTARTUP'), (N'SLEEP_DCOMSTARTUP'), (N'SLEEP_MASTERDBREADY')
,(N'SLEEP_MASTERMDREADY'), (N'SLEEP_MASTERUPGRADED'), (N'SLEEP_MSDBSTARTUP'), (N'SLEEP_SYSTEMTASK'), (N'SLEEP_TASK')
,(N'SLEEP_TEMPDBSTARTUP'), (N'SNI_HTTP_ACCEPT'), (N'SP_SERVER_DIAGNOSTICS_SLEEP'), (N'SQLTRACE_BUFFER_FLUSH')
,(N'SQLTRACE_INCREMENTAL_FLUSH_SLEEP'), (N'SQLTRACE_WAIT_ENTRIES'), (N'WAIT_FOR_RESULTS'), (N'WAITFOR')
,(N'WAITFOR_TASKSHUTDOWN'), (N'WAIT_XTP_RECOVERY'), (N'WAIT_XTP_HOST_WAIT'), (N'WAIT_XTP_OFFLINE_CKPT_NEW_LOG')
,(N'WAIT_XTP_CKPT_CLOSE'), (N'XE_DISPATCHER_JOIN'), (N'XE_DISPATCHER_WAIT'), (N'XE_TIMER_EVENT')
,(N'DBMIRROR_DBM_EVENT'), (N'DBMIRROR_EVENTS_QUEUE'), (N'DBMIRROR_WORKER_QUEUE'), (N'DBMIRRORING_CMD'),
(N'HADR_CLUSAPI_CALL'), (N'HADR_FILESTREAM_IOMGR_IOCOMPLETION'), (N'HADR_LOGCAPTURE_WAIT'),
(N'HADR_NOTIFICATION_DEQUEUE'), (N'HADR_TIMER_TASK'), (N'HADR_WORK_QUEUE');

SELECT
 [Approx_Wait_Stats_Restart_Date] = CAST(DATEADD(minute, -CAST((CAST(ws.[wait_time_ms] as decimal(38,18)) / 60000.0) as int), SYSDATETIME()) as smalldatetime)
,[SQL_Server_Last_Restart_Date] = CAST(si.[sqlserver_start_time] as smalldatetime)
FROM sys.dm_os_wait_stats ws, sys.dm_os_sys_info si
WHERE ws.[wait_type] = N'SQLTRACE_INCREMENTAL_FLUSH_SLEEP';

SELECT TOP 25
 ws.[wait_type]
,[Total_Wait_(s)]         = CAST(SUM(ws.[wait_time_ms]) OVER (PARTITION BY ws.[wait_type]) / 1000.0 as decimal(19,3))
,[Resource_(s)]           = CAST(SUM([wait_time_ms] - [signal_wait_time_ms]) OVER (PARTITION BY ws.[wait_type]) / 1000.0 as decimal(19,3))
,[Signal_(s)]             = CAST(SUM(ws.[signal_wait_time_ms]) OVER (PARTITION BY ws.[wait_type]) / 1000.0 as decimal(19,3))
,[Avg_Total_Wait_(ms)]    = CASE WHEN SUM(ws.[waiting_tasks_count]) OVER (PARTITION BY ws.[wait_type]) > 0 THEN SUM(ws.[wait_time_ms]) OVER (PARTITION BY ws.[wait_type])/ SUM(ws.[waiting_tasks_count])OVER (PARTITION BY ws.[wait_type]) END
,[Avg_Resource_Wait_(ms)  = CASE WHEN SUM(ws.[waiting_tasks_count]) OVER (PARTITION BY ws.[wait_type]) > 0 THEN SUM(ws.[wait_time_ms] - ws.[signal_wait_time_ms]) OVER (PARTITION BY ws.[wait_type])/ SUM(ws.[waiting_tasks_count]) OVER (PARTITION BY ws.[wait_type])END
,[Avg_Signal_Wait_(ms)]   = CASE WHEN SUM(ws.[waiting_tasks_count]) OVER (PARTITION BY ws.[wait_type])> 0 THEN SUM(ws.[signal_wait_time_ms]) OVER (PARTITION BY ws.[wait_type])/ SUM(ws.[waiting_tasks_count]) OVER (PARTITION BY ws.[wait_type])END
,[Waiting_Tasks_QTY]      = SUM(ws.[waiting_tasks_count]) OVER (PARTITION BY ws.[wait_type])
,[Percent_of_Total_Waits_Time]  = CAST(CAST(SUM(ws.[wait_time_ms]) OVER (PARTITION BY ws.[wait_type]) as decimal) / CAST(SUM(ws.[wait_time_ms]) OVER() as decimal) * 100.0 as decimal(5,2))
,[Percent_of_Total_Waits_QTY]     = CAST(CAST(SUM(ws.[waiting_tasks_count]) OVER (PARTITION BY ws.[wait_type]) as decimal)/ CAST(SUM(ws.[waiting_tasks_count]) OVER() as decimal) * 100.0 as decimal(5,2))
FROM sys.dm_os_wait_stats ws
LEFT JOIN @Wait_Types_Excluded wte ON ws.[wait_type] = wte.[wait_type]
WHERE   wte.[wait_type] IS NULL
AND    ws.[waiting_tasks_count] > 0
ORDER BY [Total_Wait_(s)] DESC;  

【讨论】:

以上是关于带有 SQL 后端的高性能仪表板的主要内容,如果未能解决你的问题,请参考以下文章

阿里P8架构师谈:应用后端+移动端的性能优化指标,以及性能优化方法

阿里P8架构师谈:应用后端+移动端的性能优化指标,以及性能优化方法

在使用SQL Server后端的Microsoft Access中使用查找对话框时性能下降

如何在 SQL 中以高性能的方式使用 PARTITION BY 获取最新记录?

高性能安全式SQL拼接

性能调整实体框架查询