{"id":3328,"date":"2025-03-24T08:46:26","date_gmt":"2025-03-23T23:46:26","guid":{"rendered":"https:\/\/dexall.co.jp\/articles\/?p=3328"},"modified":"2025-03-24T08:46:56","modified_gmt":"2025-03-23T23:46:56","slug":"rails%e9%96%8b%e7%99%ba%e8%80%85%e3%81%ae%e3%81%9f%e3%82%81%e3%81%ae%e5%ae%9f%e8%b7%b5redis%e5%ae%8c%e5%85%a8%e3%82%ac%e3%82%a4%e3%83%89%ef%bc%9a%e3%83%91%e3%83%95%e3%82%a9%e3%83%bc%e3%83%9e%e3%83%b3","status":"publish","type":"post","link":"https:\/\/dexall.co.jp\/articles\/?p=3328","title":{"rendered":"Rails\u958b\u767a\u8005\u306e\u305f\u3081\u306e\u5b9f\u8df5Redis\u5b8c\u5168\u30ac\u30a4\u30c9\uff1a\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u304c3\u500d\u306b\u306a\u308b\u5c0e\u5165\u30fb\u6d3b\u7528\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\">Redis\u3068\u306f\uff1fRails\u958b\u767a\u8005\u306e\u305f\u3081\u306e\u57fa\u790e\u77e5\u8b58<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-1\">\u9ad8\u901f\u306a\u51e6\u7406\u3092\u5b9f\u73fe\u3059\u308b\u30a4\u30f3\u30e1\u30e2\u30ea\u30c7\u30fc\u30bf\u30b9\u30c8\u30a2<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-2\">Rails\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3067\u306e\u4e3b\u306a\u7528\u9014\u3068\u5229\u70b9<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-3\">Rails\u306bRedis\u3092\u5c0e\u5165\u3059\u308b\u624b\u9806<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-4\">\u5fc5\u8981\u306agem\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3068\u8a2d\u5b9a\u65b9\u6cd5<\/a>      <\/li>      <li>        <a href=\"#i-5\">Redis\u63a5\u7d9a\u8a2d\u5b9a\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-6\">\u958b\u767a\u74b0\u5883\u3068\u672c\u756a\u74b0\u5883\u3067\u306e\u6ce8\u610f\u70b9<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-7\">Rails\u3067\u306e\u30ad\u30e3\u30c3\u30b7\u30e5\u7ba1\u7406\u306bRedis\u3092\u6d3b\u7528\u3059\u308b<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-8\">Redis Store\u3092\u4f7f\u3063\u305f\u30ad\u30e3\u30c3\u30b7\u30e5\u306e\u5b9f\u88c5\u65b9\u6cd5<\/a>      <\/li>      <li>        <a href=\"#i-9\">\u30ad\u30e3\u30c3\u30b7\u30e5\u5236\u5fa1\u3068\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6700\u9069\u5316\u306e\u30b3\u30c4<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-10\">\u4e00\u822c\u7684\u306a\u30ad\u30e3\u30c3\u30b7\u30e5\u30d1\u30bf\u30fc\u30f3\u3068\u5b9f\u88c5\u4f8b<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-11\">\u30bb\u30c3\u30b7\u30e7\u30f3\u7ba1\u7406\u3068\u30b8\u30e7\u30d6\u30ad\u30e5\u30fc\u306e\u5b9f\u88c5<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-12\">Redis\u3067\u306e\u30bb\u30c3\u30b7\u30e7\u30f3\u30b9\u30c8\u30a2C\u306e\u8a2d\u5b9a\u3068\u904b\u7528<\/a>      <\/li>      <li>        <a href=\"#i-13\">Sidekiq\u3068\u7d44\u307f\u5408\u308f\u305b\u305f\u975e\u540c\u671f\u51e6\u7406\u306e\u5b9f\u88c5<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-14\">\u30b8\u30e7\u30d6\u30ad\u30e5\u30fc\u306e\u30e2\u30cb\u30bf\u30ea\u30f3\u30b0\u3068\u7ba1\u7406\u65b9\u6cd5<\/a>      <\/li>    <\/ul>  <\/li>  <li>    <a href=\"#i-15\">\u5b9f\u8df5\u7684\u306aRedis\u6d3b\u7528\u30d1\u30bf\u30fc\u30f3\u96c6<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-16\">\u30ea\u30a2\u30eb\u30bf\u30a4\u30e0\u901a\u77e5\u30b7\u30b9\u30c6\u30e0\u306e\u69cb\u7bc9\u65b9\u6cd5<\/a>      <\/li>      <li>        <a href=\"#i-17\">\u30e9\u30f3\u30ad\u30f3\u30b0\u6a5f\u80fd\u306e\u52b9\u7387\u7684\u306a\u5b9f\u88c5\u30c6\u30af\u30cb\u30c3\u30af<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-18\">\u30ec\u30fc\u30c8\u5236\u9650\u306e\u5b9f\u88c5\u3068API\u306e\u4fdd\u8b77\u65b9\u6cd5<\/a>      <\/li>    <\/ul>  <\/li>  <li class=\"last\">    <a href=\"#i-19\">Redis\u306e\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u30c1\u30e5\u30fc\u30cb\u30f3\u30b0\u3068\u30e2\u30cb\u30bf\u30ea\u30f3\u30b0<\/a>    <ul class=\"menu_level_1\">      <li class=\"first\">        <a href=\"#i-20\">\u30e1\u30e2\u30ea\u4f7f\u7528\u91cf\u306e\u6700\u9069\u5316\u3068\u76e3\u8996\u65b9\u6cd5<\/a>      <\/li>      <li>        <a href=\"#i-21\">Redis\u306e\u30d0\u30c3\u30af\u30a2\u30c3\u30d7\u3068\u5fa9\u65e7\u6226\u7565<\/a>      <\/li>      <li class=\"last\">        <a href=\"#i-22\">\u672c\u756a\u74b0\u5883\u3067\u306e\u904b\u7528\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9<\/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\">Redis\u3068\u306f\uff1fRails\u958b\u767a\u8005\u306e\u305f\u3081\u306e\u57fa\u790e\u77e5\u8b58<\/h2>\n\n\n\n<p>Redis\u306f\u3001\u9ad8\u901f\u306a\u30a4\u30f3\u30e1\u30e2\u30ea\u30c7\u30fc\u30bf\u30b9\u30c8\u30a2\u3068\u3057\u3066\u77e5\u3089\u308c\u308b\u5f37\u529b\u306a\u30aa\u30fc\u30d7\u30f3\u30bd\u30fc\u30b9\u30bd\u30d5\u30c8\u30a6\u30a7\u30a2\u3067\u3059\u3002Rails\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u3092\u5287\u7684\u306b\u5411\u4e0a\u3055\u305b\u308b\u91cd\u8981\u306a\u30c4\u30fc\u30eb\u3068\u3057\u3066\u3001\u591a\u304f\u306e\u958b\u767a\u8005\u306b\u611b\u7528\u3055\u308c\u3066\u3044\u307e\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-1\">\u9ad8\u901f\u306a\u51e6\u7406\u3092\u5b9f\u73fe\u3059\u308b\u30a4\u30f3\u30e1\u30e2\u30ea\u30c7\u30fc\u30bf\u30b9\u30c8\u30a2<\/h3>\n\n\n\n<p>Redis\u306e\u6700\u5927\u306e\u7279\u5fb4\u306f\u3001\u305d\u306e\u30b9\u30d4\u30fc\u30c9\u306b\u3042\u308a\u307e\u3059\u3002\u5f93\u6765\u306e\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304c\u30c7\u30a3\u30b9\u30af\u306b\u30c7\u30fc\u30bf\u3092\u4fdd\u5b58\u3059\u308b\u306e\u306b\u5bfe\u3057\u3001Redis\u306f\u30e1\u30a4\u30f3\u30e1\u30e2\u30ea\uff08RAM\uff09\u4e0a\u3067\u30c7\u30fc\u30bf\u3092\u7ba1\u7406\u3057\u307e\u3059\u3002\u3053\u308c\u306b\u3088\u308a\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u8aad\u307f\u66f8\u304d\u306e\u5fdc\u7b54\u6642\u9593\u304c\u6570\u30de\u30a4\u30af\u30ed\u79d2\u5358\u4f4d<\/li>\n\n\n\n<li>1\u79d2\u9593\u306b10\u4e07\u4ef6\u4ee5\u4e0a\u306e\u64cd\u4f5c\u304c\u53ef\u80fd<\/li>\n\n\n\n<li>\u8907\u96d1\u306a\u30af\u30a8\u30ea\u306e\u5b9f\u884c\u3082\u9ad8\u901f<\/li>\n<\/ul>\n\n\n\n<p>\u3055\u3089\u306b\u3001Redis\u306f\u8c4a\u5bcc\u306a\u30c7\u30fc\u30bf\u69cb\u9020\u3092\u30b5\u30dd\u30fc\u30c8\u3057\u3066\u3044\u307e\u3059\uff1a<\/p>\n\n\n<div id=\"id-97caa2e4-914d-400d-97ff-ec90ed1ac56e\">\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u30c7\u30fc\u30bf\u69cb\u9020<\/th><th>\u4e3b\u306a\u7528\u9014<\/th><\/tr><\/thead><tbody><tr><td>Strings<\/td><td>\u30ad\u30e3\u30c3\u30b7\u30e5\u3001\u30ab\u30a6\u30f3\u30bf\u30fc<\/td><\/tr><tr><td>Lists<\/td><td>\u30ad\u30e5\u30fc\u3001\u6700\u65b0\u60c5\u5831\u306e\u7ba1\u7406<\/td><\/tr><tr><td>Sets<\/td><td>\u4e00\u610f\u306a\u8981\u7d20\u306e\u96c6\u5408\u7ba1\u7406<\/td><\/tr><tr><td>Sorted Sets<\/td><td>\u30e9\u30f3\u30ad\u30f3\u30b0\u3001\u512a\u5148\u5ea6\u4ed8\u304d\u30ad\u30e5\u30fc<\/td><\/tr><tr><td>Hashes<\/td><td>\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u306e\u5c5e\u6027\u7ba1\u7406<\/td><\/tr><\/tbody><\/table><\/figure>\n<\/div>\n\n\n<h3 class=\"wp-block-heading\" id=\"i-2\">Rails\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3067\u306e\u4e3b\u306a\u7528\u9014\u3068\u5229\u70b9<\/h3>\n\n\n\n<p>Rails\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306b\u304a\u3044\u3066\u3001Redis\u306f\u4ee5\u4e0b\u306e\u3088\u3046\u306a\u5834\u9762\u3067\u7279\u306b\u52b9\u679c\u3092\u767a\u63ee\u3057\u307e\u3059\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30ad\u30e3\u30c3\u30b7\u30f3\u30b0<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30d3\u30e5\u30fc\u306e\u30d5\u30e9\u30b0\u30e1\u30f3\u30c8\u30ad\u30e3\u30c3\u30b7\u30e5<\/li>\n\n\n\n<li>API\u30ec\u30b9\u30dd\u30f3\u30b9\u306e\u30ad\u30e3\u30c3\u30b7\u30e5<\/li>\n\n\n\n<li>\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u30af\u30a8\u30ea\u7d50\u679c\u306e\u30ad\u30e3\u30c3\u30b7\u30e5<br>\u2192 \u30ec\u30b9\u30dd\u30f3\u30b9\u30bf\u30a4\u30e0\u3092\u6700\u592790%\u524a\u6e1b\u53ef\u80fd<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30bb\u30c3\u30b7\u30e7\u30f3\u7ba1\u7406<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u5206\u6563\u74b0\u5883\u3067\u306e\u7d71\u4e00\u3057\u305f\u30bb\u30c3\u30b7\u30e7\u30f3\u7ba1\u7406<\/li>\n\n\n\n<li>\u30b9\u30b1\u30fc\u30e9\u30d6\u30eb\u306a\u30e6\u30fc\u30b6\u30fc\u30bb\u30c3\u30b7\u30e7\u30f3\u306e\u51e6\u7406<\/li>\n\n\n\n<li>\u9ad8\u901f\u306a\u30bb\u30c3\u30b7\u30e7\u30f3\u30c7\u30fc\u30bf\u306e\u30a2\u30af\u30bb\u30b9<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30b8\u30e7\u30d6\u30ad\u30e5\u30fc<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Sidekiq\u3068\u7d44\u307f\u5408\u308f\u305b\u305f\u975e\u540c\u671f\u51e6\u7406<\/li>\n\n\n\n<li>\u30d0\u30c3\u30af\u30b0\u30e9\u30a6\u30f3\u30c9\u30b8\u30e7\u30d6\u306e\u52b9\u7387\u7684\u306a\u7ba1\u7406<\/li>\n\n\n\n<li>\u51e6\u7406\u306e\u512a\u5148\u5ea6\u4ed8\u3051\u3068\u5236\u5fa1<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30ea\u30a2\u30eb\u30bf\u30a4\u30e0\u6a5f\u80fd<\/strong><\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u30c1\u30e3\u30c3\u30c8\u6a5f\u80fd\u306e\u5b9f\u88c5<\/li>\n\n\n\n<li>\u901a\u77e5\u30b7\u30b9\u30c6\u30e0\u306e\u69cb\u7bc9<\/li>\n\n\n\n<li>\u30ea\u30a2\u30eb\u30bf\u30a4\u30e0\u5206\u6790<\/li>\n<\/ul>\n\n\n\n<p>\u3053\u308c\u3089\u306e\u7528\u9014\u306b\u304a\u3051\u308bRedis\u306e\u4e3b\u306a\u5229\u70b9\u306f\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u5411\u4e0a<\/strong><\/li>\n\n\n\n<li>\u5fdc\u7b54\u6642\u9593\u306e\u5927\u5e45\u306a\u524a\u6e1b<\/li>\n\n\n\n<li>\u30b5\u30fc\u30d0\u30fc\u30ea\u30bd\u30fc\u30b9\u306e\u52b9\u7387\u7684\u306a\u5229\u7528<\/li>\n\n\n\n<li>\u30b9\u30b1\u30fc\u30e9\u30d3\u30ea\u30c6\u30a3\u306e\u5411\u4e0a<\/li>\n\n\n\n<li><strong>\u958b\u767a\u52b9\u7387\u306e\u5411\u4e0a<\/strong><\/li>\n\n\n\n<li>\u30b7\u30f3\u30d7\u30eb\u306a\u5b9f\u88c5<\/li>\n\n\n\n<li>\u8c4a\u5bcc\u306agem\u306b\u3088\u308b\u30b5\u30dd\u30fc\u30c8<\/li>\n\n\n\n<li>\u67d4\u8edf\u306a\u30c7\u30fc\u30bf\u69cb\u9020\u306e\u6d3b\u7528<\/li>\n\n\n\n<li><strong>\u904b\u7528\u7ba1\u7406\u306e\u5bb9\u6613\u3055<\/strong><\/li>\n\n\n\n<li>\u76e3\u8996\u306e\u7c21\u5358\u3055<\/li>\n\n\n\n<li>\u30d0\u30c3\u30af\u30a2\u30c3\u30d7\u306e\u5bb9\u6613\u3055<\/li>\n\n\n\n<li>\u30af\u30e9\u30b9\u30bf\u30ea\u30f3\u30b0\u306e\u30b5\u30dd\u30fc\u30c8<\/li>\n<\/ul>\n\n\n\n<p>Redis\u3092\u5c0e\u5165\u3059\u308b\u3053\u3068\u3067\u3001Rails\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306f\u4ee5\u4e0b\u306e\u3088\u3046\u306a\u6539\u5584\u304c\u671f\u5f85\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=\"\"># \u5c0e\u5165\u524d\uff1a\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304b\u3089\u306e\u76f4\u63a5\u53d6\u5f97\ndef get_user_preferences\n  User.find(user_id).preferences  # \u7d04100ms\nend\n\n# \u5c0e\u5165\u5f8c\uff1aRedis\u3092\u4f7f\u7528\u3057\u305f\u30ad\u30e3\u30c3\u30b7\u30e5\ndef get_user_preferences\n  Redis.current.get(\"user:#{user_id}:preferences\") || begin\n    prefs = User.find(user_id).preferences\n    Redis.current.set(\"user:#{user_id}:preferences\", prefs)\n    prefs\n  end  # \u7d040.5ms\nend<\/pre>\n\n\n\n<p>\u3053\u306e\u3088\u3046\u306b\u3001Redis\u306f\u5358\u306a\u308b\u30ad\u30e3\u30c3\u30b7\u30e5\u30b7\u30b9\u30c6\u30e0\u3092\u8d85\u3048\u3066\u3001Rails\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u6027\u80fd\u3068\u6a5f\u80fd\u6027\u3092\u5927\u304d\u304f\u5411\u4e0a\u3055\u305b\u308b\u91cd\u8981\u306a\u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8\u3068\u306a\u3063\u3066\u3044\u307e\u3059\u3002\u6b21\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u3067\u306f\u3001\u5b9f\u969b\u306eRedis\u5c0e\u5165\u624b\u9806\u306b\u3064\u3044\u3066\u8a73\u3057\u304f\u89e3\u8aac\u3057\u3066\u3044\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-3\">Rails\u306bRedis\u3092\u5c0e\u5165\u3059\u308b\u624b\u9806<\/h2>\n\n\n\n<p>Redis\u306e\u5c0e\u5165\u306f\u3001\u9069\u5207\u306a\u624b\u9806\u3068\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9\u306b\u5f93\u3046\u3053\u3068\u3067\u3001\u30b9\u30e0\u30fc\u30ba\u306b\u9032\u3081\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002\u3053\u3053\u3067\u306f\u3001\u958b\u767a\u74b0\u5883\u304b\u3089\u672c\u756a\u74b0\u5883\u307e\u3067\u3001\u78ba\u5b9f\u306a\u5c0e\u5165\u624b\u9806\u3092\u89e3\u8aac\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-4\">\u5fc5\u8981\u306agem\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u3068\u8a2d\u5b9a\u65b9\u6cd5<\/h3>\n\n\n\n<p>\u307e\u305a\u3001Rails\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306bRedis\u3092\u5c0e\u5165\u3059\u308b\u305f\u3081\u306b\u5fc5\u8981\u306agem\u3092\u8ffd\u52a0\u3057\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=\"\"># Gemfile\ngem 'redis', '~&gt; 5.0'           # Redis\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\ngem 'redis-rails', '~&gt; 5.0'     # Rails\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\ngem 'redis-namespace'           # \u540d\u524d\u7a7a\u9593\u306b\u3088\u308b\u30ad\u30fc\u7ba1\u7406\ngem 'connection_pool'           # \u30b3\u30cd\u30af\u30b7\u30e7\u30f3\u30d7\u30fc\u30eb\u7ba1\u7406<\/pre>\n\n\n\n<p>gem\u306e\u30a4\u30f3\u30b9\u30c8\u30fc\u30eb\u5f8c\u3001\u4ee5\u4e0b\u306e\u521d\u671f\u8a2d\u5b9a\u3092\u884c\u3044\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=\"\"># config\/initializers\/redis.rb\nrequire 'redis'\nrequire 'connection_pool'\n\nRedis.current = ConnectionPool.new(size: 5, timeout: 5) do\n  Redis.new(\n    url: ENV.fetch('REDIS_URL', 'redis:\/\/localhost:6379\/0'),\n    ssl_params: { verify_mode: OpenSSL::SSL::VERIFY_NONE }\n  )\nend<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-5\">Redis\u63a5\u7d9a\u8a2d\u5b9a\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9<\/h3>\n\n\n\n<p>\u52b9\u7387\u7684\u3067\u5b89\u5168\u306aRedis\u63a5\u7d9a\u3092\u5b9f\u73fe\u3059\u308b\u305f\u3081\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9\u3092\u7d39\u4ecb\u3057\u307e\u3059\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u74b0\u5883\u5909\u6570\u306b\u3088\u308b\u8a2d\u5b9a\u7ba1\u7406<\/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=\"\"># config\/environments\/production.rb\nconfig.cache_store = :redis_cache_store, {\n  url: ENV['REDIS_URL'],\n  connect_timeout: 30,  # \u63a5\u7d9a\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8: 30\u79d2\n  read_timeout: 0.5,    # \u8aad\u307f\u53d6\u308a\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8: 0.5\u79d2\n  write_timeout: 0.5,   # \u66f8\u304d\u8fbc\u307f\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8: 0.5\u79d2\n  reconnect_attempts: 1 # \u518d\u63a5\u7d9a\u8a66\u884c\u56de\u6570\n}<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30b3\u30cd\u30af\u30b7\u30e7\u30f3\u30d7\u30fc\u30eb\u306e\u9069\u5207\u306a\u8a2d\u5b9a<\/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=\"\"># config\/initializers\/sidekiq.rb\nSidekiq.configure_server do |config|\n  config.redis = ConnectionPool.new(size: 25) do\n    Redis.new(url: ENV['REDIS_URL'])\n  end\nend\n\nSidekiq.configure_client do |config|\n  config.redis = ConnectionPool.new(size: 5) do\n    Redis.new(url: ENV['REDIS_URL'])\n  end\nend<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>\u540d\u524d\u7a7a\u9593\u306b\u3088\u308b\u5206\u96e2<\/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=\"\"># \u7570\u306a\u308b\u74b0\u5883\u3084\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u9593\u3067\u306e\u30ad\u30fc\u885d\u7a81\u3092\u9632\u3050\nRedis::Namespace.new(\n  \"myapp:#{Rails.env}\",\n  redis: Redis.current\n)<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-6\">\u958b\u767a\u74b0\u5883\u3068\u672c\u756a\u74b0\u5883\u3067\u306e\u6ce8\u610f\u70b9<\/h3>\n\n\n\n<p>\u958b\u767a\u74b0\u5883\u3068\u672c\u756a\u74b0\u5883\u305d\u308c\u305e\u308c\u3067\u8003\u616e\u3059\u3079\u304d\u91cd\u8981\u306a\u30dd\u30a4\u30f3\u30c8\u304c\u3042\u308a\u307e\u3059\uff1a<\/p>\n\n\n\n<p><strong>\u958b\u767a\u74b0\u5883\u3067\u306e\u8a2d\u5b9a<\/strong><\/p>\n\n\n<div id=\"id-8aa24ac1-8230-442e-a98d-327df5e529f5\">\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u9805\u76ee<\/th><th>\u63a8\u5968\u8a2d\u5b9a<\/th><th>\u7406\u7531<\/th><\/tr><\/thead><tbody><tr><td>maxmemory<\/td><td>100MB<\/td><td>\u958b\u767a\u30de\u30b7\u30f3\u306e\u30ea\u30bd\u30fc\u30b9\u7bc0\u7d04<\/td><\/tr><tr><td>maxmemory-policy<\/td><td>allkeys-lru<\/td><td>\u958b\u767a\u6642\u306e\u6319\u52d5\u78ba\u8a8d\u7528<\/td><\/tr><tr><td>\u30c7\u30fc\u30bf\u6c38\u7d9a\u5316<\/td><td>\u7121\u52b9<\/td><td>\u958b\u767a\u74b0\u5883\u3067\u306f\u4e0d\u8981<\/td><\/tr><\/tbody><\/table><\/figure>\n<\/div>\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\/environments\/development.rb\nconfig.cache_store = :redis_cache_store, {\n  url: 'redis:\/\/localhost:6379\/0',\n  error_handler: -&gt; (method:, returning:, exception:) {\n    Rails.logger.error(\n      \"Redis error: #{exception.class} - #{exception.message}\"\n    )\n  }\n}<\/pre>\n\n\n\n<p><strong>\u672c\u756a\u74b0\u5883\u3067\u306e\u8a2d\u5b9a<\/strong><\/p>\n\n\n\n<p>\u4ee5\u4e0b\u306e\u8a2d\u5b9a\u3092\u672c\u756a\u74b0\u5883\u3067\u5fc5\u305a\u884c\u3063\u3066\u304f\u3060\u3055\u3044\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30bb\u30ad\u30e5\u30ea\u30c6\u30a3\u8a2d\u5b9a<\/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=\"\"># redis.conf\nbind 127.0.0.1\nprotected-mode yes\nrequirepass \"YOUR_STRONG_PASSWORD\"<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u6c38\u7d9a\u5316\u306e\u8a2d\u5b9a<\/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=\"\"># redis.conf\nsave 900 1      # 15\u5206\u30671\u56de\u4ee5\u4e0a\u306e\u5909\u66f4\nsave 300 10     # 5\u5206\u306710\u56de\u4ee5\u4e0a\u306e\u5909\u66f4\nsave 60 10000   # 1\u5206\u306710000\u56de\u4ee5\u4e0a\u306e\u5909\u66f4<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>\u30e1\u30e2\u30ea\u7ba1\u7406<\/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=\"\"># redis.conf\nmaxmemory 2gb\nmaxmemory-policy volatile-lru<\/pre>\n\n\n\n<ol start=\"4\" class=\"wp-block-list\">\n<li><strong>\u76e3\u8996\u8a2d\u5b9a<\/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=\"\"># config\/initializers\/redis_monitor.rb\nRedis.current.on(:failed_command) do |command, error|\n  Honeybadger.notify(\n    error_class: \"RedisCommandError\",\n    error_message: error.message,\n    context: { command: command }\n  )\nend<\/pre>\n\n\n\n<p>\u5c0e\u5165\u5f8c\u306e\u30c1\u30a7\u30c3\u30af\u30ea\u30b9\u30c8\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>[ ] Redis\u30b5\u30fc\u30d0\u30fc\u306e\u8d77\u52d5\u78ba\u8a8d<\/li>\n\n\n\n<li>[ ] \u63a5\u7d9a\u30c6\u30b9\u30c8\u306e\u5b9f\u884c<\/li>\n\n\n\n<li>[ ] \u30ad\u30e3\u30c3\u30b7\u30e5\u52d5\u4f5c\u306e\u78ba\u8a8d<\/li>\n\n\n\n<li>[ ] \u30a8\u30e9\u30fc\u30cf\u30f3\u30c9\u30ea\u30f3\u30b0\u306e\u78ba\u8a8d<\/li>\n\n\n\n<li>[ ] \u76e3\u8996\u8a2d\u5b9a\u306e\u78ba\u8a8d<\/li>\n\n\n\n<li>[ ] \u30d0\u30c3\u30af\u30a2\u30c3\u30d7\u8a2d\u5b9a\u306e\u78ba\u8a8d<\/li>\n<\/ul>\n\n\n\n<p>\u3053\u308c\u3089\u306e\u8a2d\u5b9a\u3092\u9069\u5207\u306b\u884c\u3046\u3053\u3068\u3067\u3001\u5b89\u5b9a\u3057\u305fRedis\u74b0\u5883\u3092\u69cb\u7bc9\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002\u6b21\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u3067\u306f\u3001\u5b9f\u969b\u306e\u30ad\u30e3\u30c3\u30b7\u30e5\u7ba1\u7406\u306e\u5b9f\u88c5\u65b9\u6cd5\u306b\u3064\u3044\u3066\u8a73\u3057\u304f\u89e3\u8aac\u3057\u3066\u3044\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-7\">Rails\u3067\u306e\u30ad\u30e3\u30c3\u30b7\u30e5\u7ba1\u7406\u306bRedis\u3092\u6d3b\u7528\u3059\u308b<\/h2>\n\n\n\n<p>Redis\u3092\u4f7f\u7528\u3057\u305f\u30ad\u30e3\u30c3\u30b7\u30e5\u7ba1\u7406\u306f\u3001Rails\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u3092\u5927\u5e45\u306b\u5411\u4e0a\u3055\u305b\u308b\u91cd\u8981\u306a\u8981\u7d20\u3067\u3059\u3002\u3053\u3053\u3067\u306f\u3001\u52b9\u679c\u7684\u306a\u30ad\u30e3\u30c3\u30b7\u30e5\u5b9f\u88c5\u306e\u65b9\u6cd5\u3068\u3001\u5b9f\u8df5\u7684\u306a\u6700\u9069\u5316\u30c6\u30af\u30cb\u30c3\u30af\u3092\u89e3\u8aac\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-8\">Redis Store\u3092\u4f7f\u3063\u305f\u30ad\u30e3\u30c3\u30b7\u30e5\u306e\u5b9f\u88c5\u65b9\u6cd5<\/h3>\n\n\n\n<p>Redis\u306b\u3088\u308b\u30ad\u30e3\u30c3\u30b7\u30e5\u30b9\u30c8\u30a2\u306fRails\u306e\u30ad\u30e3\u30c3\u30b7\u30e5\u6a5f\u80fd\u3068\u5b8c\u5168\u306b\u7d71\u5408\u3067\u304d\u307e\u3059\u3002\u4ee5\u4e0b\u306b\u4e3b\u8981\u306a\u5b9f\u88c5\u30d1\u30bf\u30fc\u30f3\u3092\u793a\u3057\u307e\u3059\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u57fa\u672c\u7684\u306a\u30ad\u30e3\u30c3\u30b7\u30e5\u8a2d\u5b9a<\/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=\"\"># config\/application.rb\nconfig.cache_store = :redis_cache_store, {\n  url: ENV['REDIS_URL'],\n  namespace: 'cache',\n  expires_in: 1.hour,\n  compression: true,\n  compression_threshold: 1.kilobyte\n}<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u4f4e\u30ec\u30d9\u30eb\u30ad\u30e3\u30c3\u30b7\u30e5\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 ProductService\n  def get_product_details(product_id)\n    Rails.cache.fetch(\"products\/#{product_id}\/details\", expires_in: 12.hours) do\n      # \u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304b\u3089\u306e\u91cd\u3044\u51e6\u7406\n      product = Product.find(product_id)\n      {\n        name: product.name,\n        price: product.calculated_price,\n        stock: product.current_stock\n      }\n    end\n  end\nend<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>\u30ed\u30b7\u30a2\u30f3\u30c9\u30fc\u30eb\u30ad\u30e3\u30c3\u30b7\u30e5\u30d1\u30bf\u30fc\u30f3<\/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 Product &lt; ApplicationRecord\n  def cache_key_with_version\n    \"#{super}\/#{associated_models_cache_key}\"\n  end\n\n  private\n\n  def associated_models_cache_key\n    # \u95a2\u9023\u30e2\u30c7\u30eb\u306e\u66f4\u65b0\u3092\u691c\u77e5\n    [\n      categories.maximum(:updated_at),\n      variants.maximum(:updated_at),\n      prices.maximum(:updated_at)\n    ].map(&amp;:to_i).join('\/')\n  end\nend<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-9\">\u30ad\u30e3\u30c3\u30b7\u30e5\u5236\u5fa1\u3068\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u6700\u9069\u5316\u306e\u30b3\u30c4<\/h3>\n\n\n\n<p>\u52b9\u679c\u7684\u306a\u30ad\u30e3\u30c3\u30b7\u30e5\u5236\u5fa1\u306e\u305f\u3081\u306e\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9\u3092\u7d39\u4ecb\u3057\u307e\u3059\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30d0\u30c3\u30c1\u51e6\u7406\u306b\u3088\u308b\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 bulk_fetch_products(product_ids)\n  # \u8907\u6570\u306e\u30ad\u30fc\u3092\u4e00\u5ea6\u306b\u53d6\u5f97\n  cache_keys = product_ids.map { |id| \"products\/#{id}\/details\" }\n  cached_products = Rails.cache.read_multi(*cache_keys)\n\n  # \u30ad\u30e3\u30c3\u30b7\u30e5\u30df\u30b9\u3057\u305fID\u3092\u53d6\u5f97\n  missing_ids = product_ids.reject { |id| \n    cached_products.key?(\"products\/#{id}\/details\") \n  }\n\n  # \u30df\u30b9\u3057\u305f\u3082\u306e\u3060\u3051DB\u304b\u3089\u53d6\u5f97\n  if missing_ids.any?\n    products = Product.where(id: missing_ids).map do |product|\n      [\"products\/#{product.id}\/details\", product.attributes]\n    end\n    Rails.cache.write_multi(Hash[products])\n    cached_products.merge!(Hash[products])\n  end\n\n  cached_products\nend<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u6761\u4ef6\u4ed8\u304d\u30ad\u30e3\u30c3\u30b7\u30e5\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 ProductsController &lt; ApplicationController\n  def index\n    products = Rails.cache.fetch(\n      \"products\/#{params[:category]}\/#{cache_version}\",\n      skip_nil: true,\n      race_condition_ttl: 10.seconds\n    ) do\n      return nil unless cacheable_request?\n      Product.by_category(params[:category]).to_a\n    end\n\n    @products = products || Product.by_category(params[:category])\n  end\n\n  private\n\n  def cacheable_request?\n    !params[:sort] &amp;&amp; !params[:filter]\n  end\n\n  def cache_version\n    Product.maximum(:updated_at).to_i\n  end\nend<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-10\">\u4e00\u822c\u7684\u306a\u30ad\u30e3\u30c3\u30b7\u30e5\u30d1\u30bf\u30fc\u30f3\u3068\u5b9f\u88c5\u4f8b<\/h3>\n\n\n\n<p>\u5b9f\u8df5\u7684\u306a\u30ad\u30e3\u30c3\u30b7\u30e5\u30d1\u30bf\u30fc\u30f3\u3068\u305d\u306e\u5b9f\u88c5\u65b9\u6cd5\u3092\u7d39\u4ecb\u3057\u307e\u3059\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u968e\u5c64\u5316\u30ad\u30e3\u30c3\u30b7\u30e5<\/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 CacheManager\n  def self.fetch_with_local_cache(key, options = {}, &amp;block)\n    # \u30d7\u30ed\u30bb\u30b9\u5185\u30e1\u30e2\u30ea\u30ad\u30e3\u30c3\u30b7\u30e5\n    LocalCache.fetch(key) do\n      # Redis\u30ad\u30e3\u30c3\u30b7\u30e5\n      Rails.cache.fetch(key, options, &amp;block)\n    end\n  end\nend\n\n# \u4f7f\u7528\u4f8b\nCacheManager.fetch_with_local_cache(\"expensive_calculation\", expires_in: 1.hour) do\n  perform_expensive_calculation\nend<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30d5\u30e9\u30b0\u30e1\u30f3\u30c8\u30ad\u30e3\u30c3\u30b7\u30e5\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=\"\"># app\/views\/products\/show.html.erb\n&lt;% cache_if(cacheable_product?, [current_user, @product]) do %&gt;\n  &lt;div class=\"product-details\"&gt;\n    &lt;% cache [@product, 'basic_info'] do %&gt;\n      &lt;%= render 'basic_info', product: @product %&gt;\n    &lt;% end %&gt;\n\n    &lt;% cache [@product, 'pricing'] do %&gt;\n      &lt;%= render 'pricing', product: @product %&gt;\n    &lt;% end %&gt;\n\n    &lt;% cache [@product, 'reviews'] do %&gt;\n      &lt;%= render 'reviews', product: @product %&gt;\n    &lt;% end %&gt;\n  &lt;\/div&gt;\n&lt;% end %&gt;<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>API\u30ec\u30b9\u30dd\u30f3\u30b9\u306e\u30ad\u30e3\u30c3\u30b7\u30e5<\/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 Api::V1::ProductsController &lt; ApiController\n  def index\n    response = Rails.cache.fetch(\n      \"api\/v1\/products\/#{cache_key}\",\n      expires_in: 15.minutes,\n      race_condition_ttl: 15.seconds\n    ) do\n      {\n        products: ProductSerializer.new(fetch_products).as_json,\n        meta: {\n          total_count: Product.count,\n          updated_at: Time.current.iso8601\n        }\n      }\n    end\n\n    render json: response\n  end\n\n  private\n\n  def cache_key\n    [\n      params[:page],\n      params[:per_page],\n      Product.maximum(:updated_at).to_i\n    ].join('\/')\n  end\nend<\/pre>\n\n\n\n<p>\u30ad\u30e3\u30c3\u30b7\u30e5\u5b9f\u88c5\u6642\u306e\u91cd\u8981\u306a\u30dd\u30a4\u30f3\u30c8\uff1a<\/p>\n\n\n<div id=\"id-df93408f-5cc0-4f5e-829c-3131ad99c156\">\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u9805\u76ee<\/th><th>\u63a8\u5968\u8a2d\u5b9a<\/th><th>\u7406\u7531<\/th><\/tr><\/thead><tbody><tr><td>TTL<\/td><td>\u7528\u9014\u306b\u5fdc\u3058\u3066\u9069\u5207\u306b\u8a2d\u5b9a<\/td><td>\u30e1\u30e2\u30ea\u52b9\u7387\u306e\u6700\u9069\u5316<\/td><\/tr><tr><td>\u30ad\u30fc\u8a2d\u8a08<\/td><td>\u968e\u5c64\u69cb\u9020\u3092\u610f\u8b58<\/td><td>\u7ba1\u7406\u306e\u3057\u3084\u3059\u3055<\/td><\/tr><tr><td>\u5727\u7e2e<\/td><td>1KB\u4ee5\u4e0a\u3067\u6709\u52b9\u5316<\/td><td>\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u8ee2\u9001\u306e\u6700\u9069\u5316<\/td><\/tr><tr><td>\u30d0\u30fc\u30b8\u30e7\u30cb\u30f3\u30b0<\/td><td>\u30e2\u30c7\u30eb\u306e\u66f4\u65b0\u6642\u523b\u3092\u5229\u7528<\/td><td>\u6574\u5408\u6027\u306e\u78ba\u4fdd<\/td><\/tr><\/tbody><\/table><\/figure>\n<\/div>\n\n\n<p>\u3053\u308c\u3089\u306e\u30c6\u30af\u30cb\u30c3\u30af\u3092\u9069\u5207\u306b\u7d44\u307f\u5408\u308f\u305b\u308b\u3053\u3068\u3067\u3001\u9ad8\u901f\u3067\u5b89\u5b9a\u3057\u305f\u30ad\u30e3\u30c3\u30b7\u30e5\u30b7\u30b9\u30c6\u30e0\u3092\u69cb\u7bc9\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002\u6b21\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u3067\u306f\u3001\u30bb\u30c3\u30b7\u30e7\u30f3\u7ba1\u7406\u3068\u30b8\u30e7\u30d6\u30ad\u30e5\u30fc\u306e\u5b9f\u88c5\u306b\u3064\u3044\u3066\u89e3\u8aac\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-11\">\u30bb\u30c3\u30b7\u30e7\u30f3\u7ba1\u7406\u3068\u30b8\u30e7\u30d6\u30ad\u30e5\u30fc\u306e\u5b9f\u88c5<\/h2>\n\n\n\n<p>Redis\u3092\u6d3b\u7528\u3057\u305f\u30bb\u30c3\u30b7\u30e7\u30f3\u7ba1\u7406\u3068\u975e\u540c\u671f\u30b8\u30e7\u30d6\u30ad\u30e5\u30fc\u306e\u5b9f\u88c5\u306f\u3001\u30b9\u30b1\u30fc\u30e9\u30d6\u30eb\u306aRails\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u69cb\u7bc9\u3059\u308b\u4e0a\u3067\u91cd\u8981\u306a\u8981\u7d20\u3067\u3059\u3002\u3053\u3053\u3067\u306f\u3001\u30bb\u30c3\u30b7\u30e7\u30f3\u30b9\u30c8\u30a2\u306e\u8a2d\u5b9a\u304b\u3089\u3001Sidekiq\u3092\u4f7f\u7528\u3057\u305f\u52b9\u7387\u7684\u306a\u975e\u540c\u671f\u51e6\u7406\u306e\u5b9f\u88c5\u307e\u3067\u3001\u8a73\u3057\u304f\u89e3\u8aac\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-12\">Redis\u3067\u306e\u30bb\u30c3\u30b7\u30e7\u30f3\u30b9\u30c8\u30a2C\u306e\u8a2d\u5b9a\u3068\u904b\u7528<\/h3>\n\n\n\n<p>\u30bb\u30c3\u30b7\u30e7\u30f3\u30b9\u30c8\u30a2\u3092Redis\u306b\u79fb\u884c\u3059\u308b\u3053\u3068\u3067\u3001\u8907\u6570\u30b5\u30fc\u30d0\u30fc\u9593\u3067\u306e\u30bb\u30c3\u30b7\u30e7\u30f3\u5171\u6709\u3084\u9ad8\u901f\u306a\u30bb\u30c3\u30b7\u30e7\u30f3\u7ba1\u7406\u304c\u53ef\u80fd\u306b\u306a\u308a\u307e\u3059\u3002<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u57fa\u672c\u8a2d\u5b9a<\/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=\"\"># config\/initializers\/session_store.rb\nRails.application.config.session_store :redis_store,\n  servers: [\n    {\n      host: ENV.fetch('REDIS_HOST', 'localhost'),\n      port: ENV.fetch('REDIS_PORT', 6379),\n      db: 0,\n      namespace: \"session\"\n    }\n  ],\n  expire_after: 90.minutes,\n  key: '_myapp_session',\n  secure: Rails.env.production?<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30bb\u30c3\u30b7\u30e7\u30f3\u306e\u6697\u53f7\u5316\u8a2d\u5b9a<\/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=\"\"># config\/initializers\/redis_session.rb\nrequire 'action_dispatch\/middleware\/session\/redis_store'\nrequire 'redis'\nrequire 'redis-namespace'\n\nredis_connection = Redis::Namespace.new(\n  \"myapp:session\",\n  redis: Redis.new(\n    url: ENV['REDIS_URL'],\n    ssl_params: { verify_mode: OpenSSL::SSL::VERIFY_NONE }\n  )\n)\n\nRails.application.config.session_store :redis_store, {\n  redis: redis_connection,\n  expire_after: 90.minutes,\n  key: '_myapp_session',\n  secure: Rails.env.production?,\n  threadsafe: true,\n  signed: true,\n  encrypt: true\n}<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>\u30bb\u30c3\u30b7\u30e7\u30f3\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=\"\">class ApplicationController &lt; ActionController::Base\n  before_action :update_session_expiry\n\n  private\n\n  def update_session_expiry\n    return unless current_user\n    # \u30e6\u30fc\u30b6\u30fc\u304c\u30a2\u30af\u30c6\u30a3\u30d6\u306a\u5834\u5408\u306e\u307f\u30bb\u30c3\u30b7\u30e7\u30f3\u3092\u5ef6\u9577\n    session[:expires_at] = 90.minutes.from_now\n  end\n\n  def session_expired?\n    session[:expires_at].present? &amp;&amp; session[:expires_at] &lt; Time.current\n  end\nend<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-13\">Sidekiq\u3068\u7d44\u307f\u5408\u308f\u305b\u305f\u975e\u540c\u671f\u51e6\u7406\u306e\u5b9f\u88c5<\/h3>\n\n\n\n<p>Sidekiq\u3092\u4f7f\u7528\u3057\u305f\u52b9\u7387\u7684\u306a\u975e\u540c\u671f\u51e6\u7406\u306e\u5b9f\u88c5\u65b9\u6cd5\u3092\u89e3\u8aac\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Sidekiq\u306e\u57fa\u672c\u8a2d\u5b9a<\/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=\"\"># config\/initializers\/sidekiq.rb\nrequire 'sidekiq'\nrequire 'sidekiq\/scheduler'\n\nSidekiq.configure_server do |config|\n  config.redis = {\n    url: ENV['REDIS_URL'],\n    namespace: 'sidekiq',\n    size: 25,\n    network_timeout: 5,\n    pool_timeout: 5\n  }\n\n  config.periodic_scheduler_options = {\n    enabled: true,\n    dynamic: true\n  }\nend\n\nSidekiq.configure_client do |config|\n  config.redis = {\n    url: ENV['REDIS_URL'],\n    namespace: 'sidekiq',\n    size: 5,\n    network_timeout: 5,\n    pool_timeout: 5\n  }\nend<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30b8\u30e7\u30d6\u30af\u30e9\u30b9\u306e\u5b9f\u88c5\u4f8b<\/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 DataProcessingJob\n  include Sidekiq::Job\n\n  sidekiq_options queue: :high_priority,\n                  retry: 3,\n                  backtrace: true,\n                  dead: false,\n                  unique: :until_executed\n\n  def perform(data_id)\n    data = Data.find(data_id)\n\n    Sidekiq.logger.info \"Processing data #{data_id}\"\n\n    ActiveRecord::Base.transaction do\n      result = process_data(data)\n      update_statistics(result)\n    end\n  rescue =&gt; e\n    Sidekiq.logger.error \"Error processing data #{data_id}: #{e.message}\"\n    raise\n  end\n\n  private\n\n  def process_data(data)\n    # \u30c7\u30fc\u30bf\u51e6\u7406\u30ed\u30b8\u30c3\u30af\n  end\n\n  def update_statistics(result)\n    # \u7d71\u8a08\u66f4\u65b0\u30ed\u30b8\u30c3\u30af\n  end\nend<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>\u30d0\u30c3\u30c1\u51e6\u7406\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 BatchProcessingJob\n  include Sidekiq::Job\n\n  sidekiq_options queue: :batch,\n                  retry: 5,\n                  lock: :while_executing\n\n  def perform(batch_id)\n    batch = Batch.find(batch_id)\n\n    # \u30d7\u30ed\u30b0\u30ec\u30b9\u5831\u544a\u7528\u306eRedis\u30ad\u30fc\n    progress_key = \"batch:#{batch_id}:progress\"\n\n    batch.items.each_with_index do |item, index|\n      process_item(item)\n\n      # \u9032\u6357\u72b6\u6cc1\u3092Redis\u306b\u4fdd\u5b58\n      Redis.current.set(\n        progress_key,\n        ((index + 1).to_f \/ batch.items.count * 100).round(2)\n      )\n    end\n  ensure\n    # \u30af\u30ea\u30fc\u30f3\u30a2\u30c3\u30d7\n    Redis.current.del(progress_key)\n  end\nend<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-14\">\u30b8\u30e7\u30d6\u30ad\u30e5\u30fc\u306e\u30e2\u30cb\u30bf\u30ea\u30f3\u30b0\u3068\u7ba1\u7406\u65b9\u6cd5<\/h3>\n\n\n\n<p>\u52b9\u679c\u7684\u306a\u30b8\u30e7\u30d6\u30ad\u30e5\u30fc\u7ba1\u7406\u306e\u305f\u3081\u306e\u30e2\u30cb\u30bf\u30ea\u30f3\u30b0\u3068\u904b\u7528\u65b9\u6cd5\u3092\u7d39\u4ecb\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30ab\u30b9\u30bf\u30e0\u30df\u30c9\u30eb\u30a6\u30a7\u30a2\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 JobMonitoringMiddleware\n  def call(worker, job, queue)\n    start_time = Time.current\n    job_key = \"job:#{job['jid']}\"\n\n    # \u30b8\u30e7\u30d6\u958b\u59cb\u3092\u8a18\u9332\n    Redis.current.hmset(\n      job_key,\n      'class', worker.class.name,\n      'queue', queue,\n      'start_time', start_time.to_i,\n      'status', 'running'\n    )\n\n    begin\n      yield\n      # \u6210\u529f\u6642\u306e\u51e6\u7406\n      record_success(job_key, start_time)\n    rescue =&gt; error\n      # \u30a8\u30e9\u30fc\u6642\u306e\u51e6\u7406\n      record_failure(job_key, start_time, error)\n      raise\n    ensure\n      # 24\u6642\u9593\u5f8c\u306b\u76e3\u8996\u30c7\u30fc\u30bf\u3092\u524a\u9664\n      Redis.current.expire(job_key, 24.hours.to_i)\n    end\n  end\n\n  private\n\n  def record_success(job_key, start_time)\n    Redis.current.hmset(\n      job_key,\n      'status', 'completed',\n      'duration', Time.current - start_time,\n      'completed_at', Time.current.to_i\n    )\n  end\n\n  def record_failure(job_key, start_time, error)\n    Redis.current.hmset(\n      job_key,\n      'status', 'failed',\n      'error', error.message,\n      'duration', Time.current - start_time,\n      'failed_at', Time.current.to_i\n    )\n  end\nend<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30e2\u30cb\u30bf\u30ea\u30f3\u30b0\u30c0\u30c3\u30b7\u30e5\u30dc\u30fc\u30c9\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 JobMonitor\n  class &lt;&lt; self\n    def job_statistics\n      {\n        queued: Sidekiq::Queue.all.sum(&amp;:size),\n        processing: Sidekiq::Workers.new.size,\n        failed: Sidekiq::DeadSet.new.size,\n        total_processed: Sidekiq::Stats.new.processed,\n        total_failed: Sidekiq::Stats.new.failed\n      }\n    end\n\n    def queue_details\n      Sidekiq::Queue.all.map do |queue|\n        {\n          name: queue.name,\n          size: queue.size,\n          latency: queue.latency,\n          average_processing_time: calculate_average_time(queue.name)\n        }\n      end\n    end\n\n    private\n\n    def calculate_average_time(queue_name)\n      # \u76f4\u8fd1100\u4ef6\u306e\u30b8\u30e7\u30d6\u306e\u5e73\u5747\u51e6\u7406\u6642\u9593\u3092\u8a08\u7b97\n      pattern = \"job:*\"\n      total_time = 0\n      count = 0\n\n      Redis.current.scan_each(match: pattern) do |key|\n        job_data = Redis.current.hgetall(key)\n        if job_data['queue'] == queue_name &amp;&amp; job_data['duration']\n          total_time += job_data['duration'].to_f\n          count += 1\n        end\n        break if count &gt;= 100\n      end\n\n      count &gt; 0 ? (total_time \/ count).round(2) : 0\n    end\n  end\nend<\/pre>\n\n\n\n<p>\u904b\u7528\u4e0a\u306e\u91cd\u8981\u306a\u30dd\u30a4\u30f3\u30c8\uff1a<\/p>\n\n\n<div id=\"id-80ae74e8-1001-407b-b00c-6302e0fb985c\">\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u76e3\u8996\u9805\u76ee<\/th><th>\u8b66\u544a\u95be\u5024<\/th><th>\u30a2\u30e9\u30fc\u30c8\u95be\u5024<\/th><th>\u5bfe\u5fdc\u7b56<\/th><\/tr><\/thead><tbody><tr><td>\u30ad\u30e5\u30fc\u9577<\/td><td>1000\u4ef6\u4ee5\u4e0a<\/td><td>5000\u4ef6\u4ee5\u4e0a<\/td><td>\u30ef\u30fc\u30ab\u30fc\u6570\u5897\u52a0<\/td><\/tr><tr><td>\u51e6\u7406\u9045\u5ef6<\/td><td>5\u5206\u4ee5\u4e0a<\/td><td>15\u5206\u4ee5\u4e0a<\/td><td>\u30ad\u30e3\u30d1\u30b7\u30c6\u30a3\u898b\u76f4\u3057<\/td><\/tr><tr><td>\u30a8\u30e9\u30fc\u7387<\/td><td>5%\u4ee5\u4e0a<\/td><td>10%\u4ee5\u4e0a<\/td><td>\u30ed\u30b0\u89e3\u6790\u3068\u5bfe\u5fdc<\/td><\/tr><tr><td>\u30e1\u30e2\u30ea\u4f7f\u7528\u91cf<\/td><td>80%\u4ee5\u4e0a<\/td><td>90%\u4ee5\u4e0a<\/td><td>\u30b9\u30b1\u30fc\u30eb\u30a2\u30c3\u30d7\u691c\u8a0e<\/td><\/tr><\/tbody><\/table><\/figure>\n<\/div>\n\n\n<p>\u3053\u308c\u3089\u306e\u5b9f\u88c5\u3068\u76e3\u8996\u4f53\u5236\u306b\u3088\u308a\u3001\u5b89\u5b9a\u3057\u305f\u975e\u540c\u671f\u51e6\u7406\u30b7\u30b9\u30c6\u30e0\u3092\u69cb\u7bc9\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002\u6b21\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u3067\u306f\u3001\u5b9f\u8df5\u7684\u306aRedis\u6d3b\u7528\u30d1\u30bf\u30fc\u30f3\u306b\u3064\u3044\u3066\u89e3\u8aac\u3057\u3066\u3044\u304d\u307e\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-15\">\u5b9f\u8df5\u7684\u306aRedis\u6d3b\u7528\u30d1\u30bf\u30fc\u30f3\u96c6<\/h2>\n\n\n\n<p>Redis\u306e\u9ad8\u901f\u306a\u51e6\u7406\u80fd\u529b\u3068\u67d4\u8edf\u306a\u30c7\u30fc\u30bf\u69cb\u9020\u3092\u6d3b\u7528\u3059\u308b\u3053\u3068\u3067\u3001\u69d8\u3005\u306a\u6a5f\u80fd\u3092\u52b9\u7387\u7684\u306b\u5b9f\u88c5\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002\u3053\u3053\u3067\u306f\u3001\u5b9f\u52d9\u3067\u3088\u304f\u4f7f\u7528\u3055\u308c\u308b\u5b9f\u8df5\u7684\u306a\u30d1\u30bf\u30fc\u30f3\u3068\u305d\u306e\u5177\u4f53\u7684\u306a\u5b9f\u88c5\u65b9\u6cd5\u3092\u89e3\u8aac\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-16\">\u30ea\u30a2\u30eb\u30bf\u30a4\u30e0\u901a\u77e5\u30b7\u30b9\u30c6\u30e0\u306e\u69cb\u7bc9\u65b9\u6cd5<\/h3>\n\n\n\n<p>WebSocket\u3068\u7d44\u307f\u5408\u308f\u305b\u305f\u30ea\u30a2\u30eb\u30bf\u30a4\u30e0\u901a\u77e5\u30b7\u30b9\u30c6\u30e0\u306e\u5b9f\u88c5\u4f8b\u3092\u7d39\u4ecb\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Pub\/Sub\u3092\u4f7f\u7528\u3057\u305f\u57fa\u672c\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=\"\"># app\/channels\/notification_channel.rb\nclass NotificationChannel &lt; ApplicationCable::Channel\n  def subscribed\n    stream_from \"notifications_#{current_user.id}\"\n  end\n\n  def unsubscribed\n    stop_all_streams\n  end\nend\n\n# app\/services\/notification_service.rb\nclass NotificationService\n  def self.notify(user_id, message)\n    notification = {\n      id: SecureRandom.uuid,\n      message: message,\n      timestamp: Time.current.to_i\n    }\n\n    Redis.current.multi do |multi|\n      # \u901a\u77e5\u3092Redis\u306b\u4fdd\u5b58\n      multi.lpush(\n        \"user:#{user_id}:notifications\",\n        notification.to_json\n      )\n      # \u6700\u65b0\u306e100\u4ef6\u306e\u307f\u4fdd\u6301\n      multi.ltrim(\"user:#{user_id}:notifications\", 0, 99)\n      # \u672a\u8aad\u30ab\u30a6\u30f3\u30c8\u3092\u5897\u52a0\n      multi.incr(\"user:#{user_id}:unread_count\")\n    end\n\n    # WebSocket\u3067\u901a\u77e5\u3092\u9001\u4fe1\n    ActionCable.server.broadcast(\n      \"notifications_#{user_id}\",\n      notification\n    )\n  end\nend<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u901a\u77e5\u306e\u65e2\u8aad\u7ba1\u7406<\/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 NotificationManager\n  def mark_as_read(user_id, notification_ids)\n    Redis.current.multi do |multi|\n      notification_ids.each do |notification_id|\n        # \u65e2\u8aad\u72b6\u614b\u3092\u8a18\u9332\n        multi.sadd(\n          \"user:#{user_id}:read_notifications\",\n          notification_id\n        )\n      end\n      # \u672a\u8aad\u30ab\u30a6\u30f3\u30c8\u3092\u66f4\u65b0\n      multi.set(\n        \"user:#{user_id}:unread_count\",\n        get_unread_count(user_id)\n      )\n    end\n  end\n\n  private\n\n  def get_unread_count(user_id)\n    total = Redis.current.llen(\"user:#{user_id}:notifications\")\n    read = Redis.current.scard(\"user:#{user_id}:read_notifications\")\n    total - read\n  end\nend<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-17\">\u30e9\u30f3\u30ad\u30f3\u30b0\u6a5f\u80fd\u306e\u52b9\u7387\u7684\u306a\u5b9f\u88c5\u30c6\u30af\u30cb\u30c3\u30af<\/h3>\n\n\n\n<p>Sorted Sets\u3092\u4f7f\u7528\u3057\u305f\u9ad8\u901f\u306a\u30e9\u30f3\u30ad\u30f3\u30b0\u6a5f\u80fd\u306e\u5b9f\u88c5\u65b9\u6cd5\u3092\u89e3\u8aac\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30ea\u30a2\u30eb\u30bf\u30a4\u30e0\u30e9\u30f3\u30ad\u30f3\u30b0\u30b7\u30b9\u30c6\u30e0<\/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 RankingSystem\n  RANKING_KEY = \"game:rankings\"\n\n  def update_score(user_id, score)\n    Redis.current.zadd(RANKING_KEY, score, user_id)\n  end\n\n  def get_rank(user_id)\n    # 0\u304b\u3089\u59cb\u307e\u308b\u30a4\u30f3\u30c7\u30c3\u30af\u30b9\u306a\u306e\u3067+1\u3059\u308b\n    Redis.current.zrevrank(RANKING_KEY, user_id).to_i + 1\n  end\n\n  def get_top_players(limit = 10)\n    user_ids_with_scores = Redis.current.zrevrange(\n      RANKING_KEY,\n      0,\n      limit - 1,\n      with_scores: true\n    )\n\n    # \u30e6\u30fc\u30b6\u30fc\u60c5\u5831\u3092\u53d6\u5f97\n    user_ids = user_ids_with_scores.map(&amp;:first)\n    users = User.where(id: user_ids).index_by(&amp;:id)\n\n    user_ids_with_scores.map.with_index(1) do |(user_id, score), rank|\n      user = users[user_id.to_i]\n      {\n        rank: rank,\n        user: user,\n        score: score.to_i\n      }\n    end\n  end\n\n  def get_nearby_ranks(user_id, range = 2)\n    rank = get_rank(user_id)\n    start_rank = [rank - range, 1].max\n    end_rank = rank + range\n\n    Redis.current.zrevrange(\n      RANKING_KEY,\n      start_rank - 1,\n      end_rank - 1,\n      with_scores: true\n    )\n  end\nend<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u671f\u9593\u5225\u30e9\u30f3\u30ad\u30f3\u30b0\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 TimeBasedRanking\n  def initialize(period)\n    @period = period\n    @current_key = ranking_key_for(Time.current)\n  end\n\n  def update_score(user_id, score)\n    Redis.current.multi do |multi|\n      # \u73fe\u5728\u306e\u671f\u9593\u306e\u30e9\u30f3\u30ad\u30f3\u30b0\u3092\u66f4\u65b0\n      multi.zadd(@current_key, score, user_id)\n      # \u6c38\u7d9a\u30e9\u30f3\u30ad\u30f3\u30b0\u3082\u66f4\u65b0\n      multi.zadd(\"rankings:all_time\", score, user_id)\n    end\n  end\n\n  def get_rankings(period = :current, limit = 10)\n    key = case period\n          when :current then @current_key\n          when :all_time then \"rankings:all_time\"\n          when :previous then ranking_key_for(Time.current - @period)\n          end\n\n    Redis.current.zrevrange(key, 0, limit - 1, with_scores: true)\n  end\n\n  private\n\n  def ranking_key_for(time)\n    time_str = time.strftime(\"%Y%m%d\")\n    \"rankings:#{time_str}\"\n  end\nend<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-18\">\u30ec\u30fc\u30c8\u5236\u9650\u306e\u5b9f\u88c5\u3068API\u306e\u4fdd\u8b77\u65b9\u6cd5<\/h3>\n\n\n\n<p>\u52b9\u7387\u7684\u306a\u30ec\u30fc\u30c8\u5236\u9650\u306e\u5b9f\u88c5\u65b9\u6cd5\u3092\u7d39\u4ecb\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30b9\u30e9\u30a4\u30c7\u30a3\u30f3\u30b0\u30a6\u30a3\u30f3\u30c9\u30a6\u306b\u3088\u308b\u30ec\u30fc\u30c8\u5236\u9650<\/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 RateLimiter\n  def initialize(action:, limit:, period:)\n    @action = action\n    @limit = limit\n    @period = period\n  end\n\n  def allowed?(user_id)\n    key = \"rate_limit:#{@action}:#{user_id}\"\n    current_time = Time.current.to_i\n\n    Redis.current.multi do |multi|\n      # \u671f\u9650\u5207\u308c\u306e\u30ea\u30af\u30a8\u30b9\u30c8\u3092\u524a\u9664\n      multi.zremrangebyscore(key, 0, current_time - @period)\n      # \u73fe\u5728\u306e\u30ea\u30af\u30a8\u30b9\u30c8\u6570\u3092\u53d6\u5f97\n      multi.zcard(key)\n      # \u65b0\u3057\u3044\u30ea\u30af\u30a8\u30b9\u30c8\u3092\u8ffd\u52a0\n      multi.zadd(key, current_time, \"#{current_time}.#{SecureRandom.hex(6)}\")\n      # \u30ad\u30fc\u306e\u6709\u52b9\u671f\u9650\u3092\u8a2d\u5b9a\n      multi.expire(key, @period)\n    end.then do |_, count, _, _|\n      count &lt; @limit\n    end\n  end\nend\n\n# \u4f7f\u7528\u4f8b\nclass ApiController &lt; ApplicationController\n  before_action :check_rate_limit\n\n  private\n\n  def check_rate_limit\n    limiter = RateLimiter.new(\n      action: controller_name,\n      limit: 100,\n      period: 1.hour.to_i\n    )\n\n    unless limiter.allowed?(current_user.id)\n      render json: { error: 'Rate limit exceeded' },\n             status: :too_many_requests\n    end\n  end\nend<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>IP\u30d9\u30fc\u30b9\u306e\u30ec\u30fc\u30c8\u5236\u9650<\/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 IpRateLimiter\n  def initialize\n    @redis = Redis.current\n  end\n\n  def check_rate_limit(ip, endpoint)\n    key = \"rate_limit:#{ip}:#{endpoint}\"\n    now = Time.current.to_i\n\n    # 1\u5206\u9593\u306e\u6700\u5927\u30ea\u30af\u30a8\u30b9\u30c8\u6570\u3092\u8a2d\u5b9a\n    window_size = 60\n    max_requests = endpoint == 'login' ? 5 : 60\n\n    # \u30d1\u30a4\u30d7\u30e9\u30a4\u30f3\u51e6\u7406\u3067\u52b9\u7387\u5316\n    count = @redis.multi do |multi|\n      multi.zremrangebyscore(key, 0, now - window_size)\n      multi.zadd(key, now, \"#{now}.#{SecureRandom.hex(6)}\")\n      multi.zcard(key)\n      multi.expire(key, window_size)\n    end[2]\n\n    {\n      allowed: count &lt;= max_requests,\n      remaining: max_requests - count,\n      reset_time: now + window_size\n    }\n  end\nend<\/pre>\n\n\n\n<p>\u5b9f\u88c5\u6642\u306e\u91cd\u8981\u306a\u30dd\u30a4\u30f3\u30c8\uff1a<\/p>\n\n\n<div id=\"id-01383450-b6ee-4e2c-a8d2-60f0256a663e\">\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u30d1\u30bf\u30fc\u30f3<\/th><th>\u30e1\u30ea\u30c3\u30c8<\/th><th>\u6ce8\u610f\u70b9<\/th><th>\u63a8\u5968\u8a2d\u5b9a<\/th><\/tr><\/thead><tbody><tr><td>Pub\/Sub<\/td><td>\u30ea\u30a2\u30eb\u30bf\u30a4\u30e0\u6027<\/td><td>\u30e1\u30c3\u30bb\u30fc\u30b8\u6c38\u7d9a\u5316\u306a\u3057<\/td><td>\u5c0f\u898f\u6a21\u301c\u4e2d\u898f\u6a21<\/td><\/tr><tr><td>Sorted Sets<\/td><td>\u9ad8\u901f\u306a\u30e9\u30f3\u30ad\u30f3\u30b0<\/td><td>\u30e1\u30e2\u30ea\u4f7f\u7528\u91cf<\/td><td>\u30b9\u30b3\u30a2\u6b63\u898f\u5316<\/td><\/tr><tr><td>\u30ec\u30fc\u30c8\u5236\u9650<\/td><td>API\u4fdd\u8b77<\/td><td>\u5206\u6563\u74b0\u5883\u3067\u306e\u540c\u671f<\/td><td>\u67d4\u8edf\u306a\u5236\u9650\u5024<\/td><\/tr><\/tbody><\/table><\/figure>\n<\/div>\n\n\n<p>\u3053\u308c\u3089\u306e\u30d1\u30bf\u30fc\u30f3\u3092\u9069\u5207\u306b\u7d44\u307f\u5408\u308f\u305b\u308b\u3053\u3068\u3067\u3001\u9ad8\u6a5f\u80fd\u3067\u5b89\u5b9a\u3057\u305f\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u3092\u69cb\u7bc9\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002\u6b21\u306e\u30bb\u30af\u30b7\u30e7\u30f3\u3067\u306f\u3001Redis\u306e\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u30c1\u30e5\u30fc\u30cb\u30f3\u30b0\u3068\u30e2\u30cb\u30bf\u30ea\u30f3\u30b0\u306b\u3064\u3044\u3066\u89e3\u8aac\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"i-19\">Redis\u306e\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u30c1\u30e5\u30fc\u30cb\u30f3\u30b0\u3068\u30e2\u30cb\u30bf\u30ea\u30f3\u30b0<\/h2>\n\n\n\n<p>Redis\u3092\u672c\u756a\u74b0\u5883\u3067\u52b9\u7387\u7684\u306b\u904b\u7528\u3059\u308b\u306b\u306f\u3001\u9069\u5207\u306a\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u30c1\u30e5\u30fc\u30cb\u30f3\u30b0\u3068\u30e2\u30cb\u30bf\u30ea\u30f3\u30b0\u304c\u4e0d\u53ef\u6b20\u3067\u3059\u3002\u3053\u3053\u3067\u306f\u3001\u5b9f\u8df5\u7684\u306a\u30c1\u30e5\u30fc\u30cb\u30f3\u30b0\u624b\u6cd5\u3068\u52b9\u679c\u7684\u306a\u76e3\u8996\u65b9\u6cd5\u306b\u3064\u3044\u3066\u89e3\u8aac\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-20\">\u30e1\u30e2\u30ea\u4f7f\u7528\u91cf\u306e\u6700\u9069\u5316\u3068\u76e3\u8996\u65b9\u6cd5<\/h3>\n\n\n\n<p>Redis\u306e\u30e1\u30e2\u30ea\u4f7f\u7528\u3092\u52b9\u7387\u7684\u306b\u7ba1\u7406\u3057\u3001\u76e3\u8996\u3059\u308b\u65b9\u6cd5\u3092\u7d39\u4ecb\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30e1\u30e2\u30ea\u4f7f\u7528\u91cf\u306e\u5206\u6790<\/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 RedisMemoryAnalyzer\n  def self.analyze_memory_usage\n    info = Redis.current.info\n\n    {\n      total_memory: info['used_memory_human'],\n      peak_memory: info['used_memory_peak_human'],\n      fragmentation_ratio: info['mem_fragmentation_ratio'],\n      evicted_keys: info['evicted_keys'],\n      keyspace_hits: info['keyspace_hits'],\n      keyspace_misses: info['keyspace_misses']\n    }\n  end\n\n  def self.analyze_key_memory_usage\n    keys = Redis.current.keys('*')\n    memory_usage = {}\n\n    keys.each do |key|\n      memory_usage[key] = {\n        bytes: Redis.current.memory(:usage, key),\n        type: Redis.current.type(key)\n      }\n    end\n\n    memory_usage.sort_by { |_, stats| -stats[:bytes] }\n  end\nend<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30e1\u30e2\u30ea\u6700\u9069\u5316\u8a2d\u5b9a<\/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=\"\"># config\/initializers\/redis_memory_config.rb\nRedis.current.config(:set, 'maxmemory', '2gb')\nRedis.current.config(:set, 'maxmemory-policy', 'allkeys-lru')\n\n# \u30ad\u30fc\u6709\u52b9\u671f\u9650\u306e\u6700\u9069\u5316\nclass KeyExpiryOptimizer\n  def self.optimize_expiry\n    Redis.current.keys('*').each do |key|\n      case Redis.current.type(key)\n      when 'string'\n        optimize_string_key(key)\n      when 'hash'\n        optimize_hash_key(key)\n      when 'zset'\n        optimize_sorted_set_key(key)\n      end\n    end\n  end\n\n  private\n\n  def self.optimize_string_key(key)\n    # \u53e4\u3044\u30ad\u30e3\u30c3\u30b7\u30e5\u30c7\u30fc\u30bf\u306e\u524a\u9664\n    if key.start_with?('cache:')\n      Redis.current.expire(key, 1.week.to_i)\n    end\n  end\n\n  def self.optimize_hash_key(key)\n    # \u30bb\u30c3\u30b7\u30e7\u30f3\u30c7\u30fc\u30bf\u306e\u6709\u52b9\u671f\u9650\u8a2d\u5b9a\n    if key.start_with?('session:')\n      Redis.current.expire(key, 2.days.to_i)\n    end\n  end\n\n  def self.optimize_sorted_set_key(key)\n    # \u53e4\u3044\u30e9\u30f3\u30ad\u30f3\u30b0\u30c7\u30fc\u30bf\u306e\u524a\u9664\n    if key.start_with?('ranking:')\n      Redis.current.zremrangebyscore(\n        key,\n        '-inf',\n        (Time.current - 30.days).to_i\n      )\n    end\n  end\nend<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-21\">Redis\u306e\u30d0\u30c3\u30af\u30a2\u30c3\u30d7\u3068\u5fa9\u65e7\u6226\u7565<\/h3>\n\n\n\n<p>\u30c7\u30fc\u30bf\u306e\u5b89\u5168\u6027\u3092\u78ba\u4fdd\u3059\u308b\u305f\u3081\u306e\u30d0\u30c3\u30af\u30a2\u30c3\u30d7\u3068\u5fa9\u65e7\u65b9\u6cd5\u3092\u89e3\u8aac\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u81ea\u52d5\u30d0\u30c3\u30af\u30a2\u30c3\u30d7\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 RedisBackupManager\n  def self.create_backup\n    timestamp = Time.current.strftime('%Y%m%d_%H%M%S')\n    backup_path = Rails.root.join('backups', \"redis_#{timestamp}.rdb\")\n\n    # \u30d0\u30c3\u30af\u30a2\u30c3\u30d7\u30b3\u30de\u30f3\u30c9\u306e\u5b9f\u884c\n    system(\"redis-cli save\")\n    FileUtils.cp(\n      '\/var\/lib\/redis\/dump.rdb',\n      backup_path\n    )\n\n    # S3\u3078\u306e\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\n    s3_client = Aws::S3::Client.new\n    s3_client.put_object(\n      bucket: ENV['BACKUP_BUCKET'],\n      key: \"redis\/#{timestamp}.rdb\",\n      body: File.read(backup_path)\n    )\n  rescue =&gt; e\n    Rails.logger.error \"Backup failed: #{e.message}\"\n    Honeybadger.notify(e)\n  end\nend\n\n# config\/schedule.rb\uff08whenever gem\u4f7f\u7528\uff09\nevery 1.day, at: '4:30 am' do\n  runner \"RedisBackupManager.create_backup\"\nend<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u5fa9\u65e7\u624b\u9806\u306e\u81ea\u52d5\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 RedisRecoveryManager\n  def self.recover_from_backup(backup_date)\n    backup_key = \"redis\/#{backup_date}.rdb\"\n\n    # S3\u304b\u3089\u30d0\u30c3\u30af\u30a2\u30c3\u30d7\u3092\u53d6\u5f97\n    s3_client = Aws::S3::Client.new\n    backup_file = s3_client.get_object(\n      bucket: ENV['BACKUP_BUCKET'],\n      key: backup_key\n    )\n\n    # \u4e00\u6642\u30d5\u30a1\u30a4\u30eb\u306b\u4fdd\u5b58\n    temp_path = Rails.root.join('tmp', 'redis_recovery.rdb')\n    File.write(temp_path, backup_file.body.read)\n\n    # Redis\u3092\u505c\u6b62\u3057\u3001\u30d0\u30c3\u30af\u30a2\u30c3\u30d7\u3092\u5fa9\u5143\n    system(\"sudo service redis stop\")\n    FileUtils.cp(temp_path, '\/var\/lib\/redis\/dump.rdb')\n    system(\"sudo service redis start\")\n\n    # \u30ad\u30e3\u30c3\u30b7\u30e5\u306e\u30a6\u30a9\u30fc\u30e0\u30a2\u30c3\u30d7\n    warm_up_cache\n  end\n\n  private\n\n  def self.warm_up_cache\n    # \u4e3b\u8981\u306a\u30ad\u30e3\u30c3\u30b7\u30e5\u3092\u4e8b\u524d\u306b\u751f\u6210\n    Rails.cache.fetch(\"warm_up_cache\", force: true) do\n      Product.popular.limit(100).each(&amp;:cache_key)\n      Category.all.each(&amp;:touch)\n    end\n  end\nend<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"i-22\">\u672c\u756a\u74b0\u5883\u3067\u306e\u904b\u7528\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9<\/h3>\n\n\n\n<p>\u5b9f\u904b\u7528\u306b\u304a\u3051\u308b\u91cd\u8981\u306a\u30dd\u30a4\u30f3\u30c8\u3068\u30d9\u30b9\u30c8\u30d7\u30e9\u30af\u30c6\u30a3\u30b9\u3092\u7d39\u4ecb\u3057\u307e\u3059\u3002<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u30e2\u30cb\u30bf\u30ea\u30f3\u30b0\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 RedisMonitor\n  def self.collect_metrics\n    stats = Redis.current.info\n    latency = measure_latency\n\n    {\n      performance: {\n        commands_per_second: stats['instantaneous_ops_per_sec'],\n        input_kbps: stats['instantaneous_input_kbps'],\n        output_kbps: stats['instantaneous_output_kbps'],\n        connected_clients: stats['connected_clients'],\n        blocked_clients: stats['blocked_clients']\n      },\n      memory: {\n        used_memory: stats['used_memory'],\n        used_memory_peak: stats['used_memory_peak'],\n        mem_fragmentation_ratio: stats['mem_fragmentation_ratio']\n      },\n      latency: {\n        avg_latency_ms: latency[:avg],\n        max_latency_ms: latency[:max],\n        min_latency_ms: latency[:min]\n      }\n    }\n  end\n\n  private\n\n  def self.measure_latency\n    latencies = 10.times.map do\n      start_time = Time.now.to_f\n      Redis.current.ping\n      (Time.now.to_f - start_time) * 1000\n    end\n\n    {\n      avg: latencies.sum \/ latencies.size,\n      max: latencies.max,\n      min: latencies.min\n    }\n  end\nend<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u30a2\u30e9\u30fc\u30c8\u8a2d\u5b9a\u3068\u30a4\u30f3\u30b7\u30c7\u30f3\u30c8\u5bfe\u5fdc<\/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 RedisAlertManager\n  THRESHOLDS = {\n    memory_usage_percent: 80,\n    fragmentation_ratio: 1.5,\n    connection_count: 5000,\n    latency_ms: 100\n  }\n\n  def self.check_alerts\n    metrics = RedisMonitor.collect_metrics\n    alerts = []\n\n    check_memory_usage(metrics, alerts)\n    check_fragmentation(metrics, alerts)\n    check_connections(metrics, alerts)\n    check_latency(metrics, alerts)\n\n    send_alerts(alerts) if alerts.any?\n  end\n\n  private\n\n  def self.check_memory_usage(metrics, alerts)\n    memory_percent = (metrics[:memory][:used_memory].to_f \/ \n                     metrics[:memory][:used_memory_peak].to_f) * 100\n\n    if memory_percent &gt; THRESHOLDS[:memory_usage_percent]\n      alerts &lt;&lt; {\n        type: :memory_usage,\n        value: memory_percent,\n        threshold: THRESHOLDS[:memory_usage_percent]\n      }\n    end\n  end\n\n  # \u4ed6\u306e\u30c1\u30a7\u30c3\u30af\u30e1\u30bd\u30c3\u30c9\u3082\u540c\u69d8\u306b\u5b9f\u88c5...\n\n  def self.send_alerts(alerts)\n    alerts.each do |alert|\n      Slack.notify(\n        channel: '#redis-alerts',\n        text: \"Redis Alert: #{alert[:type]} (#{alert[:value]})\"\n      )\n    end\n  end\nend<\/pre>\n\n\n\n<p>\u904b\u7528\u6642\u306e\u91cd\u8981\u306a\u76e3\u8996\u9805\u76ee\u3068\u63a8\u5968\u5024\uff1a<\/p>\n\n\n<div id=\"id-2bceb9fc-7e6f-48a2-bf82-a051113277bc\">\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u76e3\u8996\u9805\u76ee<\/th><th>\u8b66\u544a\u95be\u5024<\/th><th>\u5371\u967a\u95be\u5024<\/th><th>\u5bfe\u5fdc\u7b56<\/th><\/tr><\/thead><tbody><tr><td>\u30e1\u30e2\u30ea\u4f7f\u7528\u7387<\/td><td>80%<\/td><td>90%<\/td><td>\u30ad\u30fc\u306e\u524a\u9664\u3001\u30e1\u30e2\u30ea\u5897\u8a2d<\/td><\/tr><tr><td>\u30ec\u30a4\u30c6\u30f3\u30b7<\/td><td>100ms<\/td><td>200ms<\/td><td>\u30b3\u30cd\u30af\u30b7\u30e7\u30f3\u6570\u524a\u6e1b\u3001\u30c1\u30e5\u30fc\u30cb\u30f3\u30b0<\/td><\/tr><tr><td>CPU\u4f7f\u7528\u7387<\/td><td>70%<\/td><td>85%<\/td><td>\u30b3\u30de\u30f3\u30c9\u306e\u6700\u9069\u5316\u3001\u30b9\u30b1\u30fc\u30eb\u30a2\u30c3\u30d7<\/td><\/tr><tr><td>\u30ad\u30fc\u6570<\/td><td>100\u4e07<\/td><td>500\u4e07<\/td><td>\u6709\u52b9\u671f\u9650\u306e\u898b\u76f4\u3057\u3001\u30d1\u30fc\u30c6\u30a3\u30b7\u30e7\u30cb\u30f3\u30b0<\/td><\/tr><\/tbody><\/table><\/figure>\n<\/div>\n\n\n<p>\u672c\u756a\u74b0\u5883\u3067\u306e\u30c1\u30e5\u30fc\u30cb\u30f3\u30b0\u30dd\u30a4\u30f3\u30c8\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u30e1\u30e2\u30ea\u7ba1\u7406<\/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=\"\"># redis.conf\nmaxmemory-policy allkeys-lru\nmaxmemory 2gb<\/pre>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>\u6c38\u7d9a\u5316\u8a2d\u5b9a<\/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=\"\"># redis.conf\nsave 900 1\nsave 300 10\nsave 60 10000\nappendonly yes\nappendfsync everysec<\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u8a2d\u5b9a<\/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=\"\"># redis.conf\ntcp-backlog 511\ntimeout 0\ntcp-keepalive 300<\/pre>\n\n\n\n<ol start=\"4\" class=\"wp-block-list\">\n<li><strong>\u30af\u30e9\u30a4\u30a2\u30f3\u30c8\u8a2d\u5b9a<\/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=\"\"># config\/initializers\/redis.rb\nRedis.current = Redis.new(\n  url: ENV['REDIS_URL'],\n  timeout: 5,\n  reconnect_attempts: 3,\n  reconnect_delay: 0.5,\n  reconnect_delay_max: 2\n)<\/pre>\n\n\n\n<p>\u3053\u308c\u3089\u306e\u8a2d\u5b9a\u3068\u76e3\u8996\u4f53\u5236\u3092\u6574\u3048\u308b\u3053\u3068\u3067\u3001\u5b89\u5b9a\u3057\u305fRedis\u904b\u7528\u304c\u53ef\u80fd\u306b\u306a\u308a\u307e\u3059\u3002\u5b9a\u671f\u7684\u306a\u30d1\u30d5\u30a9\u30fc\u30de\u30f3\u30b9\u5206\u6790\u3068\u8a2d\u5b9a\u306e\u898b\u76f4\u3057\u3092\u884c\u3046\u3053\u3068\u3067\u3001\u30b7\u30b9\u30c6\u30e0\u306e\u5065\u5168\u6027\u3092\u7dad\u6301\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":[12],"tags":[],"class_list":{"0":"post-3328","1":"post","2":"type-post","3":"status-publish","4":"format-standard","6":"category-php","7":"nothumb"},"_links":{"self":[{"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/posts\/3328","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=3328"}],"version-history":[{"count":2,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/posts\/3328\/revisions"}],"predecessor-version":[{"id":3330,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=\/wp\/v2\/posts\/3328\/revisions\/3330"}],"wp:attachment":[{"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=3328"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=3328"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dexall.co.jp\/articles\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=3328"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}