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插入数据库
增加一些澄清:
- 这是2008 R2服务器上的2008 R2企业SQL Server。机器有4个内核,8gb内存。全部为64位。80%的平均负载来自这台机器,显示大约20%的cpu负载
- 该表很简单,没有主键,只有一个关系引用(instrument reference)上的索引和一个唯一的(在一组工具中,因此这不是强制的)时间戳
- 表中的字段是:时间戳、仪器参考(无强制外键)、数据类型(char 1,表示发布数据的一个字符)、价格(double)和数量(int)。正如你所看到的,这是一张非常薄的桌子。所讨论的数据是金融工具的勾选数据
- 问题还涉及硬件等方面——主要是因为我没有看到真正的瓶颈。我正在插入多个事务,这给了我好处,但只是一个小好处。磁盘、CPU没有显示明显的负载,网络io等待很高(300毫秒/秒,目前为30%),但这是在同一个虚拟化平台上运行的,该平台运行两台服务器的JSUT,并且有足够的内核来运行所有服务器。我非常愿意“购买另一台服务器”,但我想首先确定瓶颈。。。。特别是在一天结束时,我没有抓住瓶颈是什么。日志记录与此无关-批量插入不会作为数据(无聚集索引)进入数据日志
进一步澄清:速度更高(90k),现在明显受到机器间网络IO的限制,这可能是VM交换 我现在要做的是每32k行进行一次连接,建立一个临时表,用SqlBUlkdCopy插入其中,然后使用一条sql语句复制到主表,从而最大限度地减少主表上的任何锁定时间
大多数等待时间现在仍在网络IO上。似乎我遇到了一些问题,VM是明智的。将在未来几个月转向物理硬件;) 您可以使用水平分区吗? 见: 您可能还想看看这个问题,并可能更改恢复模型: 一些问题: 您使用的是哪个版本的SQL Server 为什么一个核心是80%?这可能是瓶颈,因此可能值得调查
您使用的是什么操作系统,它是64位的吗?表中有没有可以不用的索引?编辑:打字时询问
是否可以将价格转换为整数,然后除以1000或其他任何查询?您是否尝试将pk添加到表中?这能提高速度吗 还有一种基于集合的方法可以使用理货表从中导入csv数据(接近底部,需要免费注册,但值得)
您可能想尝试一下并测试它的性能。。。有一个小的理货台,理货台索引正确。如果你每秒管理70k行,到目前为止你非常幸运。但我怀疑这是因为你有一个非常简单的模式 我真不敢相信你会问我这种负担
- 虚拟服务器
- 单阵列
- SATA磁盘
- 购买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'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;
}