{"id":1217,"date":"2025-03-24T08:52:35","date_gmt":"2025-03-23T23:52:35","guid":{"rendered":"https:\/\/dexall.co.jp\/articles\/?p=1217"},"modified":"2025-03-24T08:52:35","modified_gmt":"2025-03-23T23:52:35","slug":"activerecordimport%e3%81%a7%e5%ae%9f%e7%8f%be%e3%81%99%e3%82%8b%e7%88%86%e9%80%9f%e3%83%87%e3%83%bc%e3%82%bf%e3%82%a4%e3%83%b3%e3%83%9d%e3%83%bc%e3%83%88%ef%bc%81%e5%ae%9f%e8%b7%b5%e7%9a%84%e3%81%aa","status":"publish","type":"post","link":"https:\/\/dexall.co.jp\/articles\/?p=1217","title":{"rendered":"ActiveRecord::Import\u3067\u5b9f\u73fe\u3059\u308b\u7206\u901f\u30c7\u30fc\u30bf\u30a4\u30f3\u30dd\u30fc\u30c8\uff01\u5b9f\u8df5\u7684\u306a7\u3064\u306e\u6700\u9069\u5316\u30c6\u30af\u30cb\u30c3\u30af"},"content":{"rendered":"\n<div class=\"toc\"><br \/>\n<b>Warning<\/b>:  Undefined array key \"is_admin\" in <b>\/home\/xs392991\/dexall.co.jp\/public_html\/articles\/wp-content\/themes\/sango-theme\/library\/gutenberg\/dist\/classes\/Toc.php<\/b> on line <b>116<\/b><br \/>\n<br \/>\n<b>Warning<\/b>:  Undefined array key \"is_category_top\" in <b>\/home\/xs392991\/dexall.co.jp\/public_html\/articles\/wp-content\/themes\/sango-theme\/library\/gutenberg\/dist\/classes\/Toc.php<\/b> on line <b>121<\/b><br \/>\n<br \/>\n<b>Warning<\/b>:  Undefined array key \"is_top\" in <b>\/home\/xs392991\/dexall.co.jp\/public_html\/articles\/wp-content\/themes\/sango-theme\/library\/gutenberg\/dist\/classes\/Toc.php<\/b> on line <b>128<\/b><br \/>\n    <div id=\"toc_container\" class=\"sgb-toc--bullets js-smooth-scroll\" data-dialog-title=\"\u76ee\u6b21\">\n      <p class=\"toc_title\">\u76ee\u6b21 <\/p>\n      <ul class=\"toc_list\">  <li class=\"first\">    <a href=\"#i-0\">ActiveRecord::Import\u3068\u306f\uff1f\u57fa\u790e\u304b\u3089\u6027\u80fd\u306e\u9055\u3044\u307e\u3067\u89e3\u8aac<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-1\">\u901a\u5e38\u306eActiveRecord\u3068\u306e\u6c7a\u5b9a\u7684\u306a\u9055\u3044<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-2\">\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u304c\u5411\u4e0a\u3059\u308b\u7406\u7531\u3068\u4ed5\u7d44\u307f<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-3\">ActiveRecord::Import\u306e\u57fa\u672c\u7684\u306a\u4f7f\u3044\u65b9<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-4\">gem\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3068\u521d\u671f\u8a2d\u5b9a<\/a>      <\/li>      <li>        <a href=\"#i-5\">\u30b7\u30f3\u30d7\u30eb\u306a\u4e00\u62ec\u30a4\u30f3\u30dd\u30fc\u30c8\u306e\u5b9f\u88c5\u65b9\u6cd5<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-6\">\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u306e\u6271\u3044\u65b9\u3068\u6ce8\u610f\u70b9<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-7\">\u5b9f\u8df5\u7684\u306a\u6700\u9069\u5316\u30c6\u30af\u30cb\u30c3\u30af<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-8\">\u30d0\u30c3\u30c1\u30b5\u30a4\u30ba\u306e\u6700\u9069\u5316\u306b\u3088\u308b\u51e6\u7406\u901f\u5ea6\u306e\u5411\u4e0a<\/a>      <\/li>      <li>        <a href=\"#i-9\">\u30d0\u30eb\u30af\u30a4\u30f3\u30b5\u30fc\u30c8\u6642\u306e\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u7ba1\u7406<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-10\">\u30c8\u30e9\u30f3\u30b6\u30af\u30b7\u30e7\u30f3\u5236\u5fa1\u306b\u3088\u308b\u30c7\u30fc\u30bf\u6574\u5408\u6027\u306e\u78ba\u4fdd<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-11\">\u9ad8\u5ea6\u306a\u4f7f\u7528\u65b9\u6cd5\u3068\u30ab\u30b9\u30bf\u30de\u30a4\u30ba<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-12\">\u95a2\u9023\u30c6\u30fc\u30d6\u30eb\u3092\u542b\u3080\u30a4\u30f3\u30dd\u30fc\u30c8\u51e6\u7406\u306e\u5b9f\u88c5<\/a>      <\/li>      <li>        <a href=\"#i-13\">\u65e2\u5b58\u30ec\u30b3\u30fc\u30c9\u306e\u66f4\u65b0\u6226\u7565<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-14\">\u30ab\u30b9\u30bf\u30e0\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u306e\u8ffd\u52a0\u65b9\u6cd5<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-15\">\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u30e2\u30cb\u30bf\u30ea\u30f3\u30b0\u3068\u30c1\u30e5\u30fc\u30cb\u30f3\u30b0<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-16\">\u5b9f\u884c\u6642\u9593\u3068\u30e1\u30e2\u30ea\u4f7f\u7528\u91cf\u306e\u8a08\u6e2c\u65b9\u6cd5<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-17\">\u30dc\u30c8\u30eb\u30cd\u30c3\u30af\u7279\u5b9a\u3068\u6539\u5584\u30a2\u30d7\u30ed\u30fc\u30c1<\/a>      <\/li>    <\/ul>  <\/li>  <li class=\"last\">    <a href=\"#i-18\">\u5b9f\u969b\u306e\u30e6\u30fc\u30b9\u30b1\u30fc\u30b9\u3068\u5b9f\u88c5\u4f8b<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-19\">\u5927\u898f\u6a21\u30c7\u30fc\u30bf\u79fb\u884c\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3067\u306e\u6d3b\u7528\u4e8b\u4f8b<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-20\">\u5b9a\u671f\u7684\u306a\u30c7\u30fc\u30bf\u30a4\u30f3\u30dd\u30fc\u30c8\u306e\u81ea\u52d5\u5316\u5b9f\u88c5<\/a>      <\/li>    <\/ul>  <\/li><\/ul>\n      <a href=\"#\" class=\"sgb-toc-button js-toc-button\" rel=\"nofollow\" data-open-dialog=\"true\"><i class=\"fa fa-list\"><\/i><span class=\"sgb-toc-button__text\">\u76ee\u6b21\u3078<\/span><\/a>\n    <\/div><\/div><h2 class=\"wp-block-heading\" id=\"i-0\">ActiveRecord::Import\u3068\u306f\uff1f\u57fa\u790e\u304b\u3089\u6027\u80fd\u306e\u9055\u3044\u307e\u3067\u89e3\u8aac<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-1\">\u901a\u5e38\u306eActiveRecord\u3068\u306e\u6c7a\u5b9a\u7684\u306a\u9055\u3044<\/h3>\n\n\n\n<p>ActiveRecord::Import\u306f\u3001Ruby\u3067\u5927\u91cf\u306e\u30c7\u30fc\u30bf\u3092\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b\u52b9\u7387\u7684\u306b\u30a4\u30f3\u30dd\u30fc\u30c8\u3059\u308b\u305f\u3081\u306egem\u3067\u3059\u3002\u901a\u5e38\u306eActiveRecord\u306b\u3088\u308b\u4e00\u62ec\u30c7\u30fc\u30bf\u767b\u9332\u3068\u6bd4\u8f03\u3057\u3066\u3001\u4ee5\u4e0b\u306e\u70b9\u3067\u5927\u304d\u304f\u7570\u306a\u308a\u307e\u3059\uff1a<\/p>\n\n\n<div id=\"id-744bd657-6e29-483b-b6f7-2f718e0374c5\">\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u6bd4\u8f03\u9805\u76ee<\/th><th>\u901a\u5e38\u306eActiveRecord<\/th><th>ActiveRecord::Import<\/th><\/tr><\/thead><tbody><tr><td>SQL\u5b9f\u884c\u56de\u6570<\/td><td>\u30ec\u30b3\u30fc\u30c9\u6570\u5206\u306eINSERT\u6587\u3092\u5b9f\u884c<\/td><td>1\u56de\u306eINSERT\u6587\u3067\u8907\u6570\u30ec\u30b3\u30fc\u30c9\u3092\u767b\u9332<\/td><\/tr><tr><td>\u30e1\u30e2\u30ea\u4f7f\u7528\u91cf<\/td><td>\u5404\u30ec\u30b3\u30fc\u30c9\u3054\u3068\u306b\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u3092\u4f5c\u6210<\/td><td>\u6700\u5c0f\u9650\u306e\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u4f5c\u6210\u3067\u6e08\u3080<\/td><\/tr><tr><td>\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3<\/td><td>\u30ec\u30b3\u30fc\u30c9\u3054\u3068\u306b\u5b9f\u884c<\/td><td>\u4e00\u62ec\u3067\u306e\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u304c\u53ef\u80fd<\/td><\/tr><tr><td>\u30c8\u30e9\u30f3\u30b6\u30af\u30b7\u30e7\u30f3<\/td><td>\u30ec\u30b3\u30fc\u30c9\u3054\u3068\u306b\u767a\u751f<\/td><td>\u4e00\u62ec\u51e6\u7406\u3068\u3057\u30661\u30c8\u30e9\u30f3\u30b6\u30af\u30b7\u30e7\u30f3<\/td><\/tr><\/tbody><\/table><\/figure>\n<\/div>\n\n\n<p>\u4f8b\u3048\u3070\u30011\u4e07\u4ef6\u306e\u30e6\u30fc\u30b6\u30fc\u30c7\u30fc\u30bf\u3092\u767b\u9332\u3059\u308b\u5834\u5408\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u901a\u5e38\u306eActiveRecord\uff08\u975e\u52b9\u7387\uff09\nusers.each do |user|\n  User.create!(\n    name: user.name,\n    email: user.email\n  )\nend\n\n# ActiveRecord::Import\uff08\u52b9\u7387\u7684\uff09\nUser.import users, validate: true<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-2\">\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u304c\u5411\u4e0a\u3059\u308b\u7406\u7531\u3068\u4ed5\u7d44\u307f<\/h3>\n\n\n\n<p>ActiveRecord::Import\u306e\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u5411\u4e0a\u306f\u3001\u4e3b\u306b\u4ee5\u4e0b\u306e\u6700\u9069\u5316\u306b\u3088\u3063\u3066\u5b9f\u73fe\u3055\u308c\u3066\u3044\u307e\u3059\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>SQL\u306e\u6700\u9069\u5316<\/strong><br>\u30d0\u30eb\u30af\u30a4\u30f3\u30b5\u30fc\u30c8\u7528\u306eSQL\u3092\u751f\u6210\u3057\u30011\u56de\u306e\u30af\u30a8\u30ea\u3067\u8907\u6570\u30ec\u30b3\u30fc\u30c9\u3092\u633f\u5165\u3057\u307e\u3059\uff1a<\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">INSERT INTO users (name, email) VALUES \n  ('user1', 'user1@example.com'),\n  ('user2', 'user2@example.com'),\n  ...<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30e1\u30e2\u30ea\u4f7f\u7528\u91cf\u306e\u524a\u6e1b<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>ActiveRecord\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u306e\u4f5c\u6210\u3092\u6700\u5c0f\u9650\u306b\u6291\u5236<\/li>\n\n\n\n<li>\u4e00\u6642\u7684\u306a\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u306e\u5373\u6642\u89e3\u653e<\/li>\n\n\n\n<li>\u30d0\u30c3\u30c1\u51e6\u7406\u306b\u3088\u308b\u52b9\u7387\u7684\u306a\u30e1\u30e2\u30ea\u7ba1\u7406<\/li>\n<\/ul>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u8ca0\u8377\u306e\u8efd\u6e1b<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30b3\u30cd\u30af\u30b7\u30e7\u30f3\u6570\u306e\u524a\u6e1b<\/li>\n\n\n\n<li>\u30c8\u30e9\u30f3\u30b6\u30af\u30b7\u30e7\u30f3\u51e6\u7406\u306e\u6700\u9069\u5316<\/li>\n\n\n\n<li>\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u66f4\u65b0\u306e\u4e00\u62ec\u5316<\/li>\n<\/ul>\n\n\n\n<p>\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6bd4\u8f03\u306e\u5177\u4f53\u4f8b\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">require 'benchmark'\n\nusers = Array.new(10000) do |i|\n  { name: \"User#{i}\", email: \"user#{i}@example.com\" }\nend\n\nBenchmark.bm do |x|\n  x.report(\"ActiveRecord:\") do\n    User.transaction do\n      users.each { |user| User.create!(user) }\n    end\n  end\n\n  x.report(\"Import:\") do\n    User.import users\n  end\nend\n\n# \u5b9f\u884c\u7d50\u679c\u4f8b\uff1a\n#                  user     system      total        real\n# ActiveRecord:  28.350      0.980     29.330     30.123\n# Import:         0.890      0.120      1.010      1.234<\/pre>\n\n\n\n<p>\u3053\u306e\u6700\u9069\u5316\u306b\u3088\u308a\u3001\u4ee5\u4e0b\u306e\u3088\u3046\u306a\u5177\u4f53\u7684\u306a\u52b9\u679c\u304c\u5f97\u3089\u308c\u307e\u3059\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3078\u306e\u8ca0\u8377\u8efd\u6e1b\uff1a90%\u4ee5\u4e0a\u306e\u524a\u6e1b<\/li>\n\n\n\n<li>\u30e1\u30e2\u30ea\u4f7f\u7528\u91cf\uff1a\u6700\u592780%\u306e\u524a\u6e1b<\/li>\n\n\n\n<li>\u51e6\u7406\u6642\u9593\uff1a\u6700\u592795%\u306e\u77ed\u7e2e<\/li>\n<\/ul>\n\n\n\n<p>\u305f\u3060\u3057\u3001\u4ee5\u4e0b\u306e\u70b9\u306b\u306f\u6ce8\u610f\u304c\u5fc5\u8981\u3067\u3059\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u5c0f\u898f\u6a21\u30c7\u30fc\u30bf\uff08\u6570\u5341\u4ef6\u7a0b\u5ea6\uff09\u3067\u306f\u52b9\u679c\u304c\u9650\u5b9a\u7684<\/li>\n\n\n\n<li>\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u306e\u65b9\u6cd5\u306b\u3088\u3063\u3066\u306f\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u304c\u4f4e\u4e0b\u3059\u308b\u53ef\u80fd\u6027<\/li>\n\n\n\n<li>\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u8a2d\u5b9a\u306b\u3088\u3063\u3066\u6700\u9069\u306a\u30d0\u30c3\u30c1\u30b5\u30a4\u30ba\u304c\u7570\u306a\u308b<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-3\">ActiveRecord::Import\u306e\u57fa\u672c\u7684\u306a\u4f7f\u3044\u65b9<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-4\">gem\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3068\u521d\u671f\u8a2d\u5b9a<\/h3>\n\n\n\n<p>ActiveRecord::Import\u306e\u5c0e\u5165\u306f\u975e\u5e38\u306b\u7c21\u5358\u3067\u3059\u3002\u4ee5\u4e0b\u306e\u624b\u9806\u3067\u8a2d\u5b9a\u3067\u304d\u307e\u3059\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Gemfile\u3078\u306e\u8ffd\u52a0\uff1a<\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># Gemfile\ngem 'activerecord-import'<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li>\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u306e\u5b9f\u884c\uff1a<\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">bundle install<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li>\u8a2d\u5b9a\u30d5\u30a1\u30a4\u30eb\u3067\u306e\u30ab\u30b9\u30bf\u30de\u30a4\u30ba\uff08\u30aa\u30d7\u30b7\u30e7\u30f3\uff09\uff1a<\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># config\/initializers\/active_record_import.rb\nActiveRecord::Import.configure do |config|\n  # \u30d0\u30c3\u30c1\u30b5\u30a4\u30ba\u306e\u30c7\u30d5\u30a9\u30eb\u30c8\u5024\u8a2d\u5b9a\n  config.batch_size = 1000\n\n  # \u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u30a8\u30e9\u30fc\u6642\u306e\u52d5\u4f5c\u8a2d\u5b9a\n  config.on_duplicate_key_update = [:updated_at]\nend<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-5\">\u30b7\u30f3\u30d7\u30eb\u306a\u4e00\u62ec\u30a4\u30f3\u30dd\u30fc\u30c8\u306e\u5b9f\u88c5\u65b9\u6cd5<\/h3>\n\n\n\n<p>\u57fa\u672c\u7684\u306a\u4f7f\u7528\u65b9\u6cd5\u3092\u3044\u304f\u3064\u304b\u306e\u30d1\u30bf\u30fc\u30f3\u3067\u7d39\u4ecb\u3057\u307e\u3059\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u914d\u5217\u304b\u3089\u306e\u4e00\u62ec\u30a4\u30f3\u30dd\u30fc\u30c8<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u30e2\u30c7\u30eb\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u306e\u914d\u5217\u3092\u4f7f\u7528\nusers = []\n10.times do |i|\n  users &lt;&lt; User.new(name: \"User #{i}\", email: \"user#{i}@example.com\")\nend\n\n# \u4e00\u62ec\u30a4\u30f3\u30dd\u30fc\u30c8\u306e\u5b9f\u884c\nUser.import users\n\n# \u51e6\u7406\u7d50\u679c\u306e\u78ba\u8a8d\nputs \"Imported #{users.length} users\"<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30cf\u30c3\u30b7\u30e5\u304b\u3089\u306e\u4e00\u62ec\u30a4\u30f3\u30dd\u30fc\u30c8<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u30cf\u30c3\u30b7\u30e5\u306e\u914d\u5217\u3092\u76f4\u63a5\u4f7f\u7528\nuser_data = [\n  { name: 'User 1', email: 'user1@example.com' },\n  { name: 'User 2', email: 'user2@example.com' }\n]\n\n# \u30ab\u30e9\u30e0\u3092\u660e\u793a\u7684\u306b\u6307\u5b9a\u3057\u3066\u30a4\u30f3\u30dd\u30fc\u30c8\nUser.import [:name, :email], user_data<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>CSV\u30c7\u30fc\u30bf\u304b\u3089\u306e\u30a4\u30f3\u30dd\u30fc\u30c8<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">require 'csv'\n\nusers = []\nCSV.foreach('users.csv', headers: true) do |row|\n  users &lt;&lt; {\n    name: row['name'],\n    email: row['email']\n  }\nend\n\n# \u30d0\u30c3\u30c1\u30b5\u30a4\u30ba\u3092\u6307\u5b9a\u3057\u3066\u30a4\u30f3\u30dd\u30fc\u30c8\nUser.import users, batch_size: 1000<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-6\">\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u306e\u6271\u3044\u65b9\u3068\u6ce8\u610f\u70b9<\/h3>\n\n\n\n<p>ActiveRecord::Import\u3067\u306e\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u51e6\u7406\u306b\u306f\u3001\u3044\u304f\u3064\u304b\u306e\u91cd\u8981\u306a\u7279\u5fb4\u304c\u3042\u308a\u307e\u3059\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u306e\u5236\u5fa1<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u3092\u5b9f\u884c\u3057\u3066\u30a4\u30f3\u30dd\u30fc\u30c8\nresult = User.import users, validate: true\n\n# \u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u7d50\u679c\u306e\u78ba\u8a8d\nif result.failed_instances.any?\n  puts \"Failed to import #{result.failed_instances.count} records:\"\n  result.failed_instances.each do |instance|\n    puts instance.errors.full_messages.join(\", \")\n  end\nend<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u306e\u30ab\u30b9\u30bf\u30de\u30a4\u30ba<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class User &lt; ApplicationRecord\n  # \u30d0\u30eb\u30af\u30a4\u30f3\u30dd\u30fc\u30c8\u6642\u306e\u307f\u9069\u7528\u3059\u308b\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\n  validates :email, presence: true, if: :bulk_importing?\n\n  def bulk_importing?\n    self.class.bulk_importing\n  end\n\n  class &lt;&lt; self\n    attr_accessor :bulk_importing\n  end\nend\n\n# \u30d0\u30eb\u30af\u30a4\u30f3\u30dd\u30fc\u30c8\u6642\u306e\u30d5\u30e9\u30b0\u8a2d\u5b9a\nUser.bulk_importing = true\nUser.import users\nUser.bulk_importing = false<\/pre>\n\n\n\n<p>\u6ce8\u610f\u3059\u3079\u304d\u91cd\u8981\u306a\u30dd\u30a4\u30f3\u30c8\uff1a<\/p>\n\n\n<div id=\"id-d8eb7d1e-0517-489c-8056-aeb513d8c0ba\">\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u9805\u76ee<\/th><th>\u6ce8\u610f\u70b9<\/th><\/tr><\/thead><tbody><tr><td>\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u30b9\u30d4\u30fc\u30c9<\/td><td>\u5927\u91cf\u30c7\u30fc\u30bf\u306e\u5834\u5408\u3001\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u304c\u30dc\u30c8\u30eb\u30cd\u30c3\u30af\u306b\u306a\u308b\u53ef\u80fd\u6027\u3042\u308a<\/td><\/tr><tr><td>\u30e1\u30e2\u30ea\u4f7f\u7528\u91cf<\/td><td>\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u30a8\u30e9\u30fc\u60c5\u5831\u306e\u4fdd\u6301\u306b\u3088\u308b\u30e1\u30e2\u30ea\u5897\u52a0<\/td><\/tr><tr><td>\u30b3\u30fc\u30eb\u30d0\u30c3\u30af<\/td><td><code>after_save<\/code>\u306a\u3069\u306e\u30b3\u30fc\u30eb\u30d0\u30c3\u30af\u306f\u5b9f\u884c\u3055\u308c\u306a\u3044<\/td><\/tr><tr><td>\u4e00\u610f\u6027\u5236\u7d04<\/td><td>\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30ec\u30d9\u30eb\u306e\u4e00\u610f\u6027\u5236\u7d04\u306f\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u3088\u308a\u512a\u5148<\/td><\/tr><\/tbody><\/table><\/figure>\n<\/div>\n\n\n<p>\u4e00\u62ec\u30a4\u30f3\u30dd\u30fc\u30c8\u6642\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u9069\u5207\u306a\u30d0\u30c3\u30c1\u30b5\u30a4\u30ba\u306e\u9078\u629e\uff08\u901a\u5e38500-1000\u4ef6\uff09<\/li>\n\n\n\n<li>\u30c8\u30e9\u30f3\u30b6\u30af\u30b7\u30e7\u30f3\u306e\u9069\u5207\u306a\u4f7f\u7528<\/li>\n\n\n\n<li>\u30a8\u30e9\u30fc\u30cf\u30f3\u30c9\u30ea\u30f3\u30b0\u306e\u5b9f\u88c5<\/li>\n\n\n\n<li>\u30a4\u30f3\u30dd\u30fc\u30c8\u524d\u306e\u30c7\u30fc\u30bf\u691c\u8a3c<\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u63a8\u5968\u3055\u308c\u308b\u5b9f\u88c5\u30d1\u30bf\u30fc\u30f3\ndef import_users(file_path)\n  users = []\n  failed_rows = []\n\n  CSV.foreach(file_path, headers: true).with_index(2) do |row, line|\n    user = User.new(\n      name: row['name'],\n      email: row['email']\n    )\n\n    if user.valid?\n      users &lt;&lt; user\n    else\n      failed_rows &lt;&lt; { line: line, errors: user.errors.full_messages }\n    end\n  end\n\n  if failed_rows.empty?\n    User.import users, batch_size: 1000\n    { success: true, imported_count: users.size }\n  else\n    { success: false, errors: failed_rows }\n  end\nrescue StandardError =&gt; e\n  { success: false, error: e.message }\nend<\/pre>\n\n\n\n<p>\u3053\u306e\u30b3\u30fc\u30c9\u306f\u3001\u30a8\u30e9\u30fc\u30cf\u30f3\u30c9\u30ea\u30f3\u30b0\u3001\u30d0\u30c3\u30c1\u51e6\u7406\u3001\u4e8b\u524d\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u3092\u9069\u5207\u306b\u7d44\u307f\u5408\u308f\u305b\u305f\u5b9f\u8df5\u7684\u306a\u5b9f\u88c5\u4f8b\u3067\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-7\">\u5b9f\u8df5\u7684\u306a\u6700\u9069\u5316\u30c6\u30af\u30cb\u30c3\u30af<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-8\">\u30d0\u30c3\u30c1\u30b5\u30a4\u30ba\u306e\u6700\u9069\u5316\u306b\u3088\u308b\u51e6\u7406\u901f\u5ea6\u306e\u5411\u4e0a<\/h3>\n\n\n\n<p>\u30d0\u30c3\u30c1\u30b5\u30a4\u30ba\u306e\u6700\u9069\u5316\u306f\u3001ActiveRecord::Import\u306e\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u3092\u5927\u304d\u304f\u5de6\u53f3\u3059\u308b\u91cd\u8981\u306a\u8981\u7d20\u3067\u3059\u3002<\/p>\n\n\n\n<p><strong>\u6700\u9069\u306a\u30d0\u30c3\u30c1\u30b5\u30a4\u30ba\u306e\u6c7a\u5b9a\u8981\u56e0\uff1a<\/strong><\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u8a2d\u5b9a<\/li>\n\n\n\n<li>\u30b5\u30fc\u30d0\u30fc\u306e\u30e1\u30e2\u30ea\u5bb9\u91cf<\/li>\n\n\n\n<li>\u30ec\u30b3\u30fc\u30c9\u306e\u8907\u96d1\u3055<\/li>\n\n\n\n<li>\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u306e\u6570<\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u30d0\u30c3\u30c1\u30b5\u30a4\u30ba\u306e\u5b9f\u9a13\u7684\u306a\u691c\u8a3c\nrequire 'benchmark'\n\ndata = Array.new(100_000) do |i|\n  { name: \"User#{i}\", email: \"user#{i}@example.com\" }\nend\n\nbatch_sizes = [100, 500, 1000, 5000, 10000]\n\nBenchmark.bm(20) do |x|\n  batch_sizes.each do |size|\n    x.report(\"Batch size: #{size}\") do\n      User.import data, batch_size: size\n    end\n  end\nend\n\n# \u30e1\u30e2\u30ea\u4f7f\u7528\u91cf\u306e\u76e3\u8996\ndef import_with_memory_tracking(data, batch_size)\n  memory_before = GetProcessMem.new.mb\n  User.import data, batch_size: batch_size\n  memory_after = GetProcessMem.new.mb\n\n  puts \"Memory usage: #{(memory_after - memory_before).round(2)} MB\"\nend<\/pre>\n\n\n\n<p>\u5b9f\u9a13\u7684\u306b\u5f97\u3089\u308c\u305f\u6700\u9069\u306a\u30d0\u30c3\u30c1\u30b5\u30a4\u30ba\u306e\u76ee\u5b89\uff1a<\/p>\n\n\n<div id=\"id-61181d60-a76d-4959-b7a9-22f571b41f26\">\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u30c7\u30fc\u30bf\u91cf<\/th><th>\u63a8\u5968\u30d0\u30c3\u30c1\u30b5\u30a4\u30ba<\/th><th>\u5099\u8003<\/th><\/tr><\/thead><tbody><tr><td>\u301c1\u4e07\u4ef6<\/td><td>1,000\u4ef6<\/td><td>\u30e1\u30e2\u30ea\u52b9\u7387\u304c\u826f\u597d<\/td><\/tr><tr><td>1\u4e07\u301c10\u4e07\u4ef6<\/td><td>5,000\u4ef6<\/td><td>\u51e6\u7406\u901f\u5ea6\u3068\u6d88\u8cbb\u30e1\u30e2\u30ea\u306e\u30d0\u30e9\u30f3\u30b9\u304c\u6700\u9069<\/td><\/tr><tr><td>10\u4e07\u4ef6\u4ee5\u4e0a<\/td><td>10,000\u4ef6<\/td><td>\u5927\u898f\u6a21\u30c7\u30fc\u30bf\u306b\u9069\u3057\u305f\u8a2d\u5b9a<\/td><\/tr><\/tbody><\/table><\/figure>\n<\/div>\n\n\n<h3 class=\"wp-block-heading\" id=\"i-9\">\u30d0\u30eb\u30af\u30a4\u30f3\u30b5\u30fc\u30c8\u6642\u306e\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u7ba1\u7406<\/h3>\n\n\n\n<p>\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u306e\u4e00\u6642\u7684\u306a\u7121\u52b9\u5316\u306b\u3088\u308a\u3001\u30a4\u30f3\u30dd\u30fc\u30c8\u901f\u5ea6\u3092\u5927\u5e45\u306b\u5411\u4e0a\u3055\u305b\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class BulkImporter\n  def self.fast_import(records)\n    ActiveRecord::Base.transaction do\n      # \u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u306e\u4e00\u6642\u7684\u306a\u7121\u52b9\u5316\n      ActiveRecord::Base.connection.execute(\n        \"ALTER TABLE users DISABLE KEYS\"\n      )\n\n      begin\n        User.import records, batch_size: 5000\n\n        # \u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u306e\u518d\u69cb\u7bc9\n        ActiveRecord::Base.connection.execute(\n          \"ALTER TABLE users ENABLE KEYS\"\n        )\n      rescue =&gt; e\n        # \u30a8\u30e9\u30fc\u6642\u306e\u30ed\u30fc\u30eb\u30d0\u30c3\u30af\u51e6\u7406\n        ActiveRecord::Base.connection.execute(\n          \"ALTER TABLE users ENABLE KEYS\"\n        )\n        raise e\n      end\n    end\n  end\nend\n\n# \u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u8a08\u6e2c\u4ed8\u304d\u306e\u4f7f\u7528\u4f8b\nBenchmark.measure do\n  BulkImporter.fast_import(large_dataset)\nend<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-10\">\u30c8\u30e9\u30f3\u30b6\u30af\u30b7\u30e7\u30f3\u5236\u5fa1\u306b\u3088\u308b\u30c7\u30fc\u30bf\u6574\u5408\u6027\u306e\u78ba\u4fdd<\/h3>\n\n\n\n<p>\u5927\u898f\u6a21\u30c7\u30fc\u30bf\u306e\u30a4\u30f3\u30dd\u30fc\u30c8\u3067\u306f\u3001\u9069\u5207\u306a\u30c8\u30e9\u30f3\u30b6\u30af\u30b7\u30e7\u30f3\u5236\u5fa1\u304c\u91cd\u8981\u3067\u3059\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class SafeBulkImporter\n  def self.import_with_safety(records)\n    success_count = 0\n    failed_records = []\n\n    ActiveRecord::Base.transaction do\n      begin\n        # \u30c1\u30a7\u30c3\u30af\u30dd\u30a4\u30f3\u30c8\u306e\u8a2d\u5b9a\n        ActiveRecord::Base.connection.execute(\"SAVEPOINT major_import\")\n\n        records.each_slice(1000) do |batch|\n          result = User.import batch, validate: true\n\n          if result.failed_instances.any?\n            failed_records.concat(result.failed_instances)\n            # \u90e8\u5206\u7684\u306a\u30ed\u30fc\u30eb\u30d0\u30c3\u30af\n            ActiveRecord::Base.connection.execute(\"ROLLBACK TO SAVEPOINT major_import\")\n          else\n            success_count += batch.size\n            # \u30c1\u30a7\u30c3\u30af\u30dd\u30a4\u30f3\u30c8\u306e\u66f4\u65b0\n            ActiveRecord::Base.connection.execute(\"RELEASE SAVEPOINT major_import\")\n            ActiveRecord::Base.connection.execute(\"SAVEPOINT major_import\")\n          end\n        end\n\n        # \u6700\u7d42\u30c1\u30a7\u30c3\u30af\u30dd\u30a4\u30f3\u30c8\u306e\u89e3\u653e\n        ActiveRecord::Base.connection.execute(\"RELEASE SAVEPOINT major_import\")\n      rescue =&gt; e\n        # \u30a8\u30e9\u30fc\u767a\u751f\u6642\u306e\u5b8c\u5168\u30ed\u30fc\u30eb\u30d0\u30c3\u30af\n        raise ActiveRecord::Rollback\n      end\n    end\n\n    {\n      success_count: success_count,\n      failed_count: failed_records.size,\n      failed_records: failed_records\n    }\n  end\nend<\/pre>\n\n\n\n<p>\u30a4\u30f3\u30dd\u30fc\u30c8\u51e6\u7406\u306e\u6700\u9069\u5316\u306e\u305f\u3081\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30e1\u30e2\u30ea\u7ba1\u7406\u306e\u6700\u9069\u5316<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">def optimized_import(file_path)\n  # \u30d5\u30a1\u30a4\u30eb\u30b9\u30c8\u30ea\u30fc\u30e0\u3092\u4f7f\u7528\u3057\u305f\u52b9\u7387\u7684\u306a\u8aad\u307f\u8fbc\u307f\n  File.open(file_path) do |file|\n    CSV.new(file, headers: true).each_slice(1000) do |batch|\n      records = batch.map { |row| row.to_h }\n      User.import records\n\n      # \u30e1\u30e2\u30ea\u306e\u660e\u793a\u7684\u89e3\u653e\n      GC.start\n    end\n  end\nend<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u975e\u540c\u671f\u51e6\u7406\u3068\u306e\u7d44\u307f\u5408\u308f\u305b<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class BulkImportJob &lt; ApplicationJob\n  def perform(file_path)\n    optimized_import(file_path)\n  rescue =&gt; e\n    # \u30a8\u30e9\u30fc\u901a\u77e5\u306e\u9001\u4fe1\n    ErrorNotifier.notify(e)\n    raise e\n  end\nend\n\n# \u30b8\u30e7\u30d6\u306e\u30ad\u30e5\u30fc\u30a4\u30f3\u30b0\nBulkImportJob.perform_later('path\/to\/file.csv')<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>\u30d7\u30ed\u30b0\u30ec\u30b9\u76e3\u8996\u306e\u5b9f\u88c5<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">def import_with_progress(records)\n  total = records.size\n  progress = 0\n\n  records.each_slice(1000) do |batch|\n    User.import batch\n    progress += batch.size\n\n    # \u9032\u6357\u7387\u306e\u8a08\u7b97\u3068\u5831\u544a\n    percentage = ((progress.to_f \/ total) * 100).round(2)\n    Rails.logger.info \"Import progress: #{percentage}%\"\n  end\nend<\/pre>\n\n\n\n<p>\u3053\u308c\u3089\u306e\u6700\u9069\u5316\u30c6\u30af\u30cb\u30c3\u30af\u3092\u7d44\u307f\u5408\u308f\u305b\u308b\u3053\u3068\u3067\u3001\u5927\u898f\u6a21\u30c7\u30fc\u30bf\u306e\u30a4\u30f3\u30dd\u30fc\u30c8\u3092\u52b9\u7387\u7684\u304b\u3064\u5b89\u5168\u306b\u5b9f\u884c\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-11\">\u9ad8\u5ea6\u306a\u4f7f\u7528\u65b9\u6cd5\u3068\u30ab\u30b9\u30bf\u30de\u30a4\u30ba<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-12\">\u95a2\u9023\u30c6\u30fc\u30d6\u30eb\u3092\u542b\u3080\u30a4\u30f3\u30dd\u30fc\u30c8\u51e6\u7406\u306e\u5b9f\u88c5<\/h3>\n\n\n\n<p>ActiveRecord::Import\u3067\u306f\u3001\u95a2\u9023\u30c6\u30fc\u30d6\u30eb\u3092\u542b\u3080\u8907\u96d1\u306a\u30c7\u30fc\u30bf\u69cb\u9020\u3082\u30a4\u30f3\u30dd\u30fc\u30c8\u3067\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class User &lt; ApplicationRecord\n  has_many :posts\n  has_many :comments\nend\n\nclass Post &lt; ApplicationRecord\n  belongs_to :user\n  has_many :comments\nend\n\nclass Comment &lt; ApplicationRecord\n  belongs_to :user\n  belongs_to :post\nend\n\n# \u95a2\u9023\u30c7\u30fc\u30bf\u3092\u542b\u3080\u30a4\u30f3\u30dd\u30fc\u30c8\u51e6\u7406\u306e\u5b9f\u88c5\u4f8b\ndef import_user_with_relations(user_data)\n  ActiveRecord::Base.transaction do\n    # \u30e6\u30fc\u30b6\u30fc\u306e\u30a4\u30f3\u30dd\u30fc\u30c8\n    user_columns = [:name, :email]\n    users = user_data.map do |data|\n      [data[:name], data[:email]]\n    end\n\n    user_results = User.import user_columns, users, validate: true, returning: :id\n\n    # \u6295\u7a3f\u30c7\u30fc\u30bf\u306e\u6e96\u5099\n    post_columns = [:user_id, :title, :content]\n    posts = []\n    user_data.each_with_index do |data, index|\n      user_id = user_results.ids[index]\n      data[:posts].each do |post|\n        posts &lt;&lt; [user_id, post[:title], post[:content]]\n      end\n    end\n\n    # \u6295\u7a3f\u306e\u30a4\u30f3\u30dd\u30fc\u30c8\n    post_results = Post.import post_columns, posts, validate: true, returning: :id\n\n    # \u30b3\u30e1\u30f3\u30c8\u30c7\u30fc\u30bf\u306e\u6e96\u5099\u3068\u30a4\u30f3\u30dd\u30fc\u30c8\n    comment_columns = [:user_id, :post_id, :content]\n    comments = []\n    post_results.ids.each_with_index do |post_id, index|\n      user_id = user_results.ids[index]\n      user_data[index][:posts].each do |post|\n        post[:comments].each do |comment|\n          comments &lt;&lt; [user_id, post_id, comment[:content]]\n        end\n      end\n    end\n\n    Comment.import comment_columns, comments, validate: true\n  end\nend<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-13\">\u65e2\u5b58\u30ec\u30b3\u30fc\u30c9\u306e\u66f4\u65b0\u6226\u7565<\/h3>\n\n\n\n<p>\u30c7\u30fc\u30bf\u306e\u91cd\u8907\u3084\u66f4\u65b0\u306b\u5bfe\u3059\u308b\u69d8\u3005\u306a\u6226\u7565\u3092\u5b9f\u88c5\u3067\u304d\u307e\u3059\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class BulkUpdater\n  def self.upsert_users(records)\n    # \u66f4\u65b0\u5bfe\u8c61\u306e\u30ab\u30e9\u30e0\u3092\u6307\u5b9a\n    columns = [:email, :name, :updated_at]\n\n    # \u91cd\u8907\u6642\u306e\u66f4\u65b0\u8a2d\u5b9a\n    options = {\n      on_duplicate_key_update: {\n        conflict_target: [:email],\n        columns: [:name, :updated_at]\n      },\n      validate: true,\n      batch_size: 1000\n    }\n\n    User.import columns, records, options\n  end\n\n  def self.conditional_update(records)\n    # \u6761\u4ef6\u4ed8\u304d\u66f4\u65b0\u306e\u8a2d\u5b9a\n    options = {\n      on_duplicate_key_update: {\n        conflict_target: [:email],\n        columns: [:name],\n        # \u65e2\u5b58\u30ec\u30b3\u30fc\u30c9\u3088\u308a\u65b0\u3057\u3044\u5834\u5408\u306e\u307f\u66f4\u65b0\n        condition: \"users.updated_at &lt; EXCLUDED.updated_at\"\n      }\n    }\n\n    User.import records, options\n  end\nend<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-14\">\u30ab\u30b9\u30bf\u30e0\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u306e\u8ffd\u52a0\u65b9\u6cd5<\/h3>\n\n\n\n<p>\u8907\u96d1\u306a\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u30ed\u30b8\u30c3\u30af\u3092\u5b9f\u88c5\u3059\u308b\u4f8b\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class User &lt; ApplicationRecord\n  # \u30d0\u30eb\u30af\u30a4\u30f3\u30dd\u30fc\u30c8\u7528\u306e\u30ab\u30b9\u30bf\u30e0\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\n  validate :validate_bulk_import, if: :bulk_importing?\n\n  class &lt;&lt; self\n    attr_accessor :bulk_importing\n    attr_accessor :existing_emails # \u65e2\u5b58\u306e\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9\u30ad\u30e3\u30c3\u30b7\u30e5\n  end\n\n  def bulk_importing?\n    self.class.bulk_importing\n  end\n\n  private\n\n  def validate_bulk_import\n    # \u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9\u306e\u30c9\u30e1\u30a4\u30f3\u5236\u9650\n    unless email.end_with?('@company.com')\n      errors.add(:email, 'must be a company email')\n    end\n\n    # \u65e2\u5b58\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9\u3068\u306e\u91cd\u8907\u30c1\u30a7\u30c3\u30af\n    if self.class.existing_emails&amp;.include?(email)\n      errors.add(:email, 'already exists')\n    end\n  end\nend\n\nclass CustomImporter\n  def self.import_with_validation(records)\n    User.bulk_importing = true\n\n    # \u65e2\u5b58\u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9\u306e\u30ad\u30e3\u30c3\u30b7\u30e5\n    User.existing_emails = User.pluck(:email).to_set\n\n    begin\n      # \u30ab\u30b9\u30bf\u30e0\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u4ed8\u304d\u3067\u30a4\u30f3\u30dd\u30fc\u30c8\n      result = User.import records,\n        validate: true,\n        batch_size: 1000,\n        track_validation_failures: true\n\n      if result.failed_instances.any?\n        log_validation_failures(result.failed_instances)\n      end\n\n      result\n    ensure\n      User.bulk_importing = false\n      User.existing_emails = nil\n    end\n  end\n\n  private\n\n  def self.log_validation_failures(failed_instances)\n    failed_instances.each do |instance|\n      Rails.logger.error(\n        \"Import validation failed: #{instance.errors.full_messages.join(', ')}\"\n      )\n    end\n  end\nend<\/pre>\n\n\n\n<p>\u9ad8\u5ea6\u306a\u4f7f\u7528\u4f8b\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u91cd\u8907\u30c1\u30a7\u30c3\u30af\u306e\u6700\u9069\u5316<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">def optimized_duplicate_check(records)\n  # \u30e1\u30fc\u30eb\u30a2\u30c9\u30ec\u30b9\u306e\u4e00\u62ec\u91cd\u8907\u30c1\u30a7\u30c3\u30af\n  emails = records.map { |r| r[:email] }\n  existing = User.where(email: emails).pluck(:email).to_set\n\n  # \u91cd\u8907\u3057\u3066\u3044\u306a\u3044\u30ec\u30b3\u30fc\u30c9\u306e\u307f\u3092\u62bd\u51fa\n  unique_records = records.reject { |r| existing.include?(r[:email]) }\n\n  User.import unique_records\nend<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30ab\u30b9\u30bf\u30e0\u30b3\u30fc\u30eb\u30d0\u30c3\u30af\u306e\u5b9f\u88c5<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class User &lt; ApplicationRecord\n  after_import_save do |user|\n    # \u30a4\u30f3\u30dd\u30fc\u30c8\u5f8c\u306e\u8ffd\u52a0\u51e6\u7406\n    UserMailer.welcome_email(user).deliver_later\n  end\nend<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u306e\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6700\u9069\u5316<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class FastValidator\n  def self.validate_batch(records)\n    valid_records = []\n    invalid_records = []\n\n    # \u30d0\u30c3\u30c1\u51e6\u7406\u306b\u3088\u308b\u4e8b\u524d\u691c\u8a3c\n    email_pattern = \/\\A[\\w+\\-.]+@[a-z\\d\\-.]+\\.[a-z]+\\z\/i\n\n    records.each do |record|\n      if record[:email] =~ email_pattern\n        valid_records &lt;&lt; record\n      else\n        invalid_records &lt;&lt; record\n      end\n    end<\/pre>\n\n\n[valid_records, invalid_records]\n\n\n\n<p>end end<\/p>\n\n\n\n<p>\u3053\u308c\u3089\u306e\u9ad8\u5ea6\u306a\u5b9f\u88c5\u306b\u3088\u308a\u3001\u8907\u96d1\u306a\u30d3\u30b8\u30cd\u30b9\u30ed\u30b8\u30c3\u30af\u3092\u7dad\u6301\u3057\u306a\u304c\u3089\u3001\u52b9\u7387\u7684\u306a\u30c7\u30fc\u30bf\u30a4\u30f3\u30dd\u30fc\u30c8\u3092\u5b9f\u73fe\u3067\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-15\">\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u30e2\u30cb\u30bf\u30ea\u30f3\u30b0\u3068\u30c1\u30e5\u30fc\u30cb\u30f3\u30b0<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-16\">\u5b9f\u884c\u6642\u9593\u3068\u30e1\u30e2\u30ea\u4f7f\u7528\u91cf\u306e\u8a08\u6e2c\u65b9\u6cd5<\/h3>\n\n\n\n<p>ActiveRecord::Import\u306e\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u3092\u6b63\u78ba\u306b\u628a\u63e1\u3059\u308b\u305f\u3081\u306b\u3001\u5b9f\u884c\u6642\u9593\u3068\u30e1\u30e2\u30ea\u4f7f\u7528\u91cf\u3092\u8a08\u6e2c\u3059\u308b\u65b9\u6cd5\u3092\u7d39\u4ecb\u3057\u307e\u3059\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">require 'benchmark'\nrequire 'get_process_mem'\nrequire 'memory_profiler'\n\nclass ImportProfiler\n  def self.profile_import(records)\n    memory_before = GetProcessMem.new.mb\n    time = Benchmark.measure do\n      ActiveRecord::Base.transaction do\n        User.import records\n      end\n    end\n    memory_after = GetProcessMem.new.mb\n\n    {\n      real_time: time.real.round(2),\n      system_time: time.systime.round(2),\n      user_time: time.utime.round(2),\n      memory_used: (memory_after - memory_before).round(2)\n    }\n  end\n\n  def self.detailed_memory_profile(records)\n    MemoryProfiler.report do\n      User.import records\n    end.pretty_print\n  end\nend\n\n# \u4f7f\u7528\u4f8b\u3068\u7d50\u679c\u306e\u89e3\u6790\nresults = ImportProfiler.profile_import(large_dataset)\nputs \"\u5b9f\u884c\u6642\u9593: #{results[:real_time]}\u79d2\"\nputs \"\u30e1\u30e2\u30ea\u4f7f\u7528\u91cf: #{results[:memory_used]}MB\"<\/pre>\n\n\n\n<p>\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u8a08\u6e2c\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9\uff1a<\/p>\n\n\n<div id=\"id-6353fde2-222e-406b-920c-66e5576b6eb6\">\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u8a08\u6e2c\u9805\u76ee<\/th><th>\u63a8\u5968\u30c4\u30fc\u30eb<\/th><th>\u7528\u9014<\/th><\/tr><\/thead><tbody><tr><td>\u5b9f\u884c\u6642\u9593<\/td><td>Benchmark<\/td><td>\u5168\u4f53\u7684\u306a\u51e6\u7406\u6642\u9593\u306e\u8a08\u6e2c<\/td><\/tr><tr><td>\u30e1\u30e2\u30ea\u4f7f\u7528\u91cf<\/td><td>get_process_mem<\/td><td>\u30ea\u30a2\u30eb\u30bf\u30a4\u30e0\u306e\u30e1\u30e2\u30ea\u76e3\u8996<\/td><\/tr><tr><td>SQL\u30af\u30a8\u30ea<\/td><td>ActiveRecord::QueryLogs<\/td><td>\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30af\u30a8\u30ea\u306e\u5206\u6790<\/td><\/tr><tr><td>\u30e1\u30e2\u30ea\u30ea\u30fc\u30af<\/td><td>memory_profiler<\/td><td>\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u5272\u308a\u5f53\u3066\u306e\u8ffd\u8de1<\/td><\/tr><\/tbody><\/table><\/figure>\n<\/div>\n\n\n<h3 class=\"wp-block-heading\" id=\"i-17\">\u30dc\u30c8\u30eb\u30cd\u30c3\u30af\u7279\u5b9a\u3068\u6539\u5584\u30a2\u30d7\u30ed\u30fc\u30c1<\/h3>\n\n\n\n<p>\u4e00\u822c\u7684\u306a\u30dc\u30c8\u30eb\u30cd\u30c3\u30af\u3068\u305d\u306e\u6539\u5584\u65b9\u6cd5\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30af\u30a8\u30ea\u306e\u6700\u9069\u5316<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class QueryOptimizer\n  def self.analyze_import_queries(&amp;block)\n    queries = []\n\n    ActiveSupport::Notifications.subscribe('sql.active_record') do |*args|\n      event = ActiveSupport::Notifications::Event.new(*args)\n      queries &lt;&lt; {\n        sql: event.payload[:sql],\n        duration: event.duration\n      }\n    end\n\n    block.call\n\n    # \u30af\u30a8\u30ea\u5206\u6790\u7d50\u679c\u306e\u51fa\u529b\n    queries.sort_by { |q| -q[:duration] }.each do |query|\n      puts \"Duration: #{query[:duration].round(2)}ms\"\n      puts \"SQL: #{query[:sql]}\\n\\n\"\n    end\n  end\nend\n\n# \u4f7f\u7528\u4f8b\nQueryOptimizer.analyze_import_queries do\n  User.import large_dataset\nend<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30e1\u30e2\u30ea\u4f7f\u7528\u91cf\u306e\u6700\u9069\u5316<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class MemoryOptimizer\n  def self.import_with_gc_control(records)\n    results = { gc_stats: [], memory_usage: [] }\n\n    GC.disable # GC\u3092\u4e00\u6642\u7684\u306b\u7121\u52b9\u5316\n\n    records.each_slice(1000) do |batch|\n      results[:gc_stats] &lt;&lt; GC.stat\n      results[:memory_usage] &lt;&lt; GetProcessMem.new.mb\n\n      User.import batch\n\n      # \u5fc5\u8981\u306b\u5fdc\u3058\u3066GC\u3092\u624b\u52d5\u5b9f\u884c\n      if GetProcessMem.new.mb &gt; 1000 # 1GB\u8d85\u904e\u6642\n        GC.enable\n        GC.start\n        GC.disable\n      end\n    end\n\n    GC.enable\n    results\n  end\nend<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u6700\u9069\u5316<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class IndexOptimizer\n  def self.analyze_indexes\n    # \u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u306e\u4f7f\u7528\u72b6\u6cc1\u3092\u5206\u6790\n    ActiveRecord::Base.connection.execute(&lt;&lt;-SQL)\n      SELECT\n        schemaname,\n        tablename,\n        indexname,\n        idx_scan,\n        idx_tup_read,\n        idx_tup_fetch\n      FROM\n        pg_stat_user_indexes\n      WHERE\n        idx_scan = 0\n      ORDER BY\n        schemaname, tablename;\n    SQL\n  end\n\n  def self.suggest_indexes(records)\n    # \u30a4\u30f3\u30dd\u30fc\u30c8\u30c7\u30fc\u30bf\u306e\u5206\u6790\u304b\u3089\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u63d0\u6848\n    columns = records.first.keys\n    column_values = columns.map do |col|\n      values = records.map { |r| r[col] }\n      {\n        column: col,\n        unique_ratio: values.uniq.size.to_f \/ values.size\n      }\n    end\n\n    # \u4e00\u610f\u6027\u306e\u9ad8\u3044\u30ab\u30e9\u30e0\u3092\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u5019\u88dc\u3068\u3057\u3066\u63d0\u6848\n    column_values\n      .select { |cv| cv[:unique_ratio] &gt; 0.8 }\n      .map { |cv| cv[:column] }\n  end\nend<\/pre>\n\n\n\n<p>\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6539\u5584\u306e\u305f\u3081\u306e\u30c1\u30a7\u30c3\u30af\u30ea\u30b9\u30c8\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u8a2d\u5b9a\u306e\u6700\u9069\u5316<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>innodb_buffer_pool_size<\/code>\u306e\u9069\u5207\u306a\u8a2d\u5b9a<\/li>\n\n\n\n<li><code>max_allowed_packet<\/code>\u306e\u8abf\u6574<\/li>\n\n\n\n<li>\u4e00\u6642\u7684\u306a\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u7121\u52b9\u5316\u306e\u691c\u8a0e<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u8a2d\u5b9a\u306e\u6700\u9069\u5316<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u9069\u5207\u306a\u30d0\u30c3\u30c1\u30b5\u30a4\u30ba\u306e\u8a2d\u5b9a<\/li>\n\n\n\n<li>\u4e0d\u8981\u306a\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u306e\u7121\u52b9\u5316<\/li>\n\n\n\n<li>\u30c8\u30e9\u30f3\u30b6\u30af\u30b7\u30e7\u30f3\u7bc4\u56f2\u306e\u6700\u9069\u5316<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u30b7\u30b9\u30c6\u30e0\u30ea\u30bd\u30fc\u30b9\u306e\u76e3\u8996<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>CPU\u4f7f\u7528\u7387<\/li>\n\n\n\n<li>\u30e1\u30e2\u30ea\u4f7f\u7528\u91cf<\/li>\n\n\n\n<li>\u30c7\u30a3\u30b9\u30afI\/O<\/li>\n\n\n\n<li>\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u5e2f\u57df<\/li>\n<\/ul>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># \u7dcf\u5408\u7684\u306a\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u30e2\u30cb\u30bf\u30ea\u30f3\u30b0\u4f8b\nclass ImportMonitor\n  def self.monitor_import(records)\n    stats = {\n      start_time: Time.current,\n      memory: [],\n      gc_stats: [],\n      query_stats: []\n    }\n\n    # \u30af\u30a8\u30ea\u76e3\u8996\u306e\u8a2d\u5b9a\n    ActiveSupport::Notifications.subscribe('sql.active_record') do |*args|\n      event = ActiveSupport::Notifications::Event.new(*args)\n      stats[:query_stats] &lt;&lt; {\n        sql: event.payload[:sql],\n        duration: event.duration\n      }\n    end\n\n    # \u30e1\u30e2\u30ea\u3068 GC \u306e\u76e3\u8996\n    monitoring_thread = Thread.new do\n      while true\n        stats[:memory] &lt;&lt; GetProcessMem.new.mb\n        stats[:gc_stats] &lt;&lt; GC.stat\n        sleep 1\n      end\n    end\n\n    # \u30a4\u30f3\u30dd\u30fc\u30c8\u5b9f\u884c\n    begin\n      User.import records\n    ensure\n      monitoring_thread.kill\n      stats[:end_time] = Time.current\n    end\n\n    generate_report(stats)\n  end\n\n  private\n\n  def self.generate_report(stats)\n    {\n      duration: stats[:end_time] - stats[:start_time],\n      max_memory: stats[:memory].max,\n      average_memory: stats[:memory].sum \/ stats[:memory].size,\n      gc_collections: stats[:gc_stats].last[:count] - stats[:gc_stats].first[:count],\n      slow_queries: stats[:query_stats].select { |q| q[:duration] &gt; 100 }.size\n    }\n  end\nend<\/pre>\n\n\n\n<p>\u3053\u308c\u3089\u306e\u30e2\u30cb\u30bf\u30ea\u30f3\u30b0\u3068\u30c1\u30e5\u30fc\u30cb\u30f3\u30b0\u624b\u6cd5\u3092\u9069\u5207\u306b\u7d44\u307f\u5408\u308f\u305b\u308b\u3053\u3068\u3067\u3001\u30a4\u30f3\u30dd\u30fc\u30c8\u51e6\u7406\u306e\u52b9\u7387\u3092\u6700\u5927\u9650\u306b\u9ad8\u3081\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-18\">\u5b9f\u969b\u306e\u30e6\u30fc\u30b9\u30b1\u30fc\u30b9\u3068\u5b9f\u88c5\u4f8b<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-19\">\u5927\u898f\u6a21\u30c7\u30fc\u30bf\u79fb\u884c\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3067\u306e\u6d3b\u7528\u4e8b\u4f8b<\/h3>\n\n\n\n<p>\u5927\u898f\u6a21\u306a\u30c7\u30fc\u30bf\u79fb\u884c\u306f\u3001ActiveRecord::Import\u306e\u5178\u578b\u7684\u306a\u30e6\u30fc\u30b9\u30b1\u30fc\u30b9\u306e\u4e00\u3064\u3067\u3059\u3002\u4ee5\u4e0b\u306b\u3001\u5b9f\u969b\u306e\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3067\u4f7f\u7528\u3055\u308c\u305f\u5b9f\u88c5\u4f8b\u3092\u7d39\u4ecb\u3057\u307e\u3059\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class LegacyDataMigrator\n  class &lt;&lt; self\n    def migrate_legacy_data\n      # \u79fb\u884c\u306e\u9032\u6357\u7ba1\u7406\n      migration_log = MigrationLog.create!(\n        started_at: Time.current,\n        status: 'running'\n      )\n\n      begin\n        # \u30ec\u30ac\u30b7\u30fcDB\u304b\u3089\u306e\u30c7\u30fc\u30bf\u53d6\u5f97\n        legacy_records = LegacyDatabase.fetch_all_records\n        total_count = legacy_records.count\n\n        # \u30c7\u30fc\u30bf\u5909\u63db\u3068\u79fb\u884c\n        transformed_records = transform_records(legacy_records)\n        import_records(transformed_records) do |progress|\n          migration_log.update!(\n            progress_percentage: progress,\n            last_updated_at: Time.current\n          )\n        end\n\n        migration_log.update!(\n          status: 'completed',\n          completed_at: Time.current\n        )\n      rescue =&gt; e\n        migration_log.update!(\n          status: 'failed',\n          error_message: e.message\n        )\n        raise e\n      end\n    end\n\n    private\n\n    def transform_records(legacy_records)\n      legacy_records.map do |record|\n        {\n          new_id: generate_new_id(record.old_id),\n          name: normalize_name(record.name),\n          email: normalize_email(record.email),\n          status: map_status(record.old_status),\n          metadata: build_metadata(record)\n        }\n      end\n    end\n\n    def import_records(records)\n      total = records.size\n      imported = 0\n\n      records.each_slice(1000) do |batch|\n        ActiveRecord::Base.transaction do\n          User.import batch,\n            validate: true,\n            on_duplicate_key_update: [:name, :email, :status, :metadata]\n        end\n\n        imported += batch.size\n        progress = (imported.to_f \/ total * 100).round(2)\n        yield progress if block_given?\n      end\n    end\n  end\nend<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-20\">\u5b9a\u671f\u7684\u306a\u30c7\u30fc\u30bf\u30a4\u30f3\u30dd\u30fc\u30c8\u306e\u81ea\u52d5\u5316\u5b9f\u88c5<\/h3>\n\n\n\n<p>\u5b9a\u671f\u7684\u306a\u30c7\u30fc\u30bf\u30a4\u30f3\u30dd\u30fc\u30c8\u3092\u81ea\u52d5\u5316\u3059\u308b\u5b9f\u88c5\u4f8b\uff1a<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">class AutomatedDataImporter\n  class &lt;&lt; self\n    def setup_automated_import\n      # \u8a2d\u5b9a\u306e\u8aad\u307f\u8fbc\u307f\n      config = YAML.load_file(Rails.root.join('config', 'importers.yml'))\n\n      # Sidekiq\u3092\u4f7f\u7528\u3057\u305f\u30b9\u30b1\u30b8\u30e5\u30fc\u30eb\u8a2d\u5b9a\n      Sidekiq.set_schedule('daily_data_import', {\n        'cron' =&gt; '0 0 * * *', # \u6bce\u65e5\u5348\u524d0\u6642\u306b\u5b9f\u884c\n        'class' =&gt; 'DataImportWorker',\n        'queue' =&gt; 'importers'\n      })\n    end\n  end\nend\n\nclass DataImportWorker\n  include Sidekiq::Worker\n  sidekiq_options retry: 3\n\n  def perform\n    # \u5916\u90e8API\u304b\u3089\u306e\u30c7\u30fc\u30bf\u53d6\u5f97\n    raw_data = fetch_external_data\n\n    # \u30c7\u30fc\u30bf\u306e\u524d\u51e6\u7406\n    processed_data = preprocess_data(raw_data)\n\n    # \u30a4\u30f3\u30dd\u30fc\u30c8\u5b9f\u884c\n    import_with_monitoring(processed_data)\n  rescue =&gt; e\n    # \u30a8\u30e9\u30fc\u901a\u77e5\n    NotificationService.notify_error(e)\n    raise e\n  end\n\n  private\n\n  def fetch_external_data\n    response = ExternalApiClient.fetch_daily_data\n    JSON.parse(response.body)\n  end\n\n  def preprocess_data(raw_data)\n    raw_data.map do |item|\n      {\n        external_id: item['id'],\n        name: item['name'],\n        processed_at: Time.current,\n        status: 'pending'\n      }\n    end\n  end\n\n  def import_with_monitoring(data)\n    RetryableImporter.import_with_retry(data) do |result|\n      if result.success?\n        # \u6210\u529f\u30ed\u30b0\u306e\u8a18\u9332\n        ImportLog.create!(\n          source: 'daily_import',\n          records_count: data.size,\n          status: 'success'\n        )\n\n        # Slack\u901a\u77e5\n        NotificationService.notify_success(\n          \"Daily import completed: #{data.size} records imported\"\n        )\n      else\n        handle_import_failure(result)\n      end\n    end\n  end\nend\n\nclass RetryableImporter\n  def self.import_with_retry(data, max_retries: 3)\n    retries = 0\n    begin\n      result = User.import data,\n        validate: true,\n        batch_size: 1000,\n        on_duplicate_key_update: [:name, :status, :processed_at]\n\n      yield(result) if block_given?\n      result\n    rescue =&gt; e\n      retries += 1\n      if retries &lt;= max_retries\n        sleep(2 ** retries) # \u6307\u6570\u30d0\u30c3\u30af\u30aa\u30d5\n        retry\n      else\n        raise e\n      end\n    end\n  end\nend<\/pre>\n\n\n\n<p>\u5b9f\u969b\u306e\u30e6\u30fc\u30b9\u30b1\u30fc\u30b9\u306b\u304a\u3051\u308b\u91cd\u8981\u306a\u30dd\u30a4\u30f3\u30c8\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30a8\u30e9\u30fc\u30cf\u30f3\u30c9\u30ea\u30f3\u30b0\u3068\u5fa9\u65e7<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30c8\u30e9\u30f3\u30b6\u30af\u30b7\u30e7\u30f3\u7ba1\u7406<\/li>\n\n\n\n<li>\u30ea\u30c8\u30e9\u30a4\u6a5f\u69cb\u306e\u5b9f\u88c5<\/li>\n\n\n\n<li>\u30a8\u30e9\u30fc\u901a\u77e5\u306e\u8a2d\u5b9a<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u76e3\u8996\u3068\u30ed\u30ae\u30f3\u30b0<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u51e6\u7406\u306e\u9032\u6357\u7ba1\u7406<\/li>\n\n\n\n<li>\u30a8\u30e9\u30fc\u30ed\u30b0\u306e\u8a18\u9332<\/li>\n\n\n\n<li>\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u30e1\u30c8\u30ea\u30af\u30b9\u306e\u53ce\u96c6<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30c7\u30fc\u30bf\u306e\u6574\u5408\u6027\u78ba\u4fdd<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u306e\u9069\u5207\u306a\u8a2d\u5b9a<\/li>\n\n\n\n<li>\u91cd\u8907\u30c7\u30fc\u30bf\u306e\u51e6\u7406<\/li>\n\n\n\n<li>\u95a2\u9023\u30c7\u30fc\u30bf\u306e\u6574\u5408\u6027\u30c1\u30a7\u30c3\u30af<\/li>\n<\/ul>\n\n\n\n<p>\u3053\u308c\u3089\u306e\u5b9f\u88c5\u4f8b\u306f\u3001\u5b9f\u969b\u306e\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3067\u4f7f\u7528\u3055\u308c\u308b\u4e00\u822c\u7684\u306a\u30d1\u30bf\u30fc\u30f3\u3092\u793a\u3057\u3066\u3044\u307e\u3059\u3002\u5177\u4f53\u7684\u306a\u8981\u4ef6\u306b\u5fdc\u3058\u3066\u3001\u3053\u308c\u3089\u306e\u30b3\u30fc\u30c9\u3092\u30ab\u30b9\u30bf\u30de\u30a4\u30ba\u3057\u3066\u4f7f\u7528\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Warning: Undefined array key &#8220;is_admin&#8221; in \/home\/xs392991\/dexall.co.jp\/public_html\/articles\/wp-content\/themes\/ &#8230; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[],"class_list":{"0":"post-1217","1":"post","2":"type-post","3":"status-publish","4":"format-standard","6":"category-ruby","7":"nothumb"},"_links":{"self":[{"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/posts\/1217","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1217"}],"version-history":[{"count":1,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/posts\/1217\/revisions"}],"predecessor-version":[{"id":1218,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/posts\/1217\/revisions\/1218"}],"wp:attachment":[{"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1217"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1217"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1217"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}