/// <summary>备份单表数据,抽取数据和写入文件双线程</summary> /// <remarks> /// 最大支持21亿行 /// </remarks> /// <param name="table">数据表</param> /// <param name="stream">目标数据流</param> /// <returns></returns> public virtual Int32 Backup(IDataTable table, Stream stream) { using var span = Tracer?.NewSpan("db:Backup", table.Name); // 并行写入文件,提升吞吐 var writeFile = new WriteFileActor { Stream = stream, // 最多同时堆积数 BoundedCapacity = 4, TracerParent = span, Tracer = Tracer, Log = Log, }; var tableName = Dal.Db.FormatName(table); var sb = new SelectBuilder { Table = tableName }; var connName = Dal.ConnName; var extracer = GetExtracter(table); // 总行数 writeFile.Total = Dal.SelectCount(sb); WriteLog("备份[{0}/{1}]开始,共[{2:n0}]行,抽取器{3}", table, connName, writeFile.Total, extracer); // 临时关闭日志 var old = Dal.Db.ShowSQL; Dal.Db.ShowSQL = false; Dal.Session.ShowSQL = false; var total = 0; var sw = Stopwatch.StartNew(); try { foreach (var dt in extracer.Fetch()) { var row = extracer.Row; var count = dt.Rows.Count; WriteLog("备份[{0}/{1}]数据 {2:n0} + {3:n0}", table, connName, row, count); if (count == 0) { break; } // 字段名更换为属性名 for (var i = 0; i < dt.Columns.Length; i++) { var dc = table.GetColumn(dt.Columns[i]); if (dc != null) { dt.Columns[i] = dc.Name; } } // 进度报告、消费数据 OnProcess(table, row, dt, writeFile); total += count; } // 通知写入完成 writeFile.Stop(-1); } catch (Exception ex) { span?.SetError(ex, table); throw; } finally { Dal.Db.ShowSQL = old; Dal.Session.ShowSQL = old; } sw.Stop(); var ms = sw.Elapsed.TotalMilliseconds; WriteLog("备份[{0}/{1}]完成,共[{2:n0}]行,耗时{3:n0}ms,速度{4:n0}tps", table, connName, total, ms, total * 1000L / ms); // 返回总行数 return(total); }