You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用MyCouch向CouchDB批量提交文档遇问题(SQL迁移场景)

解决CouchDB批量插入8000个文档的问题

哥们,你这问题我之前帮人排查过好多次——循环逐个提交8000个文档加上**async void的异步方法**,这俩组合起来肯定要出问题的。要么是请求排队把CouchDB压垮,要么是客户端这边因为未等待的异步操作导致资源耗尽,甚至丢请求。

问题根源

  1. async void的坑:你的PostToCouch用了async void,这种签名只适合事件处理程序。在foreach循环里调用它时,程序不会等待每个请求完成,会瞬间发起8000个并发请求,CouchDB和你的客户端都扛不住。
  2. 未用批量API:CouchDB专门提供了_bulk_docs接口做批量文档操作,逐个POST的性能比批量差几个数量级,完全是浪费资源。

修复方案

直接改用_bulk_docs批量提交,同时修正异步方法的签名,确保我们能正确控制并发和等待请求完成。

第一步:修改MyClass的批量提交方法

把原来的PostToCouch改成批量版本,调用CouchDB的_bulk_docs端点:

public class MyClass
{
    // 复用HttpClient,避免频繁创建销毁连接
    private static readonly HttpClient _httpClient = new HttpClient();

    public MyClass()
    {
        _httpClient.BaseAddress = new Uri("http://你的CouchDB地址:5984/你的数据库名/");
        _httpClient.DefaultRequestHeaders.Accept.Clear();
        _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        // 如果需要认证,这里加授权头
        // _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes("用户名:密码")));
    }

    public async Task BulkInsertDocuments(List<NewSnDocument> documents)
    {
        // 构造_bulk_docs要求的JSON格式:{"docs": [文档数组]}
        var bulkPayload = new 
        { 
            docs = documents,
            // 如果需要确保要么全成功要么全失败,设置为true;默认false允许部分成功
            all_or_nothing = false 
        };

        string jsonPayload = JsonConvert.SerializeObject(bulkPayload);
        var content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");

        HttpResponseMessage response = await _httpClient.PostAsync("_bulk_docs", content);

        // 处理响应,失败时抛出异常或者记录日志
        if (!response.IsSuccessStatusCode)
        {
            string errorDetails = await response.Content.ReadAsStringAsync();
            throw new InvalidOperationException($"批量插入失败,状态码:{(int)response.StatusCode},详情:{errorDetails}");
        }
    }
}

第二步:修改调用逻辑,分批提交文档

把原来的逐个创建改成攒一批提交一批,比如每1000个文档一批(可以根据你的CouchDB性能调整这个数值):

MyClass cl = new MyClass();
const int batchSize = 1000; // 批量大小,建议1000-5000之间调整
var documentBatch = new List<NewSnDocument>();

foreach (DataRow row in dteqEvent.Rows)
{
    NewSnDocument pn = new NewSnDocument();
    pn.id = row[1].ToString(); // 注意:如果这个ID在CouchDB已存在,会覆盖旧文档!
    pn.val = row[2].ToString();

    documentBatch.Add(pn);

    // 达到批量大小就提交
    if (documentBatch.Count >= batchSize)
    {
        await cl.BulkInsertDocuments(documentBatch);
        documentBatch.Clear();
    }
}

// 提交最后一批不足batchSize的文档
if (documentBatch.Count > 0)
{
    await cl.BulkInsertDocuments(documentBatch);
}

关键注意点

  • ID重复问题:如果你的pn.id已经存在于CouchDB中,_bulk_docs会直接覆盖旧文档。如果不想覆盖,要么去掉id字段让CouchDB自动生成,要么先查询ID是否存在(批量场景下建议业务上保证ID唯一)。
  • 批量大小调整:如果提交时遇到超时或者CouchDB返回413(请求太大),就把batchSize改小,比如500;如果服务器性能足够,可以尝试加大到2000或5000。
  • 异常处理:建议在调用时加try-catch块,比如某一批失败可以记录日志后重试,避免整个迁移任务中断。
  • HttpClient复用:上面的代码里用了静态HttpClient,这是最佳实践,避免频繁创建HttpClient导致的socket资源耗尽。

内容的提问来源于stack exchange,提问作者Marcus Aurelius

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:41:49