用于处理10亿行和计数的数据库设计


10

我们以大约5000 pr的速率接收实时GPS数据。分钟(来自4个TCP服务器)。每个服务器使用单个连接来插入数据,并在两次插入之间缓冲数据。每隔15分钟左右,服务就会获取一次此数据,并将其处理为行程。生成行程后,仅当用户希望在地图上查看路线时,实际的GPS数据通常并不那么重要。

问题是数据库似乎在努力跟上插入数据的速度。有时,当负载增加时,插入时间突然急剧增加(> 30秒),这又使更多数据得以缓冲,从而导致更大的插入量和更长的插入时间。

我希望能对当前的设计发表一些评论,以及一些我们必须提高性能的想法,并回答我们的一些问题-以及人们可能拥有的其他技巧!

当前设计

当前将数据分为代表一周的表格,并且将早于一年的数据存档到辅助数据库中。整个事物在可编辑视图中连接在一起,该视图用于插入和读取。

桌子设计

  • ID(PK,唯一标识符)
  • DeviceId(FK,int)
  • PersonId(FK,int)
  • VehicleId(FK,int)
  • TokenId(FK,int)
  • UtcTime(PK,datetime2(3))
  • 纬度(浮动)
  • 经度(浮点)
  • 速度(smallint)
  • 标题(smallint)
  • 卫星(tinyint)
  • IOData(varbinary(100))
  • IgnitionState(tinyint)
  • UserInput(tinyint)
  • CreateTimeUtc(datetime2(3))

指标

  • DeviceId_CreateTimeUtc_Desc
  • DeviceId_UtcTime_Desc(集群)
  • PersonId_UtcTime_Desc
  • TokenId_UtcTime_Desc
  • VehicleId_UtcTime_Desc

当前每个星期(包括索引)占用大约10 GB的数据,并且主数据库中目前大约有300 GB的数据。

主数据库中的数据表具有自己的文件组,其中包含1个文件,但它与主数据库中的所有其他表位于同一磁盘上。辅助数据库位于不同的磁盘上,但位于同一台计算机上。

我认为,当使用新的表分区(一周)时,我们每周还会运行一次索引重建作业。不执行收缩。

该计算机是具有12 GB内存的8核HP,并且包含主数据库的磁盘正在运行RAID 10。

主意

  • 将存储在主数据库中的数据量限制为最多1个月。至少它可以使数据库更易于管理以进行备份/还原,但是通过这样做,我们可以期望看到性能的提高吗?
  • 在文件组中为当前数据创建2个文件,并将它们分发到2个不同的物理分区中
  • 创建保存当前数据的主从数据库,因此在不同数据库上执行插入和读取操作
  • 将当前数据文件放在SSD磁盘上(镜像是否会对SSD磁盘产生任何性能差异?)

请让我知道是否需要更多信息。影响性能的因素很多,并且可能有许多调整方法。


评论不作进一步讨论;此对话已转移至聊天
保罗·怀特9

Answers:


8

每分钟5000次插入是每秒约83次插入。使用5个索引,即每秒插入400条物理行。如果工作负载在内存中,即使是最小的服务器也不会造成问题。即使这是我想到的效率最低的逐行插入。从CPU的角度来看,每秒83个琐碎的查询并不有趣。

可能您是磁盘绑定的。您可以通过查看等待统计信息或来验证这一点STATISTICS IO

您的查询可能涉及很多不同的页面,因此缓冲池没有足够的空间容纳所有页面。这会导致频繁的页面读取以及可能的随机磁盘写入。

想象一下一个表,由于键不断增加,您只能在表的最后插入。工作集将是一页:最后一页。这将产生顺序的IO,以及延迟写入器或检查点进程将表的“结尾”写入磁盘。

想象一下一个带有随机放置的插入的表(经典示例:guid键)。在这里,所有页面都是工作集,因为每次插入都会触摸到一个随机页面。IO是随机的。对于工作集,这是最坏的情况。

你在中间 您的索引具有结构(SomeValue, SequentialDateTime)。第一个组件部分随机化第二个组件提供的顺序。我想“ SomeValue”有很多可能的值,因此您的索引中有许多随机放置的插入点。

您说每周将数据分成10GB表。这是一个很好的起点,因为工作集现在受到10GB的限制(不管您可能执行的任何读取操作)。但是,有了12GB的服务器内存,所有相关页面都不可能保留在内存中。

如果您可以减少每周“分区”的大小或稍微增加服务器内存,则可能很好。

我希望插入在一周的开始比结束时更快。您可以通过运行具有一定数据大小的基准测试并逐步减少服务器内存,直到看到性能下降,在开发服务器上测试该理论。

现在,即使所有读取和写入都适合内存,您仍可能具有随机的脏页刷新IO。摆脱这种情况的唯一方法是写入索引中位于同一位置的位置。如果您完全可以将索引转换为使用(更多)顺序键,将会很有帮助。

作为一种快速解决方案,我将在客户端和主表之间添加一个缓冲层。也许累积15分钟的写入临时表并定期刷新它。这样可以消除负载峰值,并使用更有效的计划来写入大表。


1
@usr感谢您提供非常全面且解释清楚的答案!我们实际上已经讨论了增加服务器内存,却不知道它会产生多大的影响-但现在我们确实有一个非常令人信服的理由:)您是对的,“ SomeValue”部分随机化了插入点-可能有大约10000个设备ID。关于登台表,您的建议是一个没有任何索引的表,然后是每X分钟插入一次主表的作业吗?
桑德加2014年

@usr注册 如果您建议将聚簇索引转换为顺序索引,我们可以添加一个自动增量。标识列(整数),并仅将聚簇索引更改为此列,以使其保持顺序?它在表之间不是唯一的,但是只要主键是,我们就可以了。
桑德加2014年

1
如果登台表很小,并且您的查询可以使用它,那么您根本不需要编制索引。但是你可以。一种策略是将CI放在“身份”列上(如您所说)。这可以解决CI是否较大而其他索引较小的问题。由于配置项是连续写入,因此它们对问题的影响要小得多。如果存在有意义的大小差异,则此策略最成功。另一个想法是每天有一张桌子。也许每月合并。
usr 2014年

好的,我们研究了为CI创建身份列的问题,但不幸的是,在分区视图上是不可能的(不允许身份列,默认值且所有列都必须包含在insert中)。也许分开的视图是一个选择不佳的设计,尽管它是由顾问推荐的
Sondergard 2014年

2
但是,严重的是,对于面临相同问题的任何人,如果您有大量的写入操作而只有少量的读取操作,那么您真的想在末尾追加内容并延迟任何索引编制。另一方面,如果您想快速读取并且不关心插入需要多长时间,则需要一个聚集索引。
tiktak 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.