从一堆模型对象生成 XML 时速度较慢
Slower while generating the XML from the bunch of model object
class GenericFormatter < Formatter
attr_accessor :tag_name,:objects
def generate_xml
builder = Nokogiri::XML::Builder.new do |xml|
xml.send(tag_name.pluralize) {
objects.each do |obj|
xml.send(tag_name.singularize){
self.generate_obj_row obj,xml
}
end
}
end
builder.to_xml
end
def initialize tag_name,objects
self.tag_name = tag_name
self.objects = objects
end
def generate_obj_row obj,xml
obj.attributes.except("updated_at").map do |key,value|
xml.send(key, value)
end
xml.updated_at obj.updated_at.try(:strftime,"%m/%d/%Y %H:%M:%S") if obj.attributes.key?('updated_at')
end
end
在上面的代码中,我实现了格式化程序,其中我使用 nokogiri XML Builder 通过操纵 code.It 内部传递的对象来生成 XML当数据不是太大时生成速度更快 XML 如果数据大于 10,000 条记录那么它会减慢 XML 生成速度并且至少需要 50-60 秒。
问题:有什么方法可以更快地生成XML,我也试过XML Builders on view但是没有work.How 我可以更快地生成 XML 吗?解决方案应该是 rails 3 上的应用程序和优化上述代码的建议吗?
您的主要问题是一次处理所有内容,而不是将数据分成几批。这一切都需要大量内存,首先构建所有那些 ActiveRecord 模型,然后构建整个 xml 文档的内存表示。元编程也很昂贵(我指的是那些 send
方法)。
看看这段代码:
class XmlGenerator
attr_accessor :tag_name, :ar_relation
def initialize(tag_name, ar_relation)
@ar_relation = ar_relation
@tag_name = tag_name
end
def generate_xml
singular_tag_name = tag_name.singularize
plural_tag_name = tag_name.pluralize
xml = ""
xml << "<#{plural_tag_name}>"
ar_relation.find_in_batches(batch_size: 1000) do |batch|
batch.each do |obj|
xml << "<#{singular_tag_name}>"
obj.attributes.except("updated_at").each do |key, value|
xml << "<#{key}>#{value}</#{key}>"
end
if obj.attributes.key?("updated_at")
xml << "<updated_at>#{obj.updated_at.strftime('%m/%d/%Y %H:%M:%S')}</updated_at>"
end
xml << "</#{singular_tag_name}>"
end
end
xml << "</#{tag_name.pluralize}>"
xml
end
end
# example usage
XmlGenerator.new("user", User.where("age < 21")).generate_xml
主要改进是:
- 批量从数据库中获取数据,需要传递ActiveRecord集合而不是ActiveRecord模型数组
- 通过构造字符串生成xml,这有产生无效xml的风险,但比使用构建器
快得多
我在超过 6 万条记录上对其进行了测试。生成这样的 xml 文档大约需要 40 秒。
可以做更多的事情来进一步改进这一点,但这完全取决于您的应用程序。
这里有一些想法:
- 不要使用 ActiveRecord 获取数据,而是使用轻量级库或纯数据库驱动程序
- 只获取您需要的数据
- 调整批量大小
- 将生成的 xml 直接写入文件(如果这是您的用例)以节省内存
Nokogiri gem
有一个很好的界面,可以从头开始创建 XML,
Nokogiri 是 libxml2.
的包装器
Gemfile gem 'nokogiri' 要生成 xml 只需像这样使用 Nokogiri XML Builder
xml = Nokogiri::XML::Builder.new { |xml|
xml.body do
xml.test1 "some string"
xml.test2 890
xml.test3 do
xml.test3_1 "some string"
end
xml.test4 "with attributes", :attribute => "some attribute"
xml.closing
end
}.to_xml
输出
<?xml version="1.0"?>
<body>
<test1>some string</test1>
<test2>890</test2>
<test3>
<test3_1>some string</test3_1>
</test3>
<test4 attribute="some attribute">with attributes</test4>
<closing/>
</body>
class GenericFormatter < Formatter
attr_accessor :tag_name,:objects
def generate_xml
builder = Nokogiri::XML::Builder.new do |xml|
xml.send(tag_name.pluralize) {
objects.each do |obj|
xml.send(tag_name.singularize){
self.generate_obj_row obj,xml
}
end
}
end
builder.to_xml
end
def initialize tag_name,objects
self.tag_name = tag_name
self.objects = objects
end
def generate_obj_row obj,xml
obj.attributes.except("updated_at").map do |key,value|
xml.send(key, value)
end
xml.updated_at obj.updated_at.try(:strftime,"%m/%d/%Y %H:%M:%S") if obj.attributes.key?('updated_at')
end
end
在上面的代码中,我实现了格式化程序,其中我使用 nokogiri XML Builder 通过操纵 code.It 内部传递的对象来生成 XML当数据不是太大时生成速度更快 XML 如果数据大于 10,000 条记录那么它会减慢 XML 生成速度并且至少需要 50-60 秒。
问题:有什么方法可以更快地生成XML,我也试过XML Builders on view但是没有work.How 我可以更快地生成 XML 吗?解决方案应该是 rails 3 上的应用程序和优化上述代码的建议吗?
您的主要问题是一次处理所有内容,而不是将数据分成几批。这一切都需要大量内存,首先构建所有那些 ActiveRecord 模型,然后构建整个 xml 文档的内存表示。元编程也很昂贵(我指的是那些 send
方法)。
看看这段代码:
class XmlGenerator
attr_accessor :tag_name, :ar_relation
def initialize(tag_name, ar_relation)
@ar_relation = ar_relation
@tag_name = tag_name
end
def generate_xml
singular_tag_name = tag_name.singularize
plural_tag_name = tag_name.pluralize
xml = ""
xml << "<#{plural_tag_name}>"
ar_relation.find_in_batches(batch_size: 1000) do |batch|
batch.each do |obj|
xml << "<#{singular_tag_name}>"
obj.attributes.except("updated_at").each do |key, value|
xml << "<#{key}>#{value}</#{key}>"
end
if obj.attributes.key?("updated_at")
xml << "<updated_at>#{obj.updated_at.strftime('%m/%d/%Y %H:%M:%S')}</updated_at>"
end
xml << "</#{singular_tag_name}>"
end
end
xml << "</#{tag_name.pluralize}>"
xml
end
end
# example usage
XmlGenerator.new("user", User.where("age < 21")).generate_xml
主要改进是:
- 批量从数据库中获取数据,需要传递ActiveRecord集合而不是ActiveRecord模型数组
- 通过构造字符串生成xml,这有产生无效xml的风险,但比使用构建器 快得多
我在超过 6 万条记录上对其进行了测试。生成这样的 xml 文档大约需要 40 秒。
可以做更多的事情来进一步改进这一点,但这完全取决于您的应用程序。
这里有一些想法:
- 不要使用 ActiveRecord 获取数据,而是使用轻量级库或纯数据库驱动程序
- 只获取您需要的数据
- 调整批量大小
- 将生成的 xml 直接写入文件(如果这是您的用例)以节省内存
Nokogiri gem
有一个很好的界面,可以从头开始创建 XML,
Nokogiri 是 libxml2.
Gemfile gem 'nokogiri' 要生成 xml 只需像这样使用 Nokogiri XML Builder
xml = Nokogiri::XML::Builder.new { |xml|
xml.body do
xml.test1 "some string"
xml.test2 890
xml.test3 do
xml.test3_1 "some string"
end
xml.test4 "with attributes", :attribute => "some attribute"
xml.closing
end
}.to_xml
输出
<?xml version="1.0"?>
<body>
<test1>some string</test1>
<test2>890</test2>
<test3>
<test3_1>some string</test3_1>
</test3>
<test4 attribute="some attribute">with attributes</test4>
<closing/>
</body>