在C#中如何使用Span<T>优化数据库二进制数据读取的性能?
优化方案
核心思路是全程用ReadOnlyMemory<byte>替代byte[]作为二进制数据的载体,利用内存视图的特性只传递缓冲区引用而非复制实际数据,仅在必须修改数据或者对接需要byte[]的旧API时才执行拷贝。
1. 数据读取阶段取消首次数组拷贝
原来的(byte[])reader["Photo"]会强制把SqlClient内部缓冲区的数据复制到新的byte数组,产生第一次不必要的拷贝。改成直接读取ReadOnlyMemory<byte>:
// 先获取字段序号,比字段名查找性能更好 int photoColOrdinal = reader.GetOrdinal("Photo"); DataAccess.Person person = new(); // 直接读取为ReadOnlyMemory<byte>,不会复制实际二进制数据 person.Photo = reader.GetFieldValue<ReadOnlyMemory<byte>>(photoColOrdinal);
注意这里需要把Person类的Photo属性从byte[]改成ReadOnlyMemory<byte>类型,如果后续需要修改数据可以替换为Memory<byte>。
2. 各层流转时避免数据拷贝
DTO的Photo属性同样定义为ReadOnlyMemory<byte>,赋值时直接传递内存视图即可:
// DTO定义 public class PersonDto { public ReadOnlyMemory<byte> Photo { get; set; } // 其他属性 } // 从数据访问实体赋值到DTO,仅复制Memory结构体(十几字节的小对象),不会复制二进制数据 PersonDto dto = new() { Photo = person.Photo };
如果需要截取二进制的部分内容,直接用Slice方法,返回的还是原数据的视图,不会产生拷贝:
// 取前100字节的视图,无拷贝 var header = dto.Photo.Slice(0, 100);
3. 业务规则处理阶段按需访问
如果业务处理只需要读取数据,直接转成Span<byte>访问,性能更高:
ReadOnlySpan<byte> photoSpan = dto.Photo.Span; // 直接读取内容,比如验证文件头 if (photoSpan.Length >= 2 && photoSpan[0] == 0xFF && photoSpan[1] == 0xD8) { // 确认是JPG格式 }
只有当你需要修改二进制内容,或者必须对接要求byte[]参数的旧API时,才调用ToArray()方法执行拷贝,尽可能推迟拷贝的发生时机,避免无意义的前置拷贝。
内容的提问来源于stack exchange,提问作者Sandy
相关产品推荐
相关产品推荐

