将行插入DB的快速方法?

时间:2013-03-08 16:25:35

标签: c# optimization insert parallel-processing tibco

我循环遍历数据集的行并将其插入活动空间环境(由tibco,它是一个内存中的数据库)。这就是我这样做的方式。

有更快的方法吗?

我正在考虑对行进行分区,然后将每个分区并行,但我不知道是否会使它更快。

System.Threading.Tasks.Parallel.ForEach(
    dataSet.Tables[0].Rows,
    currRow =>
    {
        var tuple = Com.Tibco.As.Space.Tuple.Create();

        for (int i = 0; i < currRow.Values.Length; i++)
        {
            if (currRow.Values[i] != null)
            {
                var k = ConvertToAny(currRow.Values[i].ToString());

                if (k.GetType().IsEquivalentTo(typeof(DateTime)))
                {
                    tuple.Put(dataSet.Tables[0].ColumnNames[i], (DateTime)k);
                }
                else if (k.GetType().IsEquivalentTo(typeof(double)))
                {
                    tuple.Put(dataSet.Tables[0].ColumnNames[i], (double)k);
                }
                else
                {
                    tuple.Put(dataSet.Tables[0].ColumnNames[i], k.ToString());
                }
            }
        }
        try
        {
            inSpace_.Put(tuple);
        }
        catch (Exception e)
        {
        }
    }
);

如果有人可以帮忙的话,我想一次批量处理1000个左右:(

修改

列出tuplesToAdd = new List();             for(int i = 0; i&lt; dataSet.Tables [0] .Rows.Length; i ++)             {                 var tuple = Com.Tibco.As.Space.Tuple.Create();

            for (int j = 0; j < dataSet.Tables[0].Rows[i].Values.Length; j++)
            {
                if (dataSet.Tables[0].Rows[i].Values[j] != null)
                {
                    var k = ConvertToAny(dataSet.Tables[0].Rows[i].Values[j].ToString());
                    if (k is DateTime)
                    {
                        tuple.Put(dataSet.Tables[0].ColumnNames[j], (DateTime)k);
                    }
                    else if (k is Double)
                    {
                        tuple.Put(dataSet.Tables[0].ColumnNames[j], (Double)k);
                    }
                    else
                    {
                        tuple.Put(dataSet.Tables[0].ColumnNames[j], k.ToString());
                    }
                }
            }

            tuplesToAdd.Add(tuple);

            if (i % 100000 == 0 || i == dataSet.Tables[0].Rows.Length - 1)
            {

                ThreadStart TUPLE_WORKER = delegate
                {
                    inSpace_.PutAll(tuplesToAdd);
                };
                new Thread(TUPLE_WORKER).Start();
                tuplesToAdd.Clear();
            }
        }

我尝试这样做的新方法(通过批处理)

1 个答案:

答案 0 :(得分:1)

我不确定,但您似乎可以避免转换代码中的ToString。也就是说,而不是:

var k = ConvertToAny(currRow.Values[i].ToString());

if (k.GetType().IsEquivalentTo(typeof(DateTime)))

可以替换为......

var k = currRow.Values[i];
if (k is DateTime)
{
    tuple.Put(dataSet.Tables[0].ColumnNames[i], (DateTime)k);
}

这应该可以节省你转换为字符串然后再回来。

在回复评论时添加

首先,您的ConvertToAny是不必要的。 currRow.Values[i]中的项目已经是正确的类型。你只是不知道它是什么类型。除非您说它可能是DateTimeDouble的字符串表示形式。例如,如果类型已经一个double,则没有理由转换为字符串,解析,然后转换回来。也就是说,以下两位代码执行相同的操作:

object o = 3.14;
var k = ConvertToAny(o.ToString());
if (k.GetType.IsEquivalentTo(typeof(double))

object o = 3.14;
if (o is double)

唯一的区别是第二个会更快。

但是,如果你有

object o = "3.14";

并且您希望将其转换为double,然后您必须进行转换。

批量处理的代码必须在添加和更新时锁定列表。否则你会腐败它。我建议:

lock (tuplesToAdd)
{
    tuplesToAdd.Add(tuple);
    if ((tuplesToAdd.Count % 10000) == 0)
    {
        // push them all to the database.
        inspace_.PutAll(tuplesToAdd);
        tuplesToAdd.Clear();
    }
}

当你完成所有工作(即完成Parallel.Foreach)时:

if (tuplesToAdd.Count > 0)
{
    // push the remaining items
}

现在,如果您想避免在更新期间阻止所有线程,您可以获得一些创意。

首先,创建两个可以锁定的对象:

private object lockObject = new Object();
private object pushLock = new Object();

在创建tuplesToAdd列表后立即创建。然后,当您想要添加项目时:

Monitor.Enter(lockObject); // acquires the lock
tuplesToAdd.Add(tuple);
if (tuplesToAdd.Count == 100000)
{
    var tuplesToPush = tuplesToAdd;
    tuplesToAdd = new List<tuple>(10000);
    Monitor.Exit(lockObject);  // releases the lock so other threads can process
    lock (pushLock)  // prevent multiple threads from pushing at the same time
    {
        inspace_.PutAll(tuplesToPush);
    }
}
else
{
    Monitor.Exit(lockObject);
}

这样,当一个线程正在更新数据库时,其他线程可以在下一次填充列表。


在我考虑一下之后,你可能甚至不需要使用并行处理来完成这项任务。很可能绝大部分时间是由等待Put电话的线程花费的。使用单个线程批量处理这些并批量写入它们可能比原始解决方案执行得快得多。您决定使用的并行版本会更快,但我怀疑它会非常更快。