10

最快的方法是什么:

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

我现在应该做什么:

  • 将最多 32*1024 行聚合到一个数组中,然后将其排队。
  • 在 2-3 个线程中读取队列。使用 SqlBulkCopy 插入数据库。

我每秒导入大约 60k-75k 行,这还不够,但非常接近。我很想达到 250.000 行。

到目前为止,还没有真正使用任何东西。我得到 20% 的时间“网络 I/O”块,有一个核心 80% 负载 CPU 端。光盘正在写入 7mb-14mb,大部分是空闲的。6 只猛禽的 RAID 10 上的平均队列长度为.... 0.25。

有人知道如何加快速度吗?更快的服务器(到目前为止它是虚拟的,8GB 内存,4 核,物理磁盘通过数据)。


添加一些说明:

  • 这是 2008 R2 服务器上的 2008 R2 Enterprise SQL Server。机器有4个核心,8GB内存。全部 64 位。80% 的平均负载来自这台机器,显示大约 20% 的 cpu 负载。
  • 该表很简单,没有主键,只有关系引用(仪器引用)上的索引和唯一的(在一组仪器中,因此不强制执行)时间戳。
  • 表中的字段是:时间戳、工具参考(无强制外键)、数据类型(字符 1,指示发布哪些数据的多个字符之一)、价格(双精度)和交易量(整数)。如您所见,这是一张非常薄的桌子。有问题的数据是金融工具的分时数据。
  • 问题还与硬件等有关-主要是因为我看不到真正的瓶颈。我正在插入多个交易,它给了我一个好处,但很小。磁盘、CPU 没有显示出显着的负载,网络 io 等待很高(300 毫秒/秒,目前为 30%),但这是在运行 JSUT 两台服务器的同一虚拟化平台上,并且有足够的内核来运行所有服务器。我几乎愿意“购买另一台服务器”,但我想首先确定瓶颈......特别是考虑到在一天结束时我没有抓住瓶颈是什么。日志记录无关紧要 - 批量插入不会作为数据进入数据日志(无聚集索引)。

垂直分区是否会有所帮助,例如通过一个字节(tinyint)将仪器世界划分为例如 16 个表,因此我可以同时进行多达 16 个插入?由于实际上数据来自不同的交易所,我可以为每个交易所创建一个分区。这将是一个自然的拆分字段(实际上是在仪器中,但我可以在此处复制此数据)。


更多说明:获得了更高的速度(90k),现在显然受到机器之间的网络 IO 的限制,这可能是 VM 切换。

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

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

4

6 回答 6

4

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

我不敢相信你问这种负载

  • 虚拟服务器
  • 单阵列
  • SATA 磁盘

网络和 CPU 共享,IO 受限:你不能使用所有资源。您看到的任何负载统计信息都不是很有用。我怀疑您看到的网络负载是 2 个虚拟服务器之间的流量,如果您解决此问题,您将成为 IO 绑定

在我继续之前,请阅读35K tps 的这 10 节课。他没有使用虚拟盒子。

如果您想增加卷,假设没有 SAN 和 DR 功能,这就是我要做的。

  • 购买 2 个大型物理服务器,CPU RAM 有点无关紧要,最大 RAM,去 x64 安装
  • 磁盘 + 控制器 = 最快的主轴,最快的 SCSI。或者一个很棒的 NAS
  • 1000MB + 网卡
  • RAID 10 和 6-10 磁盘仅用于您的数据库的一个日志文件
  • 用于数据文件的剩余磁盘 RAID 5 或 RAID 10

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

于 2011-01-27T20:39:26.277 回答
1

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

但是,我确实想分享表参数插入非常适合大数据传输的想法;尽管 SqlBulkCopy 似乎同样快,但它的灵活性明显较低。

我在这里写了一篇关于这个主题的文章:http ://www.altdevblogaday.com/2012/05/16/sql-server-high-performance-inserts/

总体答案是您大致想创建一个表类型:

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

后面的 SQL Server 代码如下所示:

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();
    }
}
于 2012-07-18T17:43:25.627 回答
1

这一切都很慢。

前段时间我们解决了一个类似的问题(将数万个价格数据插入数据库,我记得每个时间帧大约 50K,我们有大约 8 个时间帧都在 :00 发生冲突,所以大约有 400K 记录)它对我们来说工作得非常非常快(MS SQL 2005)。想象一下它今天的工作方式(SQL 2012):

<...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;
}
于 2012-10-27T18:40:22.847 回答
1

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

还有一种基于集合的方法可以使用计数表从http://www.sqlservercentral.com/articles/T-SQL/62867/导入 csv 数据(接近底部,需要免费注册但值得)。

您可能想尝试一下并测试它的性能……使用正确索引的小型计数表。

于 2011-01-20T14:05:04.957 回答
1

表上是否有任何您可以不用的索引?编辑:在你打字的时候问。

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

于 2011-01-20T13:59:05.203 回答
0

你可以使用水平分区吗?请参阅:http: //msdn.microsoft.com/en-us/library/ms178148.aspx & http://msdn.microsoft.com/en-us/library/ms188706.aspx

您可能还想查看这个问题,并可能更改恢复模型: Sql Server 2008 Tuning with large transactions (700k+ rows/transaction)

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

为什么一核是80%?这可能是瓶颈,因此可能值得研究。

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

于 2011-01-20T13:22:19.470 回答