使用MyCouch向CouchDB批量提交文档遇问题(SQL迁移场景)
解决CouchDB批量插入8000个文档的问题
哥们,你这问题我之前帮人排查过好多次——循环逐个提交8000个文档加上**async void的异步方法**,这俩组合起来肯定要出问题的。要么是请求排队把CouchDB压垮,要么是客户端这边因为未等待的异步操作导致资源耗尽,甚至丢请求。
问题根源
async void的坑:你的PostToCouch用了async void,这种签名只适合事件处理程序。在foreach循环里调用它时,程序不会等待每个请求完成,会瞬间发起8000个并发请求,CouchDB和你的客户端都扛不住。- 未用批量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
相关产品推荐
相关产品推荐

