Sql server 2008 从.NET代码插入SQL Server表的最快方法?

Sql server 2008 从.NET代码插入SQL Server表的最快方法?,sql-server-2008,sqlbulkcopy,Sql Server 2008,Sqlbulkcopy,最快的方法是什么: 一个表,没有我不能预先填充的引用(即,那里有一个引用键,但我已经填充了所有数据) 大量的数据。我们谈论的是每天数亿行,通过API动态地输入 在近乎实时的情况下,必须/应该尽快处理请求(即,每天不向文件中写入一个以供上传)。2秒是正常的最大延迟 用于数据/应用程序和SQL Server的独立计算机 我现在做的是: 将最多32*1024行聚合到一个数组中,然后将其排队 以2-3个线程读取队列。使用SqlBulkCopy插入数据库 我每秒导入大约60k-75k行,这还不够,

最快的方法是什么:

  • 一个表,没有我不能预先填充的引用(即,那里有一个引用键,但我已经填充了所有数据)
  • 大量的数据。我们谈论的是每天数亿行,通过API动态地输入
  • 在近乎实时的情况下,必须/应该尽快处理请求(即,每天不向文件中写入一个以供上传)。2秒是正常的最大延迟
  • 用于数据/应用程序和SQL Server的独立计算机
我现在做的是:

  • 将最多32*1024行聚合到一个数组中,然后将其排队
  • 以2-3个线程读取队列。使用SqlBulkCopy插入数据库
我每秒导入大约60k-75k行,这还不够,但非常接近。我想打25万排

到目前为止,还没有真正使用。我得到20%的时间“网络I/O”块,有一个核心80%的CPU端负载。光盘的输出容量为7mb-14mb,大部分处于空闲状态。6只猛禽组成的RAID 10上的平均队列长度为。。。。0.25

有人知道如何加速吗?更快的服务器(到目前为止,它是虚拟的,8gb ram,4核,数据的物理磁盘传输)


增加一些澄清:

  • 这是2008 R2服务器上的2008 R2企业SQL Server。机器有4个内核,8gb内存。全部为64位。80%的平均负载来自这台机器,显示大约20%的cpu负载
  • 该表很简单,没有主键,只有一个关系引用(instrument reference)上的索引和一个唯一的(在一组工具中,因此这不是强制的)时间戳
  • 表中的字段是:时间戳、仪器参考(无强制外键)、数据类型(char 1,表示发布数据的一个字符)、价格(double)和数量(int)。正如你所看到的,这是一张非常薄的桌子。所讨论的数据是金融工具的勾选数据
  • 问题还涉及硬件等方面——主要是因为我没有看到真正的瓶颈。我正在插入多个事务,这给了我好处,但只是一个小好处。磁盘、CPU没有显示明显的负载,网络io等待很高(300毫秒/秒,目前为30%),但这是在同一个虚拟化平台上运行的,该平台运行两台服务器的JSUT,并且有足够的内核来运行所有服务器。我非常愿意“购买另一台服务器”,但我想首先确定瓶颈。。。。特别是在一天结束时,我没有抓住瓶颈是什么。日志记录与此无关-批量插入不会作为数据(无聚集索引)进入数据日志
垂直分区会有帮助吗?例如,通过一个字节(tinyint)将仪器世界划分为16个表,这样我就可以同时进行16次插入了?由于数据实际上来自不同的交换,我可以为每个交换创建一个分区。这将是一个自然分割场(实际上在仪器中,但我可以在这里复制这个数据)


进一步澄清:速度更高(90k),现在明显受到机器间网络IO的限制,这可能是VM交换

我现在要做的是每32k行进行一次连接,建立一个临时表,用SqlBUlkdCopy插入其中,然后使用一条sql语句复制到主表,从而最大限度地减少主表上的任何锁定时间


大多数等待时间现在仍在网络IO上。似乎我遇到了一些问题,VM是明智的。将在未来几个月转向物理硬件;)

您可以使用水平分区吗? 见:

您可能还想看看这个问题,并可能更改恢复模型:

一些问题: 您使用的是哪个版本的SQL Server

为什么一个核心是80%?这可能是瓶颈,因此可能值得调查


您使用的是什么操作系统,它是64位的吗?

表中有没有可以不用的索引?编辑:打字时询问


是否可以将价格转换为整数,然后除以1000或其他任何查询?

您是否尝试将pk添加到表中?这能提高速度吗

还有一种基于集合的方法可以使用理货表从中导入csv数据(接近底部,需要免费注册,但值得)


您可能想尝试一下并测试它的性能。。。有一个小的理货台,理货台索引正确。

如果你每秒管理70k行,到目前为止你非常幸运。但我怀疑这是因为你有一个非常简单的模式

我真不敢相信你会问我这种负担

  • 虚拟服务器
  • 单阵列
  • SATA磁盘
网络和CPU是共享的,IO是受限的:您不能使用所有资源。 您看到的任何负载统计信息都不是很有用。我怀疑您看到的网络负载是两个虚拟服务器之间的流量,如果您解决此问题,您将受到IO限制

在我继续之前,读一下这个。他没有使用虚拟盒子

下面是我将要做的,假设您想要提升卷,就没有SAN和灾难恢复功能

  • 购买2台大型物理服务器,CPU RAM类,最大RAM,go x64安装
  • 磁盘+控制器=最快的磁盘轴,最快的SCSI。还是一个用石头砸的大家伙
  • 1000MB+NIC
  • RAID 10带有6-10磁盘,仅用于数据库的一个日志文件
  • 数据文件的剩余磁盘RAID 5或RAID 10

作为参考,我们的峰值负载是每小时1200万行(16核、16GB、SAN、x64),但负载很复杂。我们的容量不足。

从我在这里读到的答案来看,您似乎真的遇到了硬件问题,而不是代码问题。理想情况下,您可以通过提供更多的磁盘I/O或网络带宽,或者在托管数据库的同一虚拟机上运行该程序来提高性能

但是我不想
CREATE TYPE item_drop_bulk_table_rev4 AS TABLE (
    item_id BIGINT,
    monster_class_id INT,
    zone_id INT,
    xpos REAL,
    ypos REAL,
    kill_time datetime
)
CREATE PROCEDURE insert_item_drops_rev4
    @mytable item_drop_bulk_table_rev4 READONLY
AS

INSERT INTO item_drops_rev4 
    (item_id, monster_class_id, zone_id, xpos, ypos, kill_time)
SELECT 
    item_id, monster_class_id, zone_id, xpos, ypos, kill_time 
FROM 
    @mytable
DataTable dt = new DataTable();
dt.Columns.Add(new DataColumn("item_id", typeof(Int64)));
dt.Columns.Add(new DataColumn("monster_class_id", typeof(int)));
dt.Columns.Add(new DataColumn("zone_id", typeof(int)));
dt.Columns.Add(new DataColumn("xpos", typeof(float)));
dt.Columns.Add(new DataColumn("ypos", typeof(float)));
dt.Columns.Add(new DataColumn("timestamp", typeof(DateTime)));

for (int i = 0; i < MY_INSERT_SIZE; i++) {
    dt.Rows.Add(new object[] { item_id, monster_class_id, zone_id, xpos, ypos, DateTime.Now });
}

// Now we&#039;re going to do all the work with one connection!
using (SqlConnection conn = new SqlConnection(my_connection_string)) {
    conn.Open();
    using (SqlCommand cmd = new SqlCommand("insert_item_drops_rev4", conn)) {
        cmd.CommandType = CommandType.StoredProcedure;

        // Adding a "structured" parameter allows you to insert tons of data with low overhead
        SqlParameter param = new SqlParameter("@mytable", SqlDbType.Structured);
        param.Value = dt;
        cmd.Parameters.Add(param);
        cmd.ExecuteNonQuery();
    }
}
<...init...>
if(bcp_init(m_hdbc, TableName, NULL, NULL, DB_IN) == FAIL)
    return FALSE;

int col_number = 1;

// Bind columns
if(bcp_bind(m_hdbc, (BYTE *)&m_sd.SymbolName, 0, 16, (LPCBYTE)"", 1, 0, col_number++) == FAIL) return FALSE;
if(bcp_bind(m_hdbc, (BYTE *)&m_sd.Time, 0, 4, 0, 0, 0, col_number++) == FAIL) return FALSE;
if(bcp_bind(m_hdbc, (BYTE *)&m_sd.Open, 0, 8, 0, 0, 0, col_number++) == FAIL) return FALSE;
if(bcp_bind(m_hdbc, (BYTE *)&m_sd.High, 0, 8, 0, 0, 0, col_number++) == FAIL) return FALSE;
if(bcp_bind(m_hdbc, (BYTE *)&m_sd.Low, 0, 8, 0, 0, 0, col_number++) == FAIL) return FALSE;
if(bcp_bind(m_hdbc, (BYTE *)&m_sd.Close, 0, 8, 0, 0, 0, col_number++) == FAIL) return FALSE;
if(bcp_bind(m_hdbc, (BYTE *)&m_sd.Volume, 0, 8, 0, 0, 0, col_number++) == FAIL) return FALSE;


<...save into sql...>
BOOL CSymbolStorage::Copy(SQL_SYMBOL_DATA *sd)
{
    if(!m_bUseDB)
        return TRUE;

    memcpy(&m_sd, sd, sizeof(SQL_SYMBOL_DATA));

    if(bcp_sendrow(m_hdbc) != SUCCEED)
        return FALSE;

    return TRUE;
}