寻找终极 DataTable 序列化器





5.00/5 (17投票s)
我知道“从 WebService 返回 DataSets 是魔鬼的产物”,但是……
引言
我最近遇到一个复杂且性能不佳的 ASP.NET Web Forms 应用程序,该应用程序从 WCF 服务接收大量 DataTable
对象,并且其 UI 对这些对象的字段进行大量直接绑定。
我知道 这种方法可能不是最好的,但由于 UI 的复杂性和业务逻辑直接引用了大量的 DataTable
字段,因此重写这部分将是成本过高且风险巨大。
因此,我开始寻找终极的 DataTable
序列化器。
已测试的序列化器
以下是我测试过的序列化器列表。
在人工生成的 DataTable
对象以及从 AdventureWorks2012 SQL Server 示例数据库的 Production 架构中检索到的 DataTable
对象语料库上,对每个序列化器的一致性和性能都进行了多次验证和测量。
所有测试均在配备 Windows 10 Pro x64、Intel Core i3 540 @ 3.06 Ghz CPU 和 8 GB DDR3 RAM @ 1333 Mhz 的计算机上进行。
DataTable WriteXml/ReadXml 方法
此序列化器使用 DataTable
类的 WriteXml
和 ReadXml
方法,这些方法允许将 DataTable
对象当前的内容和架构写入 XML 并读回。
二进制格式化器
此序列化器使用位于 System.Runtime.Serialization.Formatters.Binary
命名空间中的 BinaryFormatter
类,该类提供了一种以二进制格式序列化和反序列化对象或连接对象整个图的通用方法。
对于使用 BinaryFormatter
类对 DataTable
对象进行序列化,已将 DataTable
对象的 RemotingFormat
属性设置为 RemotingFormat.Binary
。
压缩二进制格式化器
此序列化器仅在上述相同二进制格式化器输出的基础上添加了一个压缩层。
Protocol Buffers
Protocol Buffers 是 Google 的语言无关、平台无关、可扩展的结构化数据序列化机制——可以将其想象成 XML,但更小、更快、更简单。
此序列化器使用了 Marc Gravell 的 protobuf-net 实现,以及 Richard Dingwall 的 protobuf-net-data 针对 DataTable
对象在其之上的特定扩展。
压缩 Protocol Buffers
此序列化器仅在上述相同 Protocol Buffers 序列化器输出的基础上添加了一个压缩层。
快速序列化器
此序列化器由 SimmoTech 开发,旨在通过对拥有数据(作者的话,即相对安全可序列化的对象数据)做出一些假设来提高对象序列化的性能。
您可以在 这里 和 这里 找到作者关于“优化 .NET 中的序列化”的两部分文章。
轻量级序列化器
此序列化器由 Shital Shah 开发,它使用 BinaryFormatter
类序列化二维对象数组而不是 DataTable
对象,从而节省了少量字节,但代价是需要填充该数组对象,并且需要单独调用 DataTable
类的 WriteXmlSchema
方法来导出 DataTable
对象的架构以及数据。
您可以在 这里 找到作者关于“轻量级 DataTable 序列化”的文章。
Drew DataSet 格式化器
此序列化器由 Drew Noakes 开发,它使用与上面描述的轻量级序列化器类似的方法,使用 BinaryFormatter
类序列化从 DataTable
对象填充的自定义 Table
对象,并且与上述方法一样,节省了少量字节,但代价是需要填充该自定义 Table
对象。
您可以在 这里 找到作者关于“DataSet 序列化”的文章。
DataTable 自定义格式化器
此序列化器由我本人编写,专门用于 DataTable
对象,因此它只序列化最少的数据和元数据,避免了更通用(也更强大)的序列化器中昂贵的例程。默认情况下,在序列化器的输出中会添加一个压缩层。
序列化器原始测试结果
Synthetic.Type1Large (36 列 x 10,000 行 = 360,000 个单元格)
描述 | 大小 (字节) | 序列化 (毫秒) | 反序列化 (毫秒) |
DataTable WriteXml/ReadXml 方法 | 13,435,801 | 411.941 ± 4.932 | 467.279 ± 3.059 |
二进制格式化器 | 2,660,024 | 217.003 ± 1.254 | 317.036 ± 6.738 |
压缩二进制格式化器 | 821,369 | 232.951 ± 1.044 | 323.181 ± 2.818 |
Protocol Buffers (1) | 失败 | 失败 | 失败 |
Compressed Protocol Buffers (2) | 失败 | 失败 | 失败 |
快速序列化器 | 1,696,752 | 74.906 ± 0.782 | 106.662 ± 0.925 |
轻量级序列化器 | 3,324,311 | 339.926 ± 8.636 | 700.616 ± 8.806 |
Drew DataSet 格式化器 | 3,182,058 | 376.337 ± 1.704 | 668.623 ± 10.188 |
DataTable 自定义格式化器 | 535,194 | 60.743 ± 0.264 | 127.290 ± 0.929 |
未压缩 DataTable 自定义格式化器 | 2,966,841 | 49.940 ± 0.293 | 115.219 ± 0.635 |
(1) ProtoBuf.Data.UnsupportedColumnTypeException: 无法序列化类型为 'System.SByte' 的数据列。仅支持以下列类型:Boolean、Byte、Byte[]、Char、Char[]、DateTime、Decimal、Double、Guid、Int16、Int32、Int64、Single、String、TimeSpan。
(2) ProtoBuf.Data.UnsupportedColumnTypeException: 无法序列化类型为 'System.SByte' 的数据列。仅支持以下列类型:Boolean、Byte、Byte[]、Char、Char[]、DateTime、Decimal、Double、Guid、Int16、Int32、Int64、Single、String、TimeSpan。
Synthetic.Type2Small (4 列 x 10 行 = 40 个单元格)
描述 | 大小 (字节) | 序列化 (毫秒) | 反序列化 (毫秒) |
DataTable WriteXml/ReadXml 方法 | 3,707 | 0.116 ± 0.000 | 0.286 ± 0.001 |
二进制格式化器 | 7,609 | 0.272 ± 0.000 | 0.361 ± 0.000 |
压缩二进制格式化器 | 3,549 | 0.367 ± 0.021 | 0.414 ± 0.010 |
Protocol Buffers | 1,354 | 0.068 ± 0.000 | 0.145 ± 0.001 |
压缩 Protocol Buffers | 1,363 | 0.086 ± 0.000 | 0.148 ± 0.000 |
快速序列化器 | 1,420 | 0.017 ± 0.000 | 0.036 ± 0.000 |
轻量级序列化器 | 2,559 | 0.127 ± 0.000 | 0.274 ± 0.002 |
Drew DataSet 格式化器 | 2,192 | 0.060 ± 0.000 | 0.132 ± 0.000 |
DataTable 自定义格式化器 | 1,436 | 0.025 ± 0.000 | 0.050 ± 0.000 |
未压缩 DataTable 自定义格式化器 | 1,427 | 0.010 ± 0.000 | 0.045 ± 0.000 |
Synthetic.Type2Large (4 列 x 50,000 行 = 200,000 个单元格)
描述 | 大小 (字节) | 序列化 (毫秒) | 反序列化 (毫秒) |
DataTable WriteXml/ReadXml 方法 | 13,889,857 | 203.162 ± 2.186 | 390.192 ± 3.717 |
二进制格式化器 | 7,149,929 | 163.989 ± 0.442 | 315.307 ± 7.828 |
压缩二进制格式化器 | 6,534,987 | 249.564 ± 0.573 | 393.697 ± 8.944 |
Protocol Buffers | 6,583,542 | 74.065 ± 0.467 | 293.169 ± 11.353 |
压缩 Protocol Buffers | 6,047,817 | 151.984 ± 0.342 | 364.331 ± 12.658 |
快速序列化器 | 6,867,204 | 159.638 ± 1.432 | 214.509 ± 2.906 |
轻量级序列化器 | 7,900,979 | 282.263 ± 3.277 | 1002.640 ± 13.129 |
Drew DataSet 格式化器 | 7,200,752 | 201.556 ± 0.494 | 392.789 ± 5.377 |
DataTable 自定义格式化器 | 6,045,369 | 134.320 ± 0.826 | 284.072 ± 6.848 |
未压缩 DataTable 自定义格式化器 | 6,550,117 | 54.208 ± 0.174 | 222.759 ± 4.164 |
Production.Product (25 列 x 504 行 = 12,600 个单元格)
描述 | 大小 (字节) | 序列化 (毫秒) | 反序列化 (毫秒) |
DataTable WriteXml/ReadXml 方法 | 415,908 | 9.178 ± 0.060 | 12.013 ± 0.011 |
二进制格式化器 | 114,997 | 7.518 ± 0.030 | 7.172 ± 0.006 |
压缩二进制格式化器 | 40,642 | 8.283 ± 0.025 | 7.896 ± 0.032 |
Protocol Buffers (1) | 失败 | 失败 | 失败 |
Compressed Protocol Buffers (2) | 失败 | 失败 | 失败 |
Fast Serializer (3) | 失败 | 失败 | 失败 |
轻量级序列化器 | 115,718 | 8.829 ± 0.066 | 10.159 ± 0.020 |
Drew DataSet 格式化器 | 107,232 | 9.262 ± 0.043 | 13.954 ± 0.117 |
DataTable 自定义格式化器 | 25,885 | 1.674 ± 0.008 | 2.611 ± 0.003 |
未压缩 DataTable 自定义格式化器 | 79,342 | 1.207 ± 0.006 | 2.197 ± 0.002 |
(1) System.Exception: 反序列化数据表的 XML 表示与原始数据表不匹配。
(2) System.Exception: 反序列化数据表的 XML 表示与原始数据表不匹配。
(3) System.Exception: 反序列化数据表的 XML 表示与原始数据表不匹配。
Production.ProductDescription (4 列 x 762 行 = 3,048 个单元格)
描述 | 大小 (字节) | 序列化 (毫秒) | 反序列化 (毫秒) |
DataTable WriteXml/ReadXml 方法 | 305,996 | 5.729 ± 0.026 | 6.786 ± 0.004 |
二进制格式化器 | 135,756 | 6.310 ± 0.032 | 6.476 ± 0.006 |
压缩二进制格式化器 | 75,183 | 7.370 ± 0.006 | 7.447 ± 0.033 |
Protocol Buffers | 117,871 | 1.375 ± 0.003 | 2.515 ± 0.003 |
压缩 Protocol Buffers | 67,689 | 2.415 ± 0.012 | 3.475 ± 0.013 |
快速序列化器 | 112,598 | 1.480 ± 0.000 | 1.683 ± 0.001 |
轻量级序列化器 | 143,633 | 7.105 ± 0.024 | 7.675 ± 0.005 |
Drew DataSet 格式化器 | 132,694 | 6.172 ± 0.022 | 6.823 ± 0.005 |
DataTable 自定义格式化器 | 67,156 | 2.100 ± 0.008 | 2.668 ± 0.014 |
未压缩 DataTable 自定义格式化器 | 117,440 | 1.060 ± 0.004 | 1.718 ± 0.002 |
Production.ProductPhoto (6 列 x 101 行 = 606 个单元格)
描述 | 大小 (字节) | 序列化 (毫秒) | 反序列化 (毫秒) |
DataTable WriteXml/ReadXml 方法 | 2,715,494 | 21.841 ± 0.149 | 38.668 ± 0.258 |
二进制格式化器 | 2,025,340 | 6.428 ± 0.067 | 2.203 ± 0.015 |
压缩二进制格式化器 | 2,025,349 | 22.867 ± 0.093 | 6.929 ± 0.027 |
Protocol Buffers | 2,013,896 | 7.420 ± 0.017 | 1.796 ± 0.007 |
压缩 Protocol Buffers | 2,013,905 | 22.922 ± 0.064 | 5.973 ± 0.039 |
快速序列化器 | 2,013,926 | 7.370 ± 0.022 | 1.283 ± 0.001 |
轻量级序列化器 | 2,019,925 | 6.862 ± 0.015 | 2.119 ± 0.001 |
Drew DataSet 格式化器 | 2,018,227 | 6.393 ± 0.016 | 1.908 ± 0.001 |
DataTable 自定义格式化器 | 2,014,505 | 22.852 ± 0.046 | 5.973 ± 0.025 |
未压缩 DataTable 自定义格式化器 | 2,014,496 | 7.240 ± 0.020 | 1.335 ± 0.018 |
Production.ProductReview (8 列 x 4 行 = 32 个单元格)
描述 | 大小 (字节) | 序列化 (毫秒) | 反序列化 (毫秒) |
DataTable WriteXml/ReadXml 方法 | 7,839 | 0.150 ± 0.000 | 0.379 ± 0.002 |
二进制格式化器 | 15,450 | 0.437 ± 0.000 | 0.546 ± 0.000 |
压缩二进制格式化器 | 6,406 | 0.544 ± 0.001 | 0.640 ± 0.003 |
Protocol Buffers | 5,284 | 0.089 ± 0.000 | 0.203 ± 0.000 |
压缩 Protocol Buffers | 3,799 | 0.148 ± 0.000 | 0.256 ± 0.001 |
快速序列化器 | 5,339 | 0.039 ± 0.000 | 0.074 ± 0.000 |
轻量级序列化器 | 6,639 | 0.149 ± 0.000 | 0.341 ± 0.002 |
Drew DataSet 格式化器 | 6,161 | 0.077 ± 0.000 | 0.170 ± 0.000 |
DataTable 自定义格式化器 | 3,842 | 0.081 ± 0.000 | 0.134 ± 0.000 |
未压缩 DataTable 自定义格式化器 | 5,437 | 0.024 ± 0.000 | 0.082 ± 0.000 |