从c#到SQL Server的批量插入策略

从c#到SQL Server的批量插入策略,c#,sql-server,bulkinsert,sqlbulkcopy,C#,Sql Server,Bulkinsert,Sqlbulkcopy,在我们当前的项目中,客户将向我们的系统发送复杂/嵌套消息的集合。这些信息的频率约为1000-2000 msg/秒 这些复杂对象包含事务数据(要添加)以及主数据(如果找不到,将添加主数据)。但客户传递的不是主数据的ID,而是“名称”列 系统检查这些名称是否存在主数据。如果找到,它将使用数据库中的ID,否则首先创建此主数据,然后使用这些ID 解析主数据ID后,系统将事务数据插入SQL Server数据库(使用主数据ID)。每条消息的主实体数约为15-20 以下是我们可以采取的一些策略 我们可以首先从

在我们当前的项目中,客户将向我们的系统发送复杂/嵌套消息的集合。这些信息的频率约为1000-2000 msg/秒

这些复杂对象包含事务数据(要添加)以及主数据(如果找不到,将添加主数据)。但客户传递的不是主数据的ID,而是“名称”列

系统检查这些名称是否存在主数据。如果找到,它将使用数据库中的ID,否则首先创建此主数据,然后使用这些ID

解析主数据ID后,系统将事务数据插入SQL Server数据库(使用主数据ID)。每条消息的主实体数约为15-20

以下是我们可以采取的一些策略


  • 我们可以首先从C#代码解析主ID(如果找不到主数据,则插入主数据),并将这些ID存储在C#缓存中。解析所有ID后,我们可以使用
    SqlBulkCopy
    类批量插入事务数据。我们可以点击数据库15次以获取不同实体的ID,然后再点击数据库一次以插入最终数据。我们可以使用相同的连接,在完成所有这些处理后,它将关闭

  • 我们可以一次性将所有这些包含主数据和事务数据的消息发送到数据库(以多个TVP的形式),然后在存储过程中,首先为缺少的主数据创建主数据,然后插入事务数据

  • 在这个用例中,有人能建议最好的方法吗

    由于一些隐私问题,我无法共享实际的对象结构。但是这里是一个假设的对象结构,它非常接近我们的业务对象

    其中一条消息将包含来自不同供应商的关于一种产品(其主数据)及其价格详细信息(交易数据)的信息:

    主数据(如果找不到则需要添加)

    产品名称:ABC、产品类别:XYZ、制造商:XXX和其他一些详细信息(属性数量在15-20之间)

    交易数据(将始终添加)

    供应商名称:A,标价:XXX,折扣:XXX

    供应商名称:B,标价:XXX,折扣:XXX

    供应商名称:C,标价:XXX,折扣:XXX

    供应商名称:D,标价:XXX,折扣:XXX

    对于属于一个产品的消息,有关主数据的大多数信息将保持不变(并且更改频率会降低),但事务数据将始终波动。因此,系统将检查系统中是否存在产品“XXX”。如果没有,则检查本产品中提到的“类别”是否存在。如果没有,它将为类别插入新记录,然后为产品插入新记录。这将用于制造商和其他主数据

    多个供应商将同时发送关于多个产品(2000-5000)的数据


    因此,假设我们有1000家供应商,每个供应商发送10-15种不同产品的数据。每2-3秒,每个供应商都会向我们发送这10种产品的价格更新。他可能会开始发送有关新产品的数据,但不会非常频繁。

    从数据库的角度来看,没有比批量插入(例如从csv文件)更快的事情了。最好是尽快批量处理所有数据,然后使用存储过程进行处理


    C层只会减慢过程,因为C层和SQL层之间的所有查询都会比SQL Server直接处理的查询慢数千倍。

    您最好还是使用您的#2想法(即,使用多个TVP一次性将所有15-20个实体发送至DB,并作为一组多达2000条消息进行处理)

    在应用层缓存主数据查找并在发送到DB之前进行翻译听起来不错,但遗漏了一些东西:

  • 无论如何,您必须点击数据库才能获得初始列表
  • 无论如何,您必须点击数据库才能插入新条目
  • 在字典中查找要替换为ID的值正是数据库所做的事情(假设每个名称到ID查找都有一个非聚集索引)
  • 频繁查询的值将在缓冲池(内存缓存)中缓存其数据页
  • 为什么要在应用层复制数据库层已经提供和正在发生的内容,特别是在以下情况下:

    • 15-20个实体最多可以有20k条记录(这是一个相对较小的数字,特别是考虑到非聚集索引只需要两个字段:
      Name
      ID
      ,当使用100%填充因子时,这两个字段可以将多行打包到单个数据页中)
    • 并非所有20k条目都是“活动”或“当前”的,因此您不必担心缓存所有条目。因此,无论当前值是什么,都将很容易识别为被查询的值,而那些数据页(可能包括一些非活动条目,但没有什么大不了的)将被缓存在缓冲池中
    因此,您不必担心旧条目的老化,也不必担心由于可能会更改值(即针对特定的
    ID
    更新的
    Name
    )而强制任何密钥过期或重新加载,因为这是自然处理的

    是的,内存缓存是一项非常好的技术,可以极大地加快网站的速度,但这些场景/用例是针对非数据库进程在纯只读目的下一遍又一遍地请求相同的数据的情况。但这种特定场景中,数据被合并,查找值列表可能会频繁更改(更多的是由于新条目而不是由于更新条目)


    总而言之,选项2是一条可行之路。我已经多次使用这种技术并取得了很大的成功,尽管没有使用15个TVP。可能需要进行一些优化/调整