/
Database Index Choices vs Throughput

Database Index Choices vs Throughput

Observations/Conclusions

  1. There are some hard trade-offs here: Queue Performance, Delete Performance, Processing Performance, and Query Performance. According to the numbers, it appears a cluster key of ExternalDeviceId, CreatedDateTime, MessageId (Test 5) is the best choice, but not by a significant amount. Further testing will be required to see if this choice performs well enough when Querying for messages, since it has no other indexes and requires a clustered indexed range scan for every query.
  2. Clustering by ExternalDeviceId, CreatedDateTime, MessageId results in the smallest database size of all the options because it has the fewest indexes.
  3. We expected having a separate QueuedMessages table (to store new messages entering the system) would provided the best solution with respect to table locking during database maintenance (index rebuilds, etc), however; we did not see much improvement in this regard. Further testing on this solution should be investigated when we have more time.
  4. The database log file was by far the hardest hit (by a factor of 3-4) from the 3 database files that make up the Queue database. We recommend separating the 3 database files on separate drives and IO channels with the LOG file being on the fastest disk. Additionally, backups should be performed to a separate disk so there is no disk contention during the backup.
  5. There is a huge advantages from a scalability standpoint to having the database run on SSDs which is likely not possible running on vms in the data center. The machine running the database on SSDs exhibited none of the issues seen on the disk based solution with respect to timeouts during database maintenance.
  6. The biggest outstanding risk is how the database performance over time with 100s of million records. On the disk based system we see dramatic performance degradation during database maintenance activities such as backup and index maintenance. The concern here is that if HL7 messages cannot be stored in a timely fashion that HL7 interfaces will timeout.

Test Scenario

  1. Chicago Message set x 2 streams = 200K messages
  2. ThroughputTest.cxn (attached) Database Index Choices vs Throughput
  3. Pause all queues except the Branch Channel measure total time to Queue all messages (Queue Time)
  4. Un-Pause all channels. Measure total time to Process all Messages (Processing Time)
  5. Execute the delete command below, but WITHOUT the DROPCLEANBUFFERS call.
  6. Query for Patient with ID = 105160 from UI
  7. Execute the delete command below, but WITH the DROPCLEANBUFFERS call.
  8. Query for Patient with ID = 105160 from UI
  9. Clear Queues after each test using clear command below
  10. Execute a database backup from the Client UI to clear log, etc

Test

#

RevComputer

Clustered
Index

Other
Indexes

HL7Spy Send
(secs)

Queue Time
(secs)

Processing Time
(secs)

Delete #5
(msecs)

Query #6
(msec)

Delete #7
(msecs)

Query #8
(msecs)

Notes
01849c28762f0JRMessageIdOne for each fields in Queue worklist1383624113408813759995Baseline database
01849c28762f0NJMessageIdOne for each fields in Queue worklist6217921593750169Baseline database
1527c363e32dcJR

ExternalDeviceId, CreatedDateTime

Filtered index on Queued messages

13627843418881112801271

Use CreatedDateTime in queries instead of ModifiedDateTime

1527c363e32dcNJ

ExternalDeviceId, CreatedDateTime

Filtered index on Queued messages

60146215932828753

Use CreatedDateTime in queries instead of ModifiedDateTime

208df4637d36eJR

MessageId

Filtered index on Queued messages

Fully covered index

1324244881887831431799

 

208df4637d36eNICK

MessageId

Filtered index on Queued messages

Fully covered index

6615422411259485107

 

32854c3dfd94dJRNO Clustered index

Filtered index on Queued messages

Fully covered index

119304395338322106322111 
4937eae4ff5acJRMessageId

Filtered index on Queued messages

Fully covered index

118283380217718358803 
4937eae4ff5acNJMessageId

Filtered index on Queued messages

Fully covered index

6014424723764273131 
53261f232cd21JRExternaDeviceId, ModifiedDateTime, MesssageIdNo CorrelationKey120230403807510651665 
53261f232cd21NJExternaDeviceId, ModifiedDateTime, MesssageIdNo CorrelationKey601242472108382134 
63261f232cd21JRMessageIdOne fat index with everything11722537517016614572840 
63261f232cd21NJMessageIdOne fat index with everything601222267562307116 
7 4d923df7e6daJR

ExternalDeviceId,
CreatedDateTime,
MessageId

No query indexes1152194011251479881206 
7 4d923df7e6daNJ

ExternalDeviceId,
CreatedDateTime,
MessageId

No query indexes611262287414713446 
8 8f4a708a9733 

Separate QueuedMessage table

No query indexes11518043513021116522583 
9 5124566bec79 

Separate QueuedMessage table. MessageData and QueueData in same File Group

No query indexes1181924451279315822945 

* Calculated from:

select DATEDIFF(ss,(select top 1 ModifiedDateTime from Message order by MessageId)
                                ,(select top 1 ModifiedDateTime from Message order by MessageId desc))

** Calculated from the following where {0} = the ExternalDeviceId of one of the non-branch devices found in SELECT * FROM MessageStatistic

DECLARE @nonBranchDeviceId int
set @nonBranchDeviceId = {0}
select DATEDIFF(ss,(select top 1 ModifiedDateTime from Message where ExternalDeviceId = 2 order by MessageId)
                                ,(select top 1 ModifiedDateTime from Message where ExternalDeviceId = 2 order by MessageId desc))

** Command for Delete:

DBCC DROPCLEANBUFFERS
SET STATISTICS IO ON

DECLARE @diff int,
        @startDate DateTime,
        @olderThan DateTime,
        @timer DateTime,
        @nonBranchDeviceId int

set @nonBranchDeviceId = {0}
select top 1 @startDate = CreatedDateTime from Message where ExternalDeviceId=@nonBranchDeviceId order by MessageId
select @diff = DATEDIFF(SECOND, @startDate, (select top 1 ModifiedDateTime from Message where ExternalDeviceId=@nonBranchDeviceId order by MessageId desc))

set @olderThan = DATEADD(SECOND, @diff/10, @startDate)
set @timer = SYSDATETIME()
delete TOP(5000) from Message where ExternalDeviceId = @nonBranchDeviceId AND QueueTypeCode != 'Q' AND ModifiedDateTime < @olderThan
PRINT 'Delete Time: ' + CAST(DATEDIFF(millisecond,@timer,SYSDATETIME()) as varchar(255))

*** Query command

DECLARE @timer DATETIME,
        @nonBranchDeviceId int

set @timer = SYSDATETIME()
set @nonBranchDeviceId = {0}

SELECT TOP(100) * from Message
    where ExternalDeviceId = @nonBranchDeviceId and PatientId = '105160' order by CreatedDateTime desc, MessageId desc
PRINT 'Query Time: ' + CAST(DATEDIFF(millisecond,@timer,SYSDATETIME()) as varchar(255))

*** Clear command

IF  EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'[dbo].[FK_Message_MessageData]') AND parent_object_id = OBJECT_ID(N'[dbo].[Message]'))
ALTER TABLE [dbo].[Message] DROP CONSTRAINT [FK_Message_MessageData]

TRUNCATE TABLE Message
TRUNCATE TABLE MessageData
TRUNCATE TABLE ProcessingHistory
TRUNCATE TABLE MessageStatistic
TRUNCATE TABLE MessageStatisticHistory
CHECKPOINT

Test numberQuery typeMessage Statistics
0DELETE

Table 'Message'. Scan count 1, logical reads 144794, physical reads 164, read-ahead reads 2905, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 6, logical reads 10529, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

0QUERY

Table 'Message'. Scan count 1, logical reads 1724, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

1DELETE

Table 'Message'. Scan count 1, logical reads 81811, physical reads 3, read-ahead reads 2548, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 3, logical reads 10254, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

1QUERYTable 'Message'. Scan count 1, logical reads 629, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
1 NJDELETE

Table 'Message'. Scan count 1, logical reads 61359, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 3, logical reads 10254, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

2DELETE

Table 'Message'. Scan count 1, logical reads 46063, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 3, logical reads 10254, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

2QUERY

Table 'Message'. Scan count 1, logical reads 814, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

2 NJDELETE

Table 'Message'. Scan count 9, logical reads 65335, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 3, logical reads 10254, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

2 NJQUERYTable 'Message'. Scan count 1, logical reads 5814, physical reads 2, read-ahead reads 3214, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
3DELETE

Table 'Message'. Scan count 1, logical reads 67012, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 4, logical reads 10332, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

3QUERY

Table 'Message'. Scan count 1, logical reads 900, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

4DELETE

Table 'Message'. Scan count 1, logical reads 81452, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 5, logical reads 10370, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

4QUERYTable 'Message'. Scan count 1, logical reads 1724, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
4 NJDELETE

Table 'Message'. Scan count 1, logical reads 46286, physical reads 4, read-ahead reads 1, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 3, logical reads 10262, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

4 NJQUERY 
5DELETE

Table 'Message'. Scan count 1, logical reads 30780, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 2, logical reads 10151, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

5QUERY

Table 'Message'. Scan count 1, logical reads 1399, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

5 NJDELETE

Table 'Message'. Scan count 1, logical reads 30778, physical reads 0, read-ahead reads 234, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 2, logical reads 10151, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

5 NJQUERY 
6DELETE

Table 'Message'. Scan count 1, logical reads 46783, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 2, logical reads 10151, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

6QUERYTable 'Message'. Scan count 1, logical reads 4881, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
6 NJDELETE

Table 'Message'. Scan count 1, logical reads 58852, physical reads 55, read-ahead reads 253, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 2, logical reads 10151, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

6 NJQUERYTable 'Message'. Scan count 1, logical reads 5209, physical reads 0, read-ahead reads 585, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
7DELETE

Table 'Message'. Scan count 1, logical reads 30848, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 2, logical reads 10172, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

7 NJDELETE

Table 'Message'. Scan count 1, logical reads 30748, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 2, logical reads 10165, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

7 NJQUERYTable 'Message'. Scan count 1, logical reads 1146, physical reads 3, read-ahead reads 1495, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.