C# 使用protobuf的流式压缩IDataReader
在将数据拉入和推送到sql时,我们需要大大减少后端服务使用的带宽。SqlClient使用的TDS流相当臃肿。多年来,人们在从sql中提取数据时要求使用压缩选项,但微软并没有添加它 我想看看是否有人想到了处理这件事的最佳方法。以下是我到目前为止所做的尝试:C# 使用protobuf的流式压缩IDataReader,c#,tcp,compression,protobuf-net,idatareader,C#,Tcp,Compression,Protobuf Net,Idatareader,在将数据拉入和推送到sql时,我们需要大大减少后端服务使用的带宽。SqlClient使用的TDS流相当臃肿。多年来,人们在从sql中提取数据时要求使用压缩选项,但微软并没有添加它 我想看看是否有人想到了处理这件事的最佳方法。以下是我到目前为止所做的尝试: 我修改为在套接字层添加压缩和解压缩。因为有效负载是SSL加密的,所以没有多大区别 接下来,我将IDataReader转换为Protobuf库,并在中找到TCP框架,试图创建一个客户机-服务器代理,通过将IDataReader转换为Protobu
有节省时间的主意吗?如果不是,也许我会用它做一个开源项目 这里有一个模式,您可以使用它来获取一个大型查询并将其作为一系列批传输,其中每个批都是一个压缩的二进制序列化数据表。在传输和反序列化之后,SqlBulk Copy可以直接使用每个数据表。相同的模式可以用于其他格式,但在传递到SqlBulkCopy之前需要额外的转换器
using System.Data.SqlClient;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
namespace BatchingDataReader
{
class BatchingDataReader : IDataReader
{
private int batchSize;
private IDataReader rdr;
private int rowsRead;
private bool atEnd;
private int batchesRead;
public BatchingDataReader(IDataReader rdr, int batchSize)
{
this.batchSize = batchSize;
this.rdr = rdr;
}
public object this[int i] => rdr[i];
public object this[string name] => rdr[name];
public int Depth => rdr.Depth;
public bool IsClosed => rdr.IsClosed;
public int RecordsAffected => rdr.RecordsAffected;
public int FieldCount => rdr.FieldCount;
public void Close()
{
if (!atEnd)
return;
rdr.Close();
}
public void Dispose()
{
if (!atEnd)
return;
rdr.Dispose();
}
public bool GetBoolean(int i)
{
return rdr.GetBoolean(i);
}
public byte GetByte(int i)
{
return rdr.GetByte(i);
}
public long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length)
{
return rdr.GetBytes(i, fieldOffset, buffer, bufferoffset, length);
}
public char GetChar(int i)
{
return rdr.GetChar(i);
}
public long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length)
{
return rdr.GetChars(i, fieldoffset, buffer, bufferoffset, length);
}
public IDataReader GetData(int i)
{
return rdr.GetData(i);
}
public string GetDataTypeName(int i)
{
return rdr.GetDataTypeName(i);
}
public DateTime GetDateTime(int i)
{
return rdr.GetDateTime(i);
}
public decimal GetDecimal(int i)
{
return rdr.GetDecimal(i);
}
public double GetDouble(int i)
{
return rdr.GetDouble(i);
}
public Type GetFieldType(int i)
{
return rdr.GetFieldType(i);
}
public float GetFloat(int i)
{
return rdr.GetFloat(i);
}
public Guid GetGuid(int i)
{
return rdr.GetGuid(i);
}
public short GetInt16(int i)
{
return rdr.GetInt16(i);
}
public int GetInt32(int i)
{
return rdr.GetInt32(i);
}
public long GetInt64(int i)
{
return rdr.GetInt64(i);
}
public string GetName(int i)
{
return rdr.GetName(i);
}
public int GetOrdinal(string name)
{
return rdr.GetOrdinal(name);
}
public DataTable GetSchemaTable()
{
return rdr.GetSchemaTable();
}
public string GetString(int i)
{
return rdr.GetString(i);
}
public object GetValue(int i)
{
return rdr.GetValue(i);
}
public int GetValues(object[] values)
{
return rdr.GetValues(values);
}
public bool IsDBNull(int i)
{
return rdr.IsDBNull(i);
}
public bool NextResult()
{
if (!atEnd)
{
batchesRead += 1;
rowsRead = 0;
return true;
}
if (IsClosed)
return false;
return rdr.NextResult();
}
public bool Read()
{
if (rowsRead >= batchSize)
return false;
rowsRead += 1;
atEnd = !rdr.Read();
return !atEnd;
}
public static IEnumerable<DataTable> Read(SqlDataReader r, int batchSize)
{
var rdr = new BatchingDataReader(r, batchSize);
do
{
var dt = new DataTable();
dt.TableName = "table";
dt.Load(rdr);
yield return dt;
} while (rdr.NextResult());
}
}
class Program
{
static void Main(string[] args)
{
var constr = "server=localhost;database=master;integrated security=true";
var outfile = "c:\\temp\\out.bin";
if (File.Exists(outfile))
File.Delete(outfile);
using (var con = new SqlConnection(constr))
{
//322,162,200 TDS raw
//235,355,311 binary uncompressed out.bin
// 52,755,181 binary GZ Fastest
// 43,061,121 binary GZ optimal
// 65,282,624 XML GZ fastest
// 41,892,056 binary GZ optimal 100,000 row batches
con.Open();
var bin = new BinaryFormatter();
var cmd = new SqlCommand("select top (1000000) * from sys.messages m, sys.objects o", con);
using (SqlDataReader rdr = cmd.ExecuteReader())
using (var destFile = File.OpenWrite(outfile))
using (var zipStream = new System.IO.Compression.GZipStream(destFile,System.IO.Compression.CompressionLevel.Optimal))
{
foreach (var dt in BatchingDataReader.Read(rdr, 10000))
{
Console.WriteLine(dt.Rows.Count);
dt.RemotingFormat = SerializationFormat.Binary;
bin.Serialize(zipStream, dt);
}
}
}
}
}
}
使用System.Data.SqlClient;
使用制度;
使用系统集合;
使用System.Collections.Generic;
使用系统数据;
使用System.IO;
使用System.Runtime.Serialization.Formatters.Binary;
命名空间BatchingDataReader
{
类BatchingDataReader:IDataReader
{
私有整数批量大小;
私人IDataReader rdr;
私人住宅区;
私人住宅区;
私有int-batchesRead;
公共BatchingDataReader(IDataReader rdr,int batchSize)
{
this.batchSize=batchSize;
this.rdr=rdr;
}
公共对象this[int i]=>rdr[i];
公共对象此[字符串名称]=>rdr[名称];
public int Depth=>rdr.Depth;
public bool IsClosed=>rdr.IsClosed;
public int recordsafected=>rdr.recordsafected;
public int FieldCount=>rdr.FieldCount;
公众假期结束()
{
如果(!atEnd)
返回;
rdr.Close();
}
公共空间处置()
{
如果(!atEnd)
返回;
Dispose();
}
公共bool GetBoolean(int i)
{
返回rdr.GetBoolean(i);
}
公共字节GetByte(int i)
{
返回rdr.GetByte(i);
}
公共长GetBytes(int i,long fieldOffset,byte[]buffer,int bufferoffset,int length)
{
返回rdr.GetBytes(i,fieldOffset,buffer,bufferoffset,length);
}
公共字符GetChar(int i)
{
返回rdr.GetChar(i);
}
公共长GetChars(int i,long fieldoffset,char[]buffer,int bufferoffset,int length)
{
返回rdr.GetChars(i,fieldoffset,buffer,bufferoffset,length);
}
公共IDataReader获取数据(int i)
{
返回rdr.GetData(i);
}
公共字符串GetDataTypeName(int i)
{
返回rdr.GetDataTypeName(i);
}
公共日期时间GetDateTime(int i)
{
返回rdr.GetDateTime(i);
}
公共十进制GetDecimal(int i)
{
返回rdr.GetDecimal(i);
}
公共双精度GetDouble(int i)
{
返回rdr.GetDouble(i);
}
公共类型GetFieldType(int i)
{
返回rdr.GetFieldType(i);
}
公共浮点GetFloat(int i)
{
返回rdr.GetFloat(i);
}
公共Guid GetGuid(int i)
{
返回rdr.GetGuid(i);
}
公共短消息GetInt16(int i)
{
返回rdr.GetInt16(i);
}
公共整数GetInt32(整数i)
{
返回rdr.GetInt32(i);
}
公共长GetInt64(int i)
{
返回rdr.GetInt64(i);
}
公共字符串GetName(int i)
{
返回rdr.GetName(i);
}
public int GetOrdinal(字符串名称)
{
返回rdr.GetOrdinal(名称);
}
公共数据表GetSchemaTable()
{
返回rdr.GetSchemaTable();
}
公共字符串GetString(int i)
{
返回rdr.GetString(i);
}
公共对象GetValue(int i)
{
返回rdr.GetValue(i);
}
public int GetValues(对象[]值)
{
返回rdr.GetValues(值);
}
公共布尔IsDBNull(int i)
{
返回rdr.IsDBNull(i);
}
公共bool NextResult()
{
如果(!atEnd)
{
batchesRead+=1;
rowsRead=0;
返回true;
}
如果(已关闭)
返回false;
返回rdr。下一个
with csv as (
select n = row_number() over (order by (select null)),
line = convert(nvarchar(max), concat(
message_id, ',', language_id, ',', severity, ',',
is_event_logged, ',', '"' + replace([text], '"', '""') + '"'))
from sys.messages)
select compress(string_agg(line, char(13)) within group (order by n))
from csv group by n / 1000