<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>NotionNext BLOG</title>
        <link>https://blog.juanshen.eu.org/</link>
        <description>这是一个由NotionNext生成的站点</description>
        <lastBuildDate>Tue, 19 Sep 2023 02:15:01 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>zh-CN</language>
        <copyright>All rights reserved 2023, 卷神</copyright>
        <item>
            <title><![CDATA[电商项目-交易]]></title>
            <link>https://blog.juanshen.eu.org/article/5db70925-067c-44ec-b93d-82b0eaf168f0</link>
            <guid>https://blog.juanshen.eu.org/article/5db70925-067c-44ec-b93d-82b0eaf168f0</guid>
            <pubDate>Thu, 21 Sep 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[电商项目 交易]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-5db70925067c44ecb93d82b0eaf168f0"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><blockquote class="notion-quote notion-block-0441bbcefa1b4e17bd4a87d0409f9983"><div>文章来源说明</div></blockquote><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-95fbac4b70ad494f9ce5259addcf480f" data-id="95fbac4b70ad494f9ce5259addcf480f"><span><div id="95fbac4b70ad494f9ce5259addcf480f" class="notion-header-anchor"></div><a class="notion-hash-link" href="#95fbac4b70ad494f9ce5259addcf480f" title="业务场景介绍"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">业务场景介绍</span></span></h2><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-404647ad6f08463e997f15e7a8b13982" data-id="404647ad6f08463e997f15e7a8b13982"><span><div id="404647ad6f08463e997f15e7a8b13982" class="notion-header-anchor"></div><a class="notion-hash-link" href="#404647ad6f08463e997f15e7a8b13982" title="正常电商流程"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">正常电商流程</span></span></h3><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-e6a31d99fa44413ea2980929cbfb4792"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F654145df-c7cb-42ca-b73f-a31009dc57e5%2F9e95a848-8ac6-4f16-90bd-b923d75dcff1%2FUntitled.png?table=block&amp;id=e6a31d99-fa44-413e-a298-0929cbfb4792" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-blank notion-block-a83af8121eb740898c64c06a6bbd3090"> </div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-f1eee300e6474e449436694222001302" data-id="f1eee300e6474e449436694222001302"><span><div id="f1eee300e6474e449436694222001302" class="notion-header-anchor"></div><a class="notion-hash-link" href="#f1eee300e6474e449436694222001302" title="秒杀系统设计"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">秒杀系统设计</span></span></h3><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-4e8aed044a5445e79c838603b34d47e0" data-id="4e8aed044a5445e79c838603b34d47e0"><span><div id="4e8aed044a5445e79c838603b34d47e0" class="notion-header-anchor"></div><a class="notion-hash-link" href="#4e8aed044a5445e79c838603b34d47e0" title="秒杀业务特性"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">秒杀业务特性</span></span></h4><div class="notion-blank notion-block-7533c7ce8d144719869cf55d1fddd93d"> </div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-2eacdc780b9d41e08d4b9b33aa8aea36"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F654145df-c7cb-42ca-b73f-a31009dc57e5%2F1a679aa1-9e1d-4c0e-9eac-6565909504b0%2FUntitled.png?table=block&amp;id=2eacdc78-0b9d-41e0-8d4b-9b33aa8aea36" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-668d722338ff44c195dc7e4eb3ecd904">秒杀业务设计</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-52a85b1e8c0148c3a2143ba6a0841a0b"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F654145df-c7cb-42ca-b73f-a31009dc57e5%2Fd5a1ba8a-c81e-4c14-af19-61fe14c330db%2FUntitled.png?table=block&amp;id=52a85b1e-8c01-48c3-a214-3ba6a0841a0b" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-blank notion-block-a66aa335ba3745099451ad04d96e7b50"> </div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-1b02682cad564227ac99f533a12b2d8b" data-id="1b02682cad564227ac99f533a12b2d8b"><span><div id="1b02682cad564227ac99f533a12b2d8b" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1b02682cad564227ac99f533a12b2d8b" title="营销商品相关"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">营销商品相关</span></span></h4><div class="notion-text notion-block-bebfa8f6dcbe4b72861e6efb08253efe"><b>商品优惠</b></div><ol start="1" class="notion-list notion-list-numbered notion-block-16c1a551a38d4a3095940b57cefd546e"><li>限时促销</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-0b79e567a52b497fa9b6a75255191ccb"><li>限时抢购</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-9ad843a540a04a31b5a909443385c510"><li>秒杀</li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-1b837465281348f782ba1979d1484f2c"><li>商品包邮</li></ol><div class="notion-text notion-block-0baf65514d7c49beb2cf39899a8fc571"><b>订单优惠</b></div><ol start="1" class="notion-list notion-list-numbered notion-block-88567177730a4e2a854abe3abecc2ea2"><li>满赠</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-381bce00c3b045749a77757c9ea53e21"><li>满减</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-aac679e43c80484eb72857d08a288852"><li>送优惠券</li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-e36b0282e4e84a389f2d64655ea1e260"><li>打折扣</li></ol><ol start="5" class="notion-list notion-list-numbered notion-block-fcc47184d103495ab2f436365c3ee6da"><li>vip折扣</li></ol><ol start="6" class="notion-list notion-list-numbered notion-block-437570a60c0947c4857648e7e3b6860e"><li>订单包邮</li></ol><div class="notion-text notion-block-863733b78f8e4777b1ec858792190e95"><b>全站级别</b></div><ol start="1" class="notion-list notion-list-numbered notion-block-5fe6ef7abd4c44ba9e59497a88f720f5"><li>优惠券</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-147dca53cbdd44ddb6f639d7b46ccaac"><li>银行促销</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-04d6b5ab4021429ea797dd032fa739f3"><li>支付宝红包</li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-b0a0cf8832294f9f9fe6c5cceba9b3cd"><li>微信砍价</li></ol><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-005cc44429024fd3afa4d6560642d88d" data-id="005cc44429024fd3afa4d6560642d88d"><span><div id="005cc44429024fd3afa4d6560642d88d" class="notion-header-anchor"></div><a class="notion-hash-link" href="#005cc44429024fd3afa4d6560642d88d" title="商品限时秒杀"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">商品限时秒杀</span></span></h4><div class="notion-text notion-block-7951b4e101234421bb88a4592df04968">是一款用于常规的营销活动，在限时促销上增加“排除参与活动”、“限制用户购买次
数”、“限购种类”、“未付款取消时间”、“活动商品限制库存”等功能，是限时促销促销的增 强版，常用于用户拉新、日常的秒杀、日常活动。促销渠道(app，pc，wap，global_app， fresh_app)等</div><div class="notion-blank notion-block-8bdc6899b30648d59dd6299a6c0a1044"> </div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-08434c574a004e308d1de71ed5295961" data-id="08434c574a004e308d1de71ed5295961"><span><div id="08434c574a004e308d1de71ed5295961" class="notion-header-anchor"></div><a class="notion-hash-link" href="#08434c574a004e308d1de71ed5295961" title="订单满额减"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">订单满额减</span></span></h4><div class="notion-text notion-block-bc2cff8c922a4894864b30b8d8d6d204">常用促销工具，有满X元减Y元、满X件减Y元，支持叠加满减，订单商品满减金额，支持限制
用户参与次数，可设置包括享受优惠的商品分类，商品品牌，商品、促销会员等级，会员标签，促 销渠道(app，pc，wap，global_app，fresh_app)，订单可享受满减的支付门槛金额等，如购买 全场商品，订单满100元优惠20元</div><div class="notion-blank notion-block-eafb91f88cd54265962df9018121b790"> </div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-eee71f36bc044bbbb78d3dea83f50417" data-id="eee71f36bc044bbbb78d3dea83f50417"><span><div id="eee71f36bc044bbbb78d3dea83f50417" class="notion-header-anchor"></div><a class="notion-hash-link" href="#eee71f36bc044bbbb78d3dea83f50417" title="银行促销"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">银行促销</span></span></h4><div class="notion-text notion-block-0a83b545903a4e3cac5e90582dc9ccc5">常用促销工具，与银行合作在一段时间内每周固定几天进行优惠，可设置用户总参与次数，每 天总活动次数，在用户进行支付时进行减免。当前只有光大银行每周二、周六有活动，参与渠道只 有pc、h5端，支持排除部分商品，通常是虚拟商品</div><div class="notion-blank notion-block-4f97edf506184622867ee189eb654732"> </div><div class="notion-blank notion-block-72efdbdb3f164f7f828560c79aafd4c5"> </div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-ec166aa8c97e4620a30141586988fbd3" data-id="ec166aa8c97e4620a30141586988fbd3"><span><div id="ec166aa8c97e4620a30141586988fbd3" class="notion-header-anchor"></div><a class="notion-hash-link" href="#ec166aa8c97e4620a30141586988fbd3" title="秒杀技术"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">秒杀技术</span></span></h3><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-0407f084d5e6445d959819b177accb0d"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F654145df-c7cb-42ca-b73f-a31009dc57e5%2Fc25aedf0-becb-40a3-96ea-d1934d41a03e%2FUntitled.png?table=block&amp;id=0407f084-d5e6-445d-9598-19b177accb0d" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-blank notion-block-85914cf767e0488ea2be40b79afd6456"> </div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-f26f6eec845f4d4a85d27b84b524cdbb" data-id="f26f6eec845f4d4a85d27b84b524cdbb"><span><div id="f26f6eec845f4d4a85d27b84b524cdbb" class="notion-header-anchor"></div><a class="notion-hash-link" href="#f26f6eec845f4d4a85d27b84b524cdbb" title="单一职责"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">单一职责</span></span></h4><div class="notion-text notion-block-b3a520fc567447528309205537d8f47f">独立部署，与其他业务分开，互不影响，方便扩容</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-3b2ad86363a642d1862ea626dc09562e" data-id="3b2ad86363a642d1862ea626dc09562e"><span><div id="3b2ad86363a642d1862ea626dc09562e" class="notion-header-anchor"></div><a class="notion-hash-link" href="#3b2ad86363a642d1862ea626dc09562e" title="防止超卖"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">防止超卖</span></span></h4><div class="notion-text notion-block-3050a8ad9f8c4f679205e4983f9c210a">10个库存，1000人买，如何保证只有10个人买到</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-098f46c3273b437dbde761307f1a2f5e" data-id="098f46c3273b437dbde761307f1a2f5e"><span><div id="098f46c3273b437dbde761307f1a2f5e" class="notion-header-anchor"></div><a class="notion-hash-link" href="#098f46c3273b437dbde761307f1a2f5e" title="限流，熔断，降级"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">限流，熔断，降级</span></span></h4><div class="notion-text notion-block-d50c9abc3c1f4e698bc70e38337a2ef2">防止程序崩溃，核心就是限制次数，总量，快速失败，降级运行</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-117173a8ea4d4ae8ba11287f6b7cf8e8" data-id="117173a8ea4d4ae8ba11287f6b7cf8e8"><span><div id="117173a8ea4d4ae8ba11287f6b7cf8e8" class="notion-header-anchor"></div><a class="notion-hash-link" href="#117173a8ea4d4ae8ba11287f6b7cf8e8" title="队列晓峰"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">队列晓峰</span></span></h4><div class="notion-text notion-block-59b683a1d32e4017b5519de3332877a4">下单请求，加入队列</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-807a8df9ab8c487d944f8ebf662bd25c" data-id="807a8df9ab8c487d944f8ebf662bd25c"><span><div id="807a8df9ab8c487d944f8ebf662bd25c" class="notion-header-anchor"></div><a class="notion-hash-link" href="#807a8df9ab8c487d944f8ebf662bd25c" title="流量错峰"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">流量错峰</span></span></h4><div class="notion-text notion-block-83462cd43ab44b278bc44f48bc3822bc">使用各种手段，将流量分担到更多的时间，比如验证码</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-9522ebede61048abae8027fc0f7b97ae" data-id="9522ebede61048abae8027fc0f7b97ae"><span><div id="9522ebede61048abae8027fc0f7b97ae" class="notion-header-anchor"></div><a class="notion-hash-link" href="#9522ebede61048abae8027fc0f7b97ae" title="预热，快速扣减"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">预热，快速扣减</span></span></h4><div class="notion-text notion-block-9c79662d2d7c46e7af07177ec9675b39">秒杀读多写少，活动和库存都可以提前预热，把数据放缓存中</div><div class="notion-blank notion-block-859bec5be1814f7999963db2ecd81feb"> </div><div class="notion-blank notion-block-856ae44c48bd4c39a52831b882b23168"> </div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-14a3d1fcadc94080b26ca3b21442cb58" data-id="14a3d1fcadc94080b26ca3b21442cb58"><span><div id="14a3d1fcadc94080b26ca3b21442cb58" class="notion-header-anchor"></div><a class="notion-hash-link" href="#14a3d1fcadc94080b26ca3b21442cb58" title="下单流程"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">下单流程</span></span></h3><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-4c45a0dab1c84d7082feb738d35ed778"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F654145df-c7cb-42ca-b73f-a31009dc57e5%2Fa0a91125-7715-4419-bf97-dde4cd01a016%2FUntitled.png?table=block&amp;id=4c45a0da-b1c8-4d70-82fe-b738d35ed778" alt="notion image" loading="lazy" decoding="async"/></div></figure><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-feffec7ac3d34f15b0b8b95837e91279" data-id="feffec7ac3d34f15b0b8b95837e91279"><span><div id="feffec7ac3d34f15b0b8b95837e91279" class="notion-header-anchor"></div><a class="notion-hash-link" href="#feffec7ac3d34f15b0b8b95837e91279" title="确认下单流程"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">确认下单流程</span></span></h4><ol start="1" class="notion-list notion-list-numbered notion-block-4f05025dd41645f19cdfeafa7913c437"><li>检查</li><ol class="notion-list notion-list-numbered notion-block-4f05025dd41645f19cdfeafa7913c437"><li>检查本地缓存售罄状态</li><li>校验是否有权限购买</li><li>redis库存是否狗</li><li>是否正在队列当中</li></ol></ol><ol start="2" class="notion-list notion-list-numbered notion-block-50d7a38aa894407bb7e21bdab74b347e"><li>调用会员服务获取会员信息</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-d6801d29b603480290833a14f4652d05"><li>产品服务获取产品信息</li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-c0ced46d17c94712aa3156b433b8d66f"><li>验证秒杀是否超时</li></ol><ol start="5" class="notion-list notion-list-numbered notion-block-7203175dc5f34dcaa41c648d4af9692b"><li>获取用户收货列表</li></ol><ol start="6" class="notion-list notion-list-numbered notion-block-61325f22b7e845a7b72d2f3780873034"><li>构建商品信息</li></ol><ol start="7" class="notion-list notion-list-numbered notion-block-a4d28375e53640198777fd8361e4255c"><li>计算金额</li></ol><ol start="8" class="notion-list notion-list-numbered notion-block-d8ba9f7daea4412aad44029322d5df1d"><li>会员积分</li></ol><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-86ac8c3312a34c01947f49f2b3e88d66" data-id="86ac8c3312a34c01947f49f2b3e88d66"><span><div id="86ac8c3312a34c01947f49f2b3e88d66" class="notion-header-anchor"></div><a class="notion-hash-link" href="#86ac8c3312a34c01947f49f2b3e88d66" title="下单提交"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">下单提交</span></span></h4><div class="notion-text notion-block-41f96e77c5eb40c782b585d737380ec3">0——同步下单 1异步下单排队 -1 秒杀失败 1 秒杀成功(返回订单号)</div><div class="notion-text notion-block-1268290482a84876a88cbb00e67cfa29">主要流程</div><ol start="1" class="notion-list notion-list-numbered notion-block-e03322585b2f4b85adddcf3debd626a0"><li>检查</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-9c031ff40876469083229dbc6a4e4fc7"><li>产品服务获取产品信息</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-02a0b5e51d924f108b56d36180ce182b"><li>验证秒杀是否超时</li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-2788f5630aeb4697990f8666da1c79ac"><li>获取会员服务会员信息</li></ol><ol start="5" class="notion-list notion-list-numbered notion-block-096cd1a973f143ff933ff5fd41e5618c"><li>会员地址服务</li></ol><ol start="6" class="notion-list notion-list-numbered notion-block-8cd823cad17a4c00a51f3c06ae106702"><li>预减库存 （异步流程需要，数据库锁不需要）</li></ol><ol start="7" class="notion-list notion-list-numbered notion-block-6e3216132dec4d0790e0fd2a79253b49"><li>生成下单商品信息</li></ol><ol start="8" class="notion-list notion-list-numbered notion-block-1ae1f22b12834be9ab8115e7ab9fee55"><li>库存处理</li></ol><div class="notion-blank notion-block-a92804cb0bec44178662bb63b39e540a"> </div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-edeaca5cf9ae463b92021b73442287b3" data-id="edeaca5cf9ae463b92021b73442287b3"><span><div id="edeaca5cf9ae463b92021b73442287b3" class="notion-header-anchor"></div><a class="notion-hash-link" href="#edeaca5cf9ae463b92021b73442287b3" title="秒杀核心点"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">秒杀核心点</span></span></h3><ol start="1" class="notion-list notion-list-numbered notion-block-9ce03a3e8bc249dfb2900017a2b09099"><li>价格计算 2.库存处理</li></ol><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-aec3051b40bd4bfe90349721e09d6295" data-id="aec3051b40bd4bfe90349721e09d6295"><span><div id="aec3051b40bd4bfe90349721e09d6295" class="notion-header-anchor"></div><a class="notion-hash-link" href="#aec3051b40bd4bfe90349721e09d6295" title="商品级别价格优惠计算"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">商品级别价格优惠计算</span></span></h4><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-edac4f91d1a141f384fc951fbfb24b91"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:584px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F654145df-c7cb-42ca-b73f-a31009dc57e5%2F9dc2b73e-a85a-4507-b43e-0d21ceee7e44%2FUntitled.png?table=block&amp;id=edac4f91-d1a1-41f3-84fc-951fbfb24b91" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-blank notion-block-d698d38492774de7863e1ed9c36fc6ea"> </div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-a371d9b182514c07985387988a2d3e29" data-id="a371d9b182514c07985387988a2d3e29"><span><div id="a371d9b182514c07985387988a2d3e29" class="notion-header-anchor"></div><a class="notion-hash-link" href="#a371d9b182514c07985387988a2d3e29" title="订单级别计算优惠"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">订单级别计算优惠</span></span></h4><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-89fe6c8f13904f4584363742a2af64b4"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:528px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F654145df-c7cb-42ca-b73f-a31009dc57e5%2F70d85e22-49fb-43c8-aa4d-c951d89ace27%2FUntitled.png?table=block&amp;id=89fe6c8f-1390-4f45-8436-3742a2af64b4" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-blank notion-block-67826c164bf6429d9553aae1625ae580"> </div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-8c87607b3a8b4b449c131c6252f187a1" data-id="8c87607b3a8b4b449c131c6252f187a1"><span><div id="8c87607b3a8b4b449c131c6252f187a1" class="notion-header-anchor"></div><a class="notion-hash-link" href="#8c87607b3a8b4b449c131c6252f187a1" title="库存问题"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">库存问题</span></span></h4><div class="notion-text notion-block-05bc1e0e01294062a8ee7ebc2a395536">高并发下超卖问题</div><div class="notion-blank notion-block-38dcee759f4d4157ad7ebff31f910e62"> </div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-bf76116e4fbb4214b94fd1e98f73ec4e" data-id="bf76116e4fbb4214b94fd1e98f73ec4e"><span><div id="bf76116e4fbb4214b94fd1e98f73ec4e" class="notion-header-anchor"></div><a class="notion-hash-link" href="#bf76116e4fbb4214b94fd1e98f73ec4e" title="何时扣减库存"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">何时扣减库存</span></span></h4><ol start="1" class="notion-list notion-list-numbered notion-block-044f1b888d1247fab8834d2c570b5126"><li>下单时扣减</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-40cd7934126b455d96f3b94304674920"><li>支付时扣减</li></ol><div class="notion-blank notion-block-46e1830ae1bc4cd18f943841e44bb2d0"> </div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-929eb3e9a6c3457dadc6e09ca179890c" data-id="929eb3e9a6c3457dadc6e09ca179890c"><span><div id="929eb3e9a6c3457dadc6e09ca179890c" class="notion-header-anchor"></div><a class="notion-hash-link" href="#929eb3e9a6c3457dadc6e09ca179890c" title="库存解决"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">库存解决</span></span></h4><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-0e41c20734b94ffa91c70bc43b005fc6" data-id="0e41c20734b94ffa91c70bc43b005fc6"><span><div id="0e41c20734b94ffa91c70bc43b005fc6" class="notion-header-anchor"></div><a class="notion-hash-link" href="#0e41c20734b94ffa91c70bc43b005fc6" title="悲观锁操作"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">悲观锁操作</span></span></h4><div class="notion-text notion-block-05ec05f9adc84d5d996f99286b1f92e9">其中for update 需要走索引不然会锁表</div><div class="notion-blank notion-block-4d2e2ee4048540d5bd4da9b8ebbc7aa3"> </div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-0acfbd37177e46a7aa433ade54a9b535" data-id="0acfbd37177e46a7aa433ade54a9b535"><span><div id="0acfbd37177e46a7aa433ade54a9b535" class="notion-header-anchor"></div><a class="notion-hash-link" href="#0acfbd37177e46a7aa433ade54a9b535" title="乐观锁"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">乐观锁</span></span></h4><div class="notion-blank notion-block-2553227920a74a7caaaf56b5d2c34a49"> </div><div class="notion-text notion-block-999c1b6ddb5e4a478c375be307819bb4">select 查询还更新库存，其实还需要插入订单和订单详情等数据,不在同1个库根本无法在本地事务来解决</div><div class="notion-blank notion-block-44224fa128304efca85521137f0f1f08"> </div><div class="notion-text notion-block-ef8f77529b91489e8d5172fea133599c">问题： </div><div class="notion-text notion-block-9ca04ad5ecec444b933fed3ff6c12b43">1.性能问题 <div class="notion-text-children"><div class="notion-text notion-block-6a529dc3a0744db68a154ce349e35693">无论是悲观锁还是乐观锁对需要对数据库进行上锁，而我们数据库的资源是非常有限的。</div></div></div><div class="notion-text notion-block-fe1dec26bbb64635b1b39ac52a9324bd">2.个数问题 <div class="notion-text-children"><div class="notion-text notion-block-84e477a42ec847eca070020b2ab5eda1">如果库存数量只有1个了，但是现在小明下单这时要买两个，那这条sql语句就有问题了，我们库存 只有一个，很明显不够卖了吧。</div></div></div><div class="notion-text notion-block-8ae3cc754a3042bbaef4cab88292285d">3架构问题<div class="notion-text-children"><div class="notion-text notion-block-1b0a98ccfda34b7ca1b82d3658650b3b">1000人请求，库存只有10个，实际990个请求没有意义</div><div class="notion-blank notion-block-26308f030e0148cca5c541503d5d4134"> </div><div class="notion-blank notion-block-71be15ee823143108197848042c1aa47"> </div></div></div><div class="notion-blank notion-block-4aa6f7db1fd641ecbcf62629adcab4c7"> </div><div class="notion-blank notion-block-bc26e3cae27c42a1a0b0562526c474a8"> </div><div class="notion-blank notion-block-bff140d994f24786ba2aa965e181a351"> </div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-8d9840a9568f435eabc6134f671d293b" data-id="8d9840a9568f435eabc6134f671d293b"><span><div id="8d9840a9568f435eabc6134f671d293b" class="notion-header-anchor"></div><a class="notion-hash-link" href="#8d9840a9568f435eabc6134f671d293b" title="优化性能问题"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">优化性能问题</span></span></h3><div class="notion-text notion-block-ed15f03f4650432f8476476d5b2aabfc">秒杀场景大量请求落到数据库，数据库肯定承受不了</div><div class="notion-blank notion-block-081de3c8999746858ba0b2f6969f5224"> </div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-68c391c6608b456c9c349911cf0313ae" data-id="68c391c6608b456c9c349911cf0313ae"><span><div id="68c391c6608b456c9c349911cf0313ae" class="notion-header-anchor"></div><a class="notion-hash-link" href="#68c391c6608b456c9c349911cf0313ae" title="预下单"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">预下单</span></span></h4><div class="notion-text notion-block-3d17f538c13e4071a3c6bc63137e3f97">这种情况可以把库存放到redis里面，秒杀下单先从redis里面获取库存数量</div><div class="notion-blank notion-block-7414ab2858114d22893ce2fd96bb2057"> </div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-9e37f23cdbfe4b1d934521c6e9171618" data-id="9e37f23cdbfe4b1d934521c6e9171618"><span><div id="9e37f23cdbfe4b1d934521c6e9171618" class="notion-header-anchor"></div><a class="notion-hash-link" href="#9e37f23cdbfe4b1d934521c6e9171618" title="预售库存"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">预售库存</span></span></h4><div class="notion-text notion-block-6b17573ef1264591b1a527dc470c071e">库存不从数据库里面扣减，而是从redis里面获取，那么redis扣减库存的数量从哪儿获取</div><div class="notion-blank notion-block-3c86522a4c614cc28b7686d94ebd85ab"> </div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-01710b0ad3384789994488c4733bf3f9" data-id="01710b0ad3384789994488c4733bf3f9"><span><div id="01710b0ad3384789994488c4733bf3f9" class="notion-header-anchor"></div><a class="notion-hash-link" href="#01710b0ad3384789994488c4733bf3f9" title="初始化（全量）"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">初始化（全量）</span></span></h4><div class="notion-text notion-block-ccc783cac4564722a0be343e32f43a01">RedisConfig#afterPropertiesSet</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-d0934b6faf6148a98d430651a43576fd"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F654145df-c7cb-42ca-b73f-a31009dc57e5%2F1f3c1e69-9735-4e41-873f-b7c9deddfb69%2FUntitled.png?table=block&amp;id=d0934b6f-af61-48a9-8d43-0651a43576fd" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-blank notion-block-90adffa2a4044125b835d8ea92a1b9ec"> </div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-344e1b469c8d4847887aa473437ddee7" data-id="344e1b469c8d4847887aa473437ddee7"><span><div id="344e1b469c8d4847887aa473437ddee7" class="notion-header-anchor"></div><a class="notion-hash-link" href="#344e1b469c8d4847887aa473437ddee7" title="问题"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">问题</span></span></h3><div class="notion-text notion-block-85f5de59dea445a38b52cc3134528c13">本地缓存是jvm级别，而各自jvm 售罄状态不一样</div><div class="notion-blank notion-block-4750ef6a86574caa8ee7f98e6e0a373c"> </div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-952b927b2d53477389d286ec0f3418f3" data-id="952b927b2d53477389d286ec0f3418f3"><span><div id="952b927b2d53477389d286ec0f3418f3" class="notion-header-anchor"></div><a class="notion-hash-link" href="#952b927b2d53477389d286ec0f3418f3" title="解决方案"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">解决方案</span></span></h3><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-138415117d8e4feb99dc3b48817f598e" data-id="138415117d8e4feb99dc3b48817f598e"><span><div id="138415117d8e4feb99dc3b48817f598e" class="notion-header-anchor"></div><a class="notion-hash-link" href="#138415117d8e4feb99dc3b48817f598e" title="zookeeper"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">zookeeper</span></span></h4><div class="notion-text notion-block-73293b21b1384b819bd31e1de641297c">可以用zookeeper的watch机制来实现，给毎个jvm都监听zk的某个就节点，一旦数据有改变之
后通知到其他节点上</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-d1466a559da346779d6c256301dfb94a"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:490px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F654145df-c7cb-42ca-b73f-a31009dc57e5%2F6f988129-483f-4417-8fb0-a7e311d05d79%2FUntitled.png?table=block&amp;id=d1466a55-9da3-4677-9d6c-256301dfb94a" alt="notion image" loading="lazy" decoding="async"/></div></figure><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-60eeeb4f452347488dd813c34a435134" data-id="60eeeb4f452347488dd813c34a435134"><span><div id="60eeeb4f452347488dd813c34a435134" class="notion-header-anchor"></div><a class="notion-hash-link" href="#60eeeb4f452347488dd813c34a435134" title="Redis"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">Redis</span></span></h4><div class="notion-text notion-block-223ed3b30f3948fb92a5749c94856bdc">利用redis的channel机制实现</div><div class="notion-blank notion-block-bf4435f9faf94ca697da70b7bc8f583f"> </div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-261f0feae765420a862506bfaab35613" data-id="261f0feae765420a862506bfaab35613"><span><div id="261f0feae765420a862506bfaab35613" class="notion-header-anchor"></div><a class="notion-hash-link" href="#261f0feae765420a862506bfaab35613" title="异步下单："><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">异步下单：</span></span></h2><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-357f90cf35a64400b249ff98c7efba3e" data-id="357f90cf35a64400b249ff98c7efba3e"><span><div id="357f90cf35a64400b249ff98c7efba3e" class="notion-header-anchor"></div><a class="notion-hash-link" href="#357f90cf35a64400b249ff98c7efba3e" title="秒杀下单"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">秒杀下单</span></span></h4><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-1651d9c9d58c48bdbfe94a39cf739e41" data-id="1651d9c9d58c48bdbfe94a39cf739e41"><span><div id="1651d9c9d58c48bdbfe94a39cf739e41" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1651d9c9d58c48bdbfe94a39cf739e41" title="处理订单"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">处理订单</span></span></h4><h4 class="notion-h notion-h3 notion-h-indent-1 notion-block-16f22e15634549a39c2f61db8a150cc5" data-id="16f22e15634549a39c2f61db8a150cc5"><span><div id="16f22e15634549a39c2f61db8a150cc5" class="notion-header-anchor"></div><a class="notion-hash-link" href="#16f22e15634549a39c2f61db8a150cc5" title="实际生产订单"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">实际生产订单</span></span></h4><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-6dcfe4b439b3423db0162cb64d3a6861" data-id="6dcfe4b439b3423db0162cb64d3a6861"><span><div id="6dcfe4b439b3423db0162cb64d3a6861" class="notion-header-anchor"></div><a class="notion-hash-link" href="#6dcfe4b439b3423db0162cb64d3a6861" title="订单超时取消"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">订单超时取消</span></span></h3><ol start="1" class="notion-list notion-list-numbered notion-block-f9440bfc3f634d1290319354f0cefa5f"><li>定时任务问题</li><ol class="notion-list notion-list-numbered notion-block-f9440bfc3f634d1290319354f0cefa5f"><li>11点启动定时任务，毎半个小时扫码数据库一次，我们发现在11:01分下的单并不能30分钟之后失效， 而是要到12点也就是定时任务第三次扫码数据库才能让订单失效。</li><li>定时扫数据库的话消耗性能也很大，自然效率也会很低。对数据库压力太大。</li><li>定时任务的话，集群还需要保证处理的幂等性和分布式问题。这也给系统带来了很多的负担。</li></ol></ol><ol start="2" class="notion-list notion-list-numbered notion-block-02502dbe5e4f44849cb4ea64b7a9e145"><li>创建订单 发送延时消息取消订单</li></ol></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[电商项目-商品-详情缓存]]></title>
            <link>https://blog.juanshen.eu.org/article/aa3ce288-75f7-4201-ab79-cc63dd19ee82</link>
            <guid>https://blog.juanshen.eu.org/article/aa3ce288-75f7-4201-ab79-cc63dd19ee82</guid>
            <pubDate>Sat, 02 Sep 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[详情高效查询]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-aa3ce28875f74201ab79cc63dd19ee82"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-309966c0efcc44e4a6426444c18c5aa8" data-id="309966c0efcc44e4a6426444c18c5aa8"><span><div id="309966c0efcc44e4a6426444c18c5aa8" class="notion-header-anchor"></div><a class="notion-hash-link" href="#309966c0efcc44e4a6426444c18c5aa8" title="商品详情页问题"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">商品详情页问题</span></span></h2><div class="notion-text notion-block-eed8c49882434f43acaf2fad8b3b2065">详情页承载了大部分流量和订单入口，并且还有各种个性化需求，比如商品纬度(标题，图片，属性)</div><div class="notion-text notion-block-fc7e2840fb68441cb0ce6ddfafc1afc3">主商品纬度（商品简介，规格参数）分类维度，商家维度，店铺维度，还有一些实时要求较高的</div><div class="notion-text notion-block-18ff30039d8743ce85d96940e400004b">实时价格，实时促销，广告词，配送，预售等是通过异步加载</div><div class="notion-blank notion-block-7de911fb8130472f88aba1a0378e4a90"> </div><div class="notion-text notion-block-7cbee82815cc408d8f233957f6fdf481">SPU:Standard Product Unit (标准化产品单元),SPU是商品信息聚合的最小单位，是一组
可复用、易检索的标准化信息的集合，该集合描述了一个产品的特性。</div><div class="notion-blank notion-block-5b0681f771744057b03fd1df2d3a0f21"> </div><div class="notion-text notion-block-57e2ad4b6727492eae668c60950e4035">SKU:Stock keeping unit(库存量单位) SKU即库存进出计量的单位(买家购买、商家进货、 供应商备货、工厂生产都是依据SKU进行的)，在服装、鞋类商品中使用最多最普遍。 例如纺 织品中一个SKU通常表示:规格、颜色、款式。SKU是物理上不可分割的最小存货单元。</div><div class="notion-blank notion-block-83e90bca444344938ae7c0e640d56387"> </div><div class="notion-blank notion-block-406bca47393c4656a694387555e5816c"> </div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-361667058e06458ab5710b939f5c6b6e" data-id="361667058e06458ab5710b939f5c6b6e"><span><div id="361667058e06458ab5710b939f5c6b6e" class="notion-header-anchor"></div><a class="notion-hash-link" href="#361667058e06458ab5710b939f5c6b6e" title="优化"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">优化</span></span></h2><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-082a447850e54e3886b11c59900b33b2" data-id="082a447850e54e3886b11c59900b33b2"><span><div id="082a447850e54e3886b11c59900b33b2" class="notion-header-anchor"></div><a class="notion-hash-link" href="#082a447850e54e3886b11c59900b33b2" title="页面静态化"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">页面静态化</span></span></h3><div class="notion-text notion-block-010075ea108f4a51ab5fd5bc5e161619">FreeMarker 是一款模板引擎:即基于模板和数据源生成输出文本(html网页，配置文件，电 子邮件，源代码)的通用工具。它是一个 java 类库，最初被设计用来在MVC模式的Web开发</div><div class="notion-text notion-block-d78146af3abd42b9928decbae64683de">框架中生成HTML页面，它没有被绑定到Servlet或HTML或任意Web相关的东西上。也可以用
于非Web应用环境中。<!-- -->
模板编写使用FreeMarker Template Language(FTL)。使用方式类似JSP的EL表达式。模板中
专注于如何展示数据，模板之外可以专注于要展示什么数据。</div><div class="notion-blank notion-block-b917d3bf17504dc8be78569634fc992a"> </div><div class="notion-text notion-block-688680639abb472db4533c86dfb9264f">问题：价格改变、秒杀 倒计时、下单 ??js没有生效(js、css、图片url)，新增商品不同服务器需要更新静态化模版</div><div class="notion-blank notion-block-ad84f427b0ed4cad960f82d2fdaf0747"> </div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-ed1562de09f04e988bc2687fea387d3b" data-id="ed1562de09f04e988bc2687fea387d3b"><span><div id="ed1562de09f04e988bc2687fea387d3b" class="notion-header-anchor"></div><a class="notion-hash-link" href="#ed1562de09f04e988bc2687fea387d3b" title="后台优化"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">后台优化</span></span></h3><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-e17e3c75df60436682bf3dfa7d8a40bc" data-id="e17e3c75df60436682bf3dfa7d8a40bc"><span><div id="e17e3c75df60436682bf3dfa7d8a40bc" class="notion-header-anchor"></div><a class="notion-hash-link" href="#e17e3c75df60436682bf3dfa7d8a40bc" title="redis缓存"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">redis缓存</span></span></h4><div class="notion-text notion-block-2d347fa95a5244f88868933c5a9f22e0">加入redis之后我们发现提高了可以把之前请求 数据库查询的商品都缓存到redis中，通过对 redis的访问来减少对数据里的依赖，减少了依赖本质就是减少了磁盘IO。</div><div class="notion-blank notion-block-7363a15286d94f69ad18b8c6f76c0863"> </div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-e6e6183fa4954030a87810ed86a1ab05" data-id="e6e6183fa4954030a87810ed86a1ab05"><span><div id="e6e6183fa4954030a87810ed86a1ab05" class="notion-header-anchor"></div><a class="notion-hash-link" href="#e6e6183fa4954030a87810ed86a1ab05" title="上面代码的问题"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">上面代码的问题</span></span></h3><div class="notion-text notion-block-46995aaa7f7645a19042cd457ae627bd">缓存中如果没有数据，大量请求过来，在线程没有把数据存入到redis中，大部分线程都会去查询sql,这是我们不想的，因此需要加锁，目前都是针对分布式情况</div><div class="notion-blank notion-block-77e036e726794124ab2a7ef658c0d2e4"> </div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-b4c62bd911cf4653aa8f5eb5d30f19e6" data-id="b4c62bd911cf4653aa8f5eb5d30f19e6"><span><div id="b4c62bd911cf4653aa8f5eb5d30f19e6" class="notion-header-anchor"></div><a class="notion-hash-link" href="#b4c62bd911cf4653aa8f5eb5d30f19e6" title="加入分布式锁"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">加入分布式锁</span></span></h4><div class="notion-blank notion-block-0357e6da20714ff189f1ad81b8abdf0c"> </div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-8a8ced51cff949db8d251f0137fbe513" data-id="8a8ced51cff949db8d251f0137fbe513"><span><div id="8a8ced51cff949db8d251f0137fbe513" class="notion-header-anchor"></div><a class="notion-hash-link" href="#8a8ced51cff949db8d251f0137fbe513" title="缓存应用场景"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">缓存应用场景</span></span></h3><ol start="1" class="notion-list notion-list-numbered notion-block-fd26259e0aaa471daa2967116d570b17"><li>访问量大，qps 高，更新频率不同</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-fea750ef470e4d7e9813a4085d575dda"><li>数据一致性要求不是特别高</li></ol><div class="notion-blank notion-block-bde03549ac374dbf87ba1eb2f4622bd3"> </div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-b9828da8812142a08f0aabb93a9b78cb" data-id="b9828da8812142a08f0aabb93a9b78cb"><span><div id="b9828da8812142a08f0aabb93a9b78cb" class="notion-header-anchor"></div><a class="notion-hash-link" href="#b9828da8812142a08f0aabb93a9b78cb" title="缓存不足的问题"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">缓存不足的问题</span></span></h3><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-6f3a9ce062a84197a2924516beadc987" data-id="6f3a9ce062a84197a2924516beadc987"><span><div id="6f3a9ce062a84197a2924516beadc987" class="notion-header-anchor"></div><a class="notion-hash-link" href="#6f3a9ce062a84197a2924516beadc987" title="缓存击穿问题（热点数据单个key）"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">缓存击穿问题（热点数据单个key）</span></span></h4><div class="notion-text notion-block-ab8c6fa44a2742adb65d857eedc66b1e">对于一些设置了过期时间的key，如果这些key可能会在某些时间点被超高并发地访问，是一种 非常“热点”的数据。这个时候，需要考虑一个问题:缓存被“击穿”的问题，这个和缓存雪 崩的区别在于这里针对某一key缓存，前者则是很多key。</div><div class="notion-blank notion-block-22b14acd701f46ebadab7655504201de"> </div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-2b57b3354a0144e8b6a628590e5aaf7c" data-id="2b57b3354a0144e8b6a628590e5aaf7c"><span><div id="2b57b3354a0144e8b6a628590e5aaf7c" class="notion-header-anchor"></div><a class="notion-hash-link" href="#2b57b3354a0144e8b6a628590e5aaf7c" title="解决方案"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">解决方案</span></span></h4><ol start="1" class="notion-list notion-list-numbered notion-block-e8c3ee0e2516440b8aaa42bf4157b529"><li>加锁，未命中通过加锁避免大量请求访问数据库</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-2755521938584ed8b804403208d558a5"><li>不允许过期，后台有任务异步更新数据</li></ol><div class="notion-blank notion-block-18fbc4b2828f43cd8d068aaa454abead"> </div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-979b22291d93431189f79d7488860abe" data-id="979b22291d93431189f79d7488860abe"><span><div id="979b22291d93431189f79d7488860abe" class="notion-header-anchor"></div><a class="notion-hash-link" href="#979b22291d93431189f79d7488860abe" title="缓存穿透问题（一般是恶意攻击访问不存在的数据）"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">缓存穿透问题（一般是恶意攻击访问不存在的数据）</span></span></h4><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-fe6a5f851f204fae8cc9881b00f8c0aa" data-id="fe6a5f851f204fae8cc9881b00f8c0aa"><span><div id="fe6a5f851f204fae8cc9881b00f8c0aa" class="notion-header-anchor"></div><a class="notion-hash-link" href="#fe6a5f851f204fae8cc9881b00f8c0aa" title="解决方案："><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">解决方案：</span></span></h4><ol start="1" class="notion-list notion-list-numbered notion-block-078249dd3db54bc9b3c0dba11ec8da47"><li>布隆过滤器 将所有可能存在的数据哈希到一个足够大的 bitmap中，一个一定不存在的数据会被 这个bitmap拦截掉，从而避免了对底层存储系统的查 询压力。</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-b741f5b1eada4b02907016f8a2e6ac31"><li>缓存空数据并设置短暂的过期时间</li></ol><div class="notion-blank notion-block-0eb483ddc27444b6852f204c5384d7ba"> </div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-0bed6180ec15487b9caf6d01916fa6c0" data-id="0bed6180ec15487b9caf6d01916fa6c0"><span><div id="0bed6180ec15487b9caf6d01916fa6c0" class="notion-header-anchor"></div><a class="notion-hash-link" href="#0bed6180ec15487b9caf6d01916fa6c0" title="缓存雪崩"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">缓存雪崩</span></span></h4><div class="notion-text notion-block-33bb3cb1a3514b60b1fe2687214f2742">存雪崩是指在我们设置缓存时采用了相同的过期时间，导致缓存在某一时刻同时失效，请求全 部转发到DB，DB瞬时压力过重雪崩。</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-a47eeb00b7c245ffa308567186e73f08" data-id="a47eeb00b7c245ffa308567186e73f08"><span><div id="a47eeb00b7c245ffa308567186e73f08" class="notion-header-anchor"></div><a class="notion-hash-link" href="#a47eeb00b7c245ffa308567186e73f08" title="解决方案"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">解决方案</span></span></h4><ol start="1" class="notion-list notion-list-numbered notion-block-628a18bff6b9427daf420eec60241bca"><li>设置超时时间+随机时间防止同一个时间批量缓存失效</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-5dca18fb47924721bf799e91737b30a7"><li>事前 使用主从，哨兵，redis cluster 来避免redis 全盘崩溃</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-153943fbaa60470c980583b9dcdca727"><li>事中 使用降级 限流等策略 </li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-c61e3420821a44f2a1737be6a8e2ca21"><li>事后 redis持久化机制，尽快恢复数据</li></ol><div class="notion-blank notion-block-8bcc3a8f514a40d3933549973646fb7f"> </div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-becb6394f94a4eddbb0fd8d1da3a1c57" data-id="becb6394f94a4eddbb0fd8d1da3a1c57"><span><div id="becb6394f94a4eddbb0fd8d1da3a1c57" class="notion-header-anchor"></div><a class="notion-hash-link" href="#becb6394f94a4eddbb0fd8d1da3a1c57" title="缓存和数据库双写一致性问题"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">缓存和数据库双写一致性问题</span></span></h3><div class="notion-text notion-block-5dd9db3982d44d1d98c6d93daa25c5c8">一致性问题是分布式常见问题，还可以再分为最终一致性和强一致性。数据库和缓存双写，就必然会存在不一致的问题。
就是如果对数据有强一致性要求，不能放缓存。
我们所做的一切，只能保证最终一致性。另外，我们所做的方案其实从根本上来说，只能说降
低不一致发生的概率，无法完全避免。因此，有强一致性要求的数据，不能放缓存。</div><div class="notion-text notion-block-b0387d3b7de24f6c84c4211a49542c19">一般使用：更新db,然后删除缓存</div><div class="notion-text notion-block-47ebe435054c46b7ab12a8eccd19bdb5">也有使用双删策略</div><div class="notion-blank notion-block-cf2ea77bb98e4efb83753a3836868b96"> </div><div class="notion-blank notion-block-de1c0d63f7524ba2a7b0ba345fd9f7f3"> </div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-1feee7d1b1284b0b9a874cafb7ffea3e" data-id="1feee7d1b1284b0b9a874cafb7ffea3e"><span><div id="1feee7d1b1284b0b9a874cafb7ffea3e" class="notion-header-anchor"></div><a class="notion-hash-link" href="#1feee7d1b1284b0b9a874cafb7ffea3e" title="缓存优化"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">缓存优化</span></span></h3><div class="notion-text notion-block-73c74ce675d64706b208b7e9405f25e0">网站响应的速度，一般是网络IO和磁盘IO这两块影响很大，引入缓存本质是减少了磁盘IO,下面引入</div><div class="notion-text notion-block-71702aa8e5024f4c8e7c3a48ceeb56ce">本地缓存来优化网络I</div><div class="notion-blank notion-block-3b3e24b7a3eb424aa0b809cf56aee2b6"> </div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-78ded7366cd1416d86e7b13f0aede0a0" data-id="78ded7366cd1416d86e7b13f0aede0a0"><span><div id="78ded7366cd1416d86e7b13f0aede0a0" class="notion-header-anchor"></div><a class="notion-hash-link" href="#78ded7366cd1416d86e7b13f0aede0a0" title="引入本地缓存"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">引入本地缓存</span></span></h4><div class="notion-blank notion-block-7a49393168f8476aae6f84af50965fba"> </div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-eafe393cd5534ddd96f6868ca5bdf7c5" data-id="eafe393cd5534ddd96f6868ca5bdf7c5"><span><div id="eafe393cd5534ddd96f6868ca5bdf7c5" class="notion-header-anchor"></div><a class="notion-hash-link" href="#eafe393cd5534ddd96f6868ca5bdf7c5" title="本地缓存的不足"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">本地缓存的不足</span></span></h4><div class="notion-text notion-block-f5794307664f4380a39f5a50e48af164">可以缓存，如果有些商品访问越来越少，这个时候不需要缓存了，那请问我现在的本地缓存如何 清理掉了？怎么知道哪些数据是删除的哪些数据是不需要删除的了?这是不是得一个算法来统计下这个访问 量，然后根据排序来选择删除部分数据.</div><div class="notion-blank notion-block-387bcae596ae4f288ab2a0aac948257a"> </div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-ef58e2c7479c45d197f076089dbc43b5" data-id="ef58e2c7479c45d197f076089dbc43b5"><span><div id="ef58e2c7479c45d197f076089dbc43b5" class="notion-header-anchor"></div><a class="notion-hash-link" href="#ef58e2c7479c45d197f076089dbc43b5" title="引入gava缓存"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">引入gava缓存</span></span></h4><div class="notion-text notion-block-0f876b0725f1483abb636237d66844d3">&lt;dependency&gt;
 &lt;groupId&gt;com.google.guava&lt;/groupId&gt;
 &lt;artifactId&gt;guava&lt;/artifactId&gt;</div><div class="notion-text notion-block-aa0df3d6475a4aa5b488aa9765acdf59">&lt;version&gt;22&lt;version&gt;</div><div class="notion-text notion-block-ddb0c085d34e4df2b3c99579a8ed5ccd">&lt;/dependency&gt;</div><div class="notion-blank notion-block-e4d17329fa9f4ba299f38cff2b6c9a7f"> </div><div class="notion-blank notion-block-ab60951429e942cd89bd9ea021d6c3d6"> </div><div class="notion-text notion-block-f485ac8b90d64ca0a9a7e0518293c420">致谢：</div><div class="notion-callout notion-gray_background_co notion-block-1812f1d935b34e3faaea418f286b5f57"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="💡">💡</span></div><div class="notion-callout-text">有关Notion安装或者使用上的问题，欢迎您在底部评论区留言，一起交流~</div></div><div class="notion-blank notion-block-1adbcafce4924e7899861ec53628f150"> </div><div class="notion-blank notion-block-1fe32a7392f245fcb7a05be4465aaefa"> </div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[电商项目-商品-表设计]]></title>
            <link>https://blog.juanshen.eu.org/article/11884fe3-619f-40b8-8da8-d0f56e112589</link>
            <guid>https://blog.juanshen.eu.org/article/11884fe3-619f-40b8-8da8-d0f56e112589</guid>
            <pubDate>Sat, 26 Aug 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[商品模块表设计的]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-11884fe3619f40b88da8d0f56e112589"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-0c45254ed4644f33a02b11b2ce2b6b28" data-id="0c45254ed4644f33a02b11b2ce2b6b28"><span><div id="0c45254ed4644f33a02b11b2ce2b6b28" class="notion-header-anchor"></div><a class="notion-hash-link" href="#0c45254ed4644f33a02b11b2ce2b6b28" title="商品一般业务场景介绍"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">商品一般业务场景介绍</span></span></h2><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-09b68e52d19d442a909fd5b2dafcafe7"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F654145df-c7cb-42ca-b73f-a31009dc57e5%2F899c93a0-ec72-4e47-93a6-f41aa9e2ea5f%2FUntitled.png?table=block&amp;id=09b68e52-d19d-442a-909f-d5b2dafcafe7" alt="notion image" loading="lazy" decoding="async"/></div></figure><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-696da99d2fb440a58a99b75a660e8010"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:683px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F654145df-c7cb-42ca-b73f-a31009dc57e5%2F1521bad8-f11b-44f4-9758-5bc556d4ce15%2FUntitled.png?table=block&amp;id=696da99d-2fb4-40a5-8a99-b75a660e8010" alt="notion image" loading="lazy" decoding="async"/></div></figure><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-7a04c9fb3bc043f688fa7ca1ed3be632"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:637px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F654145df-c7cb-42ca-b73f-a31009dc57e5%2F0ba8307b-6a11-4d4e-beba-662ce8e45637%2FUntitled.png?table=block&amp;id=7a04c9fb-3bc0-43f6-88fa-7ca1ed3be632" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-blank notion-block-02e7dd2effd8422c875aa02c708f9156"> </div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-665fbdc983f9440188e07fcda50dc75b" data-id="665fbdc983f9440188e07fcda50dc75b"><span><div id="665fbdc983f9440188e07fcda50dc75b" class="notion-header-anchor"></div><a class="notion-hash-link" href="#665fbdc983f9440188e07fcda50dc75b" title="表设计"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">表设计</span></span></h3><div class="notion-text notion-block-3ee995f6b27d42209cc0f79b3e20e6ed">商品有很多名词，比如分类，属性，评价等他们关系如下</div><div class="notion-text notion-block-401f1c8ba521436d9e918c6bd271a262">分析:
分类:一个商品属于某个分类
商品属性:一个商品有N个属性，不同的商品有不同的属性
商品品牌:一个商品属于一个品牌，一个品牌下面有多个商品
商品参数:不同的商品参数不一样</div><div class="notion-text notion-block-c7895cdd408544bd9c4fcd424510947a">商品活动:一个商品可以有多个活动。
评价:一个商品有多条评设计</div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-a88e93856c7b47cbbf03ce7a13a61d74" data-id="a88e93856c7b47cbbf03ce7a13a61d74"><span><div id="a88e93856c7b47cbbf03ce7a13a61d74" class="notion-header-anchor"></div><a class="notion-hash-link" href="#a88e93856c7b47cbbf03ce7a13a61d74" title="版本1:商品+分类"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">版本1:商品+分类</span></span></h4><div class="notion-text notion-block-eef932d371944317802dfb49d766d14e">问题：比如有nick鞋和衣服，用户希望看到所有nick商品，这个模型就不能满足，因此加入了<span class="notion-purple"><b>品牌</b></span>概念</div><div class="notion-blank notion-block-2daa59c9bed24c24acb97912ff23272e"> </div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-a06d8fd23f534602ab4853c5649c0021" data-id="a06d8fd23f534602ab4853c5649c0021"><span><div id="a06d8fd23f534602ab4853c5649c0021" class="notion-header-anchor"></div><a class="notion-hash-link" href="#a06d8fd23f534602ab4853c5649c0021" title="版本2: 商品+分类+品牌"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">版本2: 商品+分类+品牌</span></span></h4><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-8c9501d40f4f44e6aba81f24f69851a2"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F654145df-c7cb-42ca-b73f-a31009dc57e5%2Fae6004de-d384-4275-b63d-babf473eba3e%2FUntitled.png?table=block&amp;id=8c9501d4-0f4f-44e6-aba8-1f24f69851a2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-blank notion-block-d855de76dd9c476b850d1adad8ebe624"> </div><div class="notion-text notion-block-8103064991dd432396ebbba3ae904e1c">问题：用户通过品牌或者分类搜索后希望进一步缩小自己范围，比如买电脑 可以选择cpu 型号，显卡型号等</div><div class="notion-blank notion-block-2ba3dad27c764c01afe6d0289a9a0c7a"> </div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-dba553e50ecb4fe1a3d7c2bd6acb014a" data-id="dba553e50ecb4fe1a3d7c2bd6acb014a"><span><div id="dba553e50ecb4fe1a3d7c2bd6acb014a" class="notion-header-anchor"></div><a class="notion-hash-link" href="#dba553e50ecb4fe1a3d7c2bd6acb014a" title="版本3: 商品+分类+品牌+属性"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">版本3: 商品+分类+品牌+属性</span></span></h4><div class="notion-blank notion-block-b13c088140f84df794e8629ca82eacec"> </div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-e9d6f7760e2f4ffaa420309ef7cc5560"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F654145df-c7cb-42ca-b73f-a31009dc57e5%2Fda73ac74-d74d-42e7-a3e6-615e1163fbd2%2FUntitled.png?table=block&amp;id=e9d6f776-0e2f-4ffa-a420-309ef7cc5560" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-ea511b70bcce4cdaaec0eb8963f280c4">问题：以京东商城的手机为例子，颜色和尺寸，一件商品不同颜色不同尺寸算1个商品还是多个商品</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-9f8deee01a6a4702967946ca066d143e"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F654145df-c7cb-42ca-b73f-a31009dc57e5%2F489b5eae-d2f5-45b6-82cc-73f324d5959c%2FUntitled.png?table=block&amp;id=9f8deee0-1a6a-4702-9679-46ca066d143e" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-blank notion-block-8dc9f53ffddb4ef180cdd35318941104"> </div><h4 class="notion-h notion-h3 notion-h-indent-2 notion-block-4aec176046a441cbbfd241412278fd97" data-id="4aec176046a441cbbfd241412278fd97"><span><div id="4aec176046a441cbbfd241412278fd97" class="notion-header-anchor"></div><a class="notion-hash-link" href="#4aec176046a441cbbfd241412278fd97" title="版本4: 商品+分类+品牌+属性+规格"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title">版本4: 商品+分类+品牌+属性+规格</span></span></h4><div class="notion-text notion-block-71f71fb60d28448b84c25debee43e7a3">经过思考后，我们发现我们混淆了两个概念——“商品”和“货品”。不同规格的货品作为独 立的商品。比如一条裤子的有L尺寸、M尺寸、一个U盘有16G还是32G的，都是同样的货品，不同 规格的商品。可以认为货品和商品是一对多的关系。弄清了这个概念，处理这个需求就容易多 了，这里的“颜色”、“尺寸”我们就作为“规格”来处理，而红色、黑色;L号、M号我们视 为规格的选项或者说规格值。一件货品对应若干规格，而具有某一规格值的货品就是商品。 spu:iphone12 sku:金色64 iphone12</div><div class="notion-blank notion-block-3b2ea9e91e2342a9b21d60d39cafcc81"> </div><div class="notion-text notion-block-6e84ea52ac664ac0bcc39d76416ea393">那规格和属性的区别是什么？</div><div class="notion-text notion-block-bcbfe1b1f0c74b1db74db900b84344d9">属性是spu级别如：电脑有内存，cpu,硬盘</div><div class="notion-text notion-block-a09aa0610c204abd9969f9eac5a01270">规格是sku级别：多大内存，什么颜色，什么尺寸，大部分规格不同都会导致价格的不同</div><div class="notion-blank notion-block-1a9ec842a98748ac864cfedecc56e7b9"> </div><div class="notion-blank notion-block-bd1eb140d85a4477b2d409e1d9404ed7"> </div><div class="notion-blank notion-block-0ec304e75030466fb4bf585ebae52857"> </div><div class="notion-blank notion-block-337a250ed3384b80b32c8ab095080d34"> </div><div class="notion-blank notion-block-beacdc17d1494fb0b730ee6f43501292"> </div><div class="notion-blank notion-block-10fcd9bad9844ffa897a0af4bf6632dc"> </div><div class="notion-blank notion-block-43ef89f8cd2f4976a33ff7e9a7028a3c"> </div><div class="notion-blank notion-block-95d199e793294cbab12badc287e53236"> </div><div class="notion-blank notion-block-c7e69c14bf864f90a68ea0be564fd14e"> </div><div class="notion-blank notion-block-5213992df83f4b9389ef37d13b15608d"> </div><div class="notion-callout notion-gray_background_co notion-block-42d0eef358bd476fa9d7822226084e42"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="💡">💡</span></div><div class="notion-callout-text">有关Notion安装或者使用上的问题，欢迎您在底部评论区留言，一起交流~</div></div><div class="notion-blank notion-block-44023238ee7d41eeac749db88e97d17d"> </div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[rocketMQ核心编程模型]]></title>
            <link>https://blog.juanshen.eu.org/article/dfe93bc0-1396-4eb5-ba09-2547a3d64204</link>
            <guid>https://blog.juanshen.eu.org/article/dfe93bc0-1396-4eb5-ba09-2547a3d64204</guid>
            <pubDate>Sat, 05 Aug 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[rocketmq消息模型 和 整合springboot]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-dfe93bc013964eb5ba092547a3d64204"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><blockquote class="notion-quote notion-block-c6d1bf813ec64587b5aacbfabffd69f7"><div>文章来源说明</div></blockquote><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-c9113a73d9bc44e5b89afe9361988af8" data-id="c9113a73d9bc44e5b89afe9361988af8"><span><div id="c9113a73d9bc44e5b89afe9361988af8" class="notion-header-anchor"></div><a class="notion-hash-link" href="#c9113a73d9bc44e5b89afe9361988af8" title="RocketMQ的消息模型"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>RocketMQ的消息模型</b></span></span></h2><div class="notion-text notion-block-82120c34fa924257a571a02cdc3ce4d7">RocketMQ基于Maven提供了客户端的核心依赖：</div><div class="notion-blank notion-block-f7ab2f68fbc3400b95d6f58f9f90286b"> </div><div class="notion-text notion-block-87c908f0d8de4c22aff8205b533d4269">使用客户端进行编程时，添加这一个核心依赖就够了。 另外还有一个与权限控制相关的核心依赖也需要了解。尽量保持与服务端版一致。</div><div class="notion-text notion-block-26a902f0a82c405388eeb3820c7e3065">一个最为简单的消息生产者代码如下：</div><div class="notion-blank notion-block-46867cd84fcd4117ad34ee38f29e3416"> </div><div class="notion-text notion-block-15ac3fd760044434a28d9e5e69f072c2">一个简单的消息消费者代码如下：</div><div class="notion-text notion-block-8063aac19e8d4571bdb8eddeb42d2ad5">RocketMQ的客户端编程模型相对比较固定，基本都有一个固定的步骤。掌握这个固定步骤，对于学习其他复杂的消息模型也是很有帮助的。</div><ul class="notion-list notion-list-disc notion-block-25114308757c4438a10be5922cd2e66a"><li>消息生产者的固定步骤</li></ul><div class="notion-text notion-block-819f4ae19ad048d49ec1190a44ffc7b9">1.创建消息生产者producer，并指定生产者组名
2.指定Nameserver地址
3.启动producer。 这个步骤比较容易忘记。可以认为这是消息生产者与服务端建立连接的过程。
4.创建消息对象，指定主题Topic、Tag和消息体
5.发送消息
6.关闭生产者producer，释放资源。</div><div class="notion-blank notion-block-5d5f95e3e69843f3b4614ab304e5fbe2"> </div><ul class="notion-list notion-list-disc notion-block-eaeec5608f1140fdbfb02d7a6582f9db"><li>消息消费者的固定步骤</li></ul><div class="notion-text notion-block-008a8854e774402f8530bbba7567202c">2.指定Nameserver地址
3.订阅主题Topic和Tag
4.设置回调函数，处理消息
5.启动消费者consumer。消费者会一直挂起，持续处理消息。</div><div class="notion-text notion-block-9bb47a1f52c04c76a4d71f2b25b82d35">	其中，最为关键的就是NameServer。从示例中可以看到，RocketMQ的客户端只需要指定NameServer地址，而不需要指定具体的Broker地址。</div><div class="notion-text notion-block-b2d73f93cfea4783b67c85ee658e175a">	指定NameServer的方式有两种。可以在客户端直接指定，例如 consumer.setNameSrvAddr(&quot;127.0.0.1:9876&quot;)。然后，也可以通过读取系统环境变量NAMESRV\_ADDR指定。其中第一种方式的优先级更高。</div><div class="notion-blank notion-block-fb741c7bf43a42afb2633bd2bc20810d"> </div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-4090d4d943eb4458b40a2640a201c655" data-id="4090d4d943eb4458b40a2640a201c655"><span><div id="4090d4d943eb4458b40a2640a201c655" class="notion-header-anchor"></div><a class="notion-hash-link" href="#4090d4d943eb4458b40a2640a201c655" title="2、消息确认机制"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>2、消息确认机制</b></span></span></h3><div class="notion-text notion-block-d6cfc9e9038645a3a7b5bce7b55d3960">RocketMQ要支持互联网金融场景，那么消息安全是必须优先保障的。而消息安全有两方面的要求，一方面是生产者要能确保将消息发送到Broker上。另一方面是消费者要能确保从Broker上争取获取到消息。</div><div class="notion-text notion-block-fbf161c2453d4f16b3b5b3d6d622c76a">1<b>、消息生产端采用消息确认加多次重试的机制保证消息正常发送到RocketMQ</b></div><div class="notion-text notion-block-5de471f14c9c4ccdab3aa43df39eb750">	针对消息发送的不确定性，封装了三种发送消息的方式。</div><div class="notion-text notion-block-5b1b5710623e4dbf86f586f43af29e33"><b>第一种称为单向发送</b></div><div class="notion-text notion-block-538d71fb36da46a484b17703f195a4af">单向发送方式下，消息生产者只管往Broker发送消息，而全然不关心Broker端有没有成功接收到消息。这就好比生产者向Broker发一封电子邮件，Broker有没有处理电子邮件，生产者并不知道。</div><div class="notion-blank notion-block-31d34697d82c48ed9f3356605cd0769e"> </div><div class="notion-text notion-block-ddcbc19229b44d938ed49cd1b7a299d7">sendOneway方法没有返回值，如果发送失败，生产者无法补救。</div><div class="notion-text notion-block-6fee38b5d0d7488686e4567c3c804db3">	单向发送有一个好处，就是发送消息的效率更高。适用于一些追求消息发送效率，而允许消息丢失的业务场景。比如日志。</div><div class="notion-blank notion-block-31c28fff89294de1a9387368dc467cea"> </div><div class="notion-text notion-block-b15eba685b8a4f908b5f847d81fc2856"><b>第二种称为同步发送</b></div><div class="notion-text notion-block-780cdf38250e4401b6a486333dfa447e">	同步发送方式下，消息生产者在往Broker端发送消息后，会阻塞当前线程，等待Broker端的相应结果。这就好比生产者给Broker打了个电话。通话期间生产者就停下手头的事情，直到Broker明确表示消息处理成功了，生产者才继续做其他的事情。</div><div class="notion-blank notion-block-b4b2543e4fdb45508126caf152b2579d"> </div><div class="notion-text notion-block-b11bbf5928d94c029426a6b9ee08cfce">SendResult来自于Broker的反馈。producer在send发出消息，到Broker返回SendResult的过程中，无法做其他的事情。</div><div class="notion-text notion-block-074f6ffb2bdf4100aff20ecf98a99c62">	在SendResult中有一个SendStatus属性，这个SendStatus是一个枚举类型，其中包含了Broker端的各种情况。</div><div class="notion-text notion-block-6e6db78f5ba549eaaacf39570d881977">在这几种枚举值中，SEND_OK表示消息已经成功发送到Broker上。至于其他几种枚举值，都是表示消息在Broker端处理失败了。使用同步发送的机制，我们就可以在消息生产者发送完消息后，对发送失败的消息进行补救。例如重新发送。</div><div class="notion-text notion-block-bcfc248bb8ed4d638dac4f27947034a6">	<span class="notion-red">但是此时要注意，如果Broker端返回的SendStatus不是SEND_OK，也并不表示消息就一定不会推送给下游的消费者。仅仅只是表示Broker端并没有完全正确的处理这些消息。因此，如果要重新发送消息，最好要带上唯一的系统标识，这样在消费者端，才能自行做幂等判断。也就是用具有业务含义的OrderID这样的字段来判断消息有没有被重复处理。</span></div><div class="notion-text notion-block-c86a02c14f524480a0dc798b418c2b12">	这种同步发送的机制能够很大程度上保证消息发送的安全性。但是，这种同步发送机制的发送效率比较低。毕竟，send方法需要消息在生产者和Broker之间传输一个来回后才能结束。如果网速比较慢，同步发送的耗时就会很长。</div><div class="notion-blank notion-block-71fa0a0fc318452a9090c9aac461fd02"> </div><div class="notion-text notion-block-4593ecd5bf5b4122bba151ef63a1e643"><b>第三种称为异步发送</b></div><div class="notion-text notion-block-732d749aa9d94798ab05e81b460669d1">	异步发送机制下，生产者在向Broker发送消息时，会同时注册一个回调函数。接下来生产者并不等待Broker的响应。当Broker端有响应数据过来时，自动触发回调函数进行对应的处理。这就好比生产者向Broker发电子邮件通知时，另外找了一个代理人专门等待Broker的响应。而生产者自己则发完消息后就去做其他的事情去了</div><div class="notion-blank notion-block-0e26e362571f40adae21819f4c889ce9"> </div><div class="notion-text notion-block-f24c3d636dfc44c2b64487ff65499d6e">在SendCallback接口中有两个方法，onSuccess和onException。当Broker端返回消息处理成功的响应信息SendResult时，就会调用onSuccess方法。当Broker端处理消息超时或者失败时，就会调用onExcetion方法，生产者就可以在onException方法中进行补救措施。</div><div class="notion-text notion-block-005679d797fa4e9f92de9b8cda38a430">	此时同样有几个问题需要注意。一是与同步发送机制类似，触发了SendCallback的onException方法同样并不一定就表示消息不会向消费者推送。如果Broker端返回响应信息太慢，超过了超时时间，也会触发onException方法。超时时间默认是3秒，可以通过<b>producer.setSendMsgTimeout</b>方法定制。而造成超时的原因则有很多，消息太大造成网络拥堵、网速太慢、Broker端处理太慢等都可能造成消息处理超时。</div><div class="notion-text notion-block-fc5a4775db5346d3adc87f107b6cd27e">	二是在SendCallback的对应方法被触发之前，生产者不能调用shutdown()方法。如果消息处理完之前，生产者线程就关闭了，生产者的SendCallback对应方法就不会触发。这是因为使用异步发送机制后，生产者虽然不用阻塞下来等待Broker端响应，但是SendCallback还是需要附属于生产者的主线程才能执行。如果Broker端还没有返回SendResult，而生产者主线程已经停止了，那么SendCallback的执行线程也就会随主线程一起停止，对应的方法自然也就无法执行了。</div><div class="notion-text notion-block-bbd12c09a64b43abb585a79e5d32b140">	这种异步发送的机制能够比较好的兼容消息的安全性以及生产者的高吞吐需求，是很多MQ产品都支持的方式。RabbitMQ和Kafka都支持这种异步发送的机制。但是异步发送机制也并不是万能的，毕竟异步发送机制对消息生产者的主线业务是有侵入的。具体使用时还是需要根据业务场景考虑。</div><div class="notion-text notion-block-24c4c48a8a9e45e396617303be24a2fc">	RocketMQ提供的这三种发送消息的方式，并不存在绝对的好坏之分。我们更多的是需要根据业务场景进行选择。例如在电商下单这个场景，我们就应该尽量选择同步发送或异步发送，优先保证数据安全。然后，如果下单场景的并发比较高，业务比较繁忙，就应该尽量优先选择异步发送的机制。这时，我们就应该对下单服务的业务进行优化定制，尽量适应异步发送机制的要求。这样就可以尽量保证下单服务能够比较可靠的将用户的订单消息发送到RocketMQ了。</div><div class="notion-blank notion-block-0d2a5241d5a742c082fb2f0cb0f89181"> </div><div class="notion-text notion-block-9fe7105125a64184a604f69f66734d08"><b>2、消息消费者端采用状态确认机制保证消费者一定能正常处理对应的消息</b></div><div class="notion-text notion-block-4381120f4769419b85d03e1dd0b9a8b2">	我们之前分析生产者的可靠性问题，核心的解决思路就是通过确认Broker端的状态来保证生产者发送消息的可靠性。对于RocketMQ的消费者来说，保证消息处理可靠性的思路也是类似的。只不过这次换成了Broker等待消费者返回消息处理状态。</div><div class="notion-blank notion-block-b75945ff61fb4e48bf3904f731ed63e7"> </div><div class="notion-text notion-block-fc75c584ff514724b97603fff3ec42d3">这个返回值是一个枚举值，有两个选项 CONSUME_SUCCESS和RECONSUME_LATER。如果消费者返回CONSUME_SUCCESS，那么消息自然就处理结束了。但是如果消费者没有处理成功，返回的是RECONSUME_LATER，Broker就会过一段时间再发起消息重试。</div><div class="notion-text notion-block-9fb349ac6a89420da2921b30d98fba63">	为了要兼容重试机制的成功率和性能，RocketMQ设计了一套非常完善的消息重试机制，从而尽可能保证消费者能够正常处理用户的订单信息。</div><div class="notion-text notion-block-a58066f6c8c14cb8a36fb425b4f1af15">1、Broker不可能无限制的向消费失败的消费者推送消息。如果消费者一直没有恢复，Broker显然不可能一直无限制的推送，这会浪费集群很多的性能。所以，Broker会记录每一个消息的重试次数。如果一个消息经过很多次重试后，消费者依然无法正常处理，那么Broker会将这个消息推入到消费者组对应的死信Topic中。死信Topic相当于windows当中的垃圾桶。你可以人工介入对死信Topic中的消息进行补救，也可以直接彻底删除这些消息。RocketMQ默认的最大重试次数是16次。</div><div class="notion-text notion-block-bee06471902e45be913bf3891759b82f">	2、为了让这些重试的消息不会影响Topic下其他正常的消息，Broker会给每个消费者组设计对应的重试Topic。MessageQueue是一个具有严格FIFO特性的数据结构。如果需要重试的这些消息还是放在原来的MessageQueue中，就会对当前MessageQueue产生阻塞，让其他正常的消息无法处理。RocketMQ的做法是给每个消费者组自动生成一个对应的重试Topic。在消息需要重试时，会先移动到对应的重试Topic中。后续Broker只要从这些重试Topic中不断拿出消息，往消费者组重新推送即可。这样，这些重试的消息有了自己单独的队列，就不会影响到Topic下的其他消息了。</div><div class="notion-text notion-block-735760f39ee747bc9cc95e5e4700fb89">	3、RocketMQ中设定的消费者组都是订阅主题和消费逻辑相同的服务备份，所以当消息重试时，Broker只要往消费者组中随意一个实例推送即可。这是消息重试机制能够正常运行的基础。但是，在客户端的具体实现时，MQDefaultMQConsumer并没有强制规定消费者组不能重复。也就是说，你完全可以实现出一些订阅主题和消费逻辑完全不同的消费者服务，共同组成一个消费组。在这种情况下，RocketMQ不会报错，但是消息的处理逻辑就无法保持一致了。这会给业务带来很大的麻烦。这是在实际应用时需要注意的地方。</div><div class="notion-text notion-block-1af58aa5ee7d4d2e9afeb025b173b431">	4、Broker端最终只通过消费者组返回的状态来确定消息有没有处理成功。至于消费者组自己的业务执行是否正常，Broker端是没有办法知道的。因此，在实现消费者的业务逻辑时，应该要尽量使用同步实现方式，保证在自己业务处理完成之后再向Broker端返回状态。而应该尽量避免异步的方式处理业务逻辑。</div><div class="notion-blank notion-block-a1e5274c166343a19f36586b329b303b"> </div><div class="notion-text notion-block-ce9579477c4344f992ba354b851fb12d"><b>3、消费者也可以自行指定起始消费位点</b></div><div class="notion-text notion-block-536e23e7422644e7bba5e694da746a06">Broker端通过Consumer返回的状态来推进所属消费者组对应的Offset。但是，这里还是会造成一种分裂，消息最终是由Consumer来处理，但是消息却是由Broker推送过来的，也就是说，Consumer无法确定自己将要处理的是哪些消息。这就好比你上班做一天事情，公司负责给你发一笔工资。如果一切正常，那么没什么问题。 但是如果出问题了呢？公司拖欠了你的工资，这时，你就还是需要能到公司查账，至少查你自己的工资记录。从上一次发工资的时候计算你该拿的钱。</div><div class="notion-text notion-block-5cc4b3851b9640f39755b63513b9ab85">	对消息队列也一样。虽然Offset完全由Broker进行维护，但是，RocketMQ也允许Consumer自己去查账，自己指定消费位点。核心代码是在Consumer中设定了一个属性ConsumeFromWhere，表示在Consumer启动时，从哪一条消息开始进行消费。Consumer当然不可能精确的知道Offset的具体参数，所以这个ConsumerFromWhere并不是直接传入Offset位点，而是可以传入一个ConsumerFromWhere对象，这是一个枚举值。名字一目了然。</div><div class="notion-text notion-block-368a0181451d4a0d866d7fc461534e60">另外，如果指定了ConsumerFromWhere.CONSUME_FROM_TIMESTAMP，这就表示要从一个具体的时间开始。具体时间点，需要通过Consumer的另一个属性ConsumerTimestamp。这个属性可以传入一个表示时间的字符串。</div><div class="notion-blank notion-block-991fc0188f8a475cac84f84e3647596e"> </div><div class="notion-blank notion-block-135a328a430143e0a81a59c297f28d9d"> </div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-f8b7f570f5e7485281ada63ad3dbbbc0" data-id="f8b7f570f5e7485281ada63ad3dbbbc0"><span><div id="f8b7f570f5e7485281ada63ad3dbbbc0" class="notion-header-anchor"></div><a class="notion-hash-link" href="#f8b7f570f5e7485281ada63ad3dbbbc0" title="3、广播消息"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>3、广播消息</b></span></span></h3><div class="notion-text notion-block-4e52d0ad0bd0432f8a4df1aea05ce91f"><b>应用场景：</b></div><div class="notion-text notion-block-b8edf21453e44601bb2afe4a3587130e">	广播模式和集群模式是RocketMQ的消费者端处理消息最基本的两种模式。<span class="notion-red">集群模式下，一个消息，只会被一个消费者组中的多个消费者实例 共同 处理一次。广播模式下，一个消息，则会推送给所有消费者实例处理，不再关心消费者组。</span></div><div class="notion-text notion-block-055fb8b2c4164494a876cd0be3dee86b"><b>示例代码：</b></div><div class="notion-text notion-block-20e41e408f764dae98aad5b5c340fcf8">	消费者核心代码</div><div class="notion-blank notion-block-856c72c0f8fe480497328da64757cc94"> </div><div class="notion-text notion-block-5ac3cfd9c1634ed8b1229581b277476c">启动多个消费者，广播模式下，这些消费者都会消费一次消息。</div><div class="notion-text notion-block-a57a47c8bf204f7ab719e545948363e6"><b>实现思路：</b></div><div class="notion-text notion-block-4c71869ac72b4bd680f686f4a79e40ad">	默认模式(也就是集群模式)下，Broker端会给每个ConsumerGroup维护一个统一的Offset，这个Offset可以保证一个消息，在同一个ConsumerGroup内只会被消费一次。而广播模式的实现方式，是将Offset转移到消费者端自行保管，这样Broker端只管向所有消费者推送消息，而不用负责维护消费进度。</div><div class="notion-text notion-block-b4d8cd4fe7d24ae09c6ec2d97fb7891f"><b>注意点：</b></div><div class="notion-text notion-block-a0a3be102f7048be9c5e940817af2856">1、Broker端不维护消费进度，意味着，如果消费者处理消息失败了，将无法进行消息重试。</div><div class="notion-text notion-block-2baea3d63e744c77b0191fa686d4ba0e">2、消费者端维护Offset的作用是可以在服务重启时，按照上一次消费的进度，处理后面没有消费过的消息。丢了也不影响服务稳定性。</div><div class="notion-text notion-block-2e47ab8d651f4ea3a216045a4e1a4cd6">比如生产者发送了1~10号消息。消费者当消费到第6个时宕机了。当他重启时，Broker端已经把第10个消息都推送完成了。如果消费者端维护好了自己的Offset，那么他就可以在服务重启时，重新向Broker申请6号到10号的消息。但是，如果消费者端的Offset丢失了，消费者服务依然可以正常运行，但是6到10号消息就无法再申请了。后续这个消费者就只能获取10号以后的消息。</div><div class="notion-text notion-block-ea60bf20ba254cd189c3f2bb14e09d15">	实际上，Offset的维护数据是放在 {clientIp}{group}/offsets.json 文件下的。</div><div class="notion-text notion-block-cf56d16e19bf47d0b850838a237390a9">	消费者端存储广播消费的本地offsets文件的默认缓存目录是 System.getProperty(“user.home”) + File.separator + “.rocketmq_offsets” ，可以通过定制 rocketmq.client.localOffsetStoreDir 系统属性进行修改。</div><div class="notion-text notion-block-6ced143ddde64d08bf826eb597badb56">	本地offsets文件在缓存目录中的具体位置与消费者的clientIp 和 instanceName有关。其中instanceName默认是DEFAULT，可以通过定制系统属性 rocketmq.client.name 进行修改。另外，每个消费者对象也可以单独设定instanceName。</div><div class="notion-text notion-block-52414436202b48cbb20c2dbb0c7bcc0a">	RocketMQ会通过定时任务不断尝试本地Offsets文件的写入，但是，如果本地Offsets文件写入失败，RocketMQ不会进行任何的补救。</div><div class="notion-blank notion-block-2124856d1a2c47beb57a1901cebaf9af"> </div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-8afc1c1f2c424a38bd54f8fa5f3820a2" data-id="8afc1c1f2c424a38bd54f8fa5f3820a2"><span><div id="8afc1c1f2c424a38bd54f8fa5f3820a2" class="notion-header-anchor"></div><a class="notion-hash-link" href="#8afc1c1f2c424a38bd54f8fa5f3820a2" title="4、顺序消息机制"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>4、顺序消息机制</b></span></span></h3><div class="notion-text notion-block-69ac9c377ec34394bf54ab26a974b42f"><b>应用场景：</b></div><div class="notion-text notion-block-5315e737f6bb4c1cab514e67f12a8437">	每一个订单有从下单、锁库存、支付、下物流等几个业务步骤。每个业务步骤都由一个消息生产者通知给下游服务。如何保证对每个订单的业务处理顺序不乱？</div><div class="notion-text notion-block-b1f8716034044b03b274d5b6d6d19f4a"><b>示例代码：</b></div><div class="notion-text notion-block-2325ddff0fe943a2a71d82124846c956">	生产者核心代码：</div><div class="notion-text notion-block-69e56b999eb94b05a25204b459b35e52"><span class="notion-red">通过MessageSelector，将orderId相同的消息，都转发到同一个MessageQueue中。</span></div><div class="notion-text notion-block-ed9f9eb57a904142a764e911298739a6">消费者核心代码：</div><div class="notion-blank notion-block-0c78d79f7f814b56bf2d84c56c464a83"> </div><div class="notion-text notion-block-b9bce4796361431eb6faf252febc55cf">注入一个MessageListenerOrderly实现。</div><div class="notion-text notion-block-d99a10e1e8bc4f8dbb6672c093156f84"><b>实现思路：</b></div><div class="notion-text notion-block-904d8dedf11b491cad3f5ce8f681639c">	基础思路：只有放到一起的一批消息，才有可能保持消息的顺序。</div><div class="notion-blank notion-block-f1539702842d42318a2c6cdd901e4267"> </div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-4d57a646a7d5402386356640219a89c4"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F654145df-c7cb-42ca-b73f-a31009dc57e5%2Fac0907ab-2350-4bb2-b463-240be71dead4%2FUntitled.png?table=block&amp;id=4d57a646-a7d5-4023-8635-6640219a89c4" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-blank notion-block-de92b0a2b63d4c95b3f0bd135f768d99"> </div><div class="notion-text notion-block-4ab17a8d36924daebba5d9090c199e69">1、生产者只有将一批有顺序要求的消息，放到同一个MesasgeQueue上，Broker才有可能保持这一批消息的顺序。</div><div class="notion-text notion-block-f0a04207134b43b29b5303be1c3fba72">2、消费者只有一次锁定一个MessageQueue，拿到MessageQueue上所有的消息，</div><div class="notion-blank notion-block-3f584cd2b651437ca3bff43be0aa7e4b"> </div><div class="notion-text notion-block-787de3eee90e4187a621d19ff8707508"><b>注意点：</b></div><div class="notion-text notion-block-7f44e79a3bd34ae0811a40fc8c3c86cf">	1、理解局部有序与全局有序。大部分业务场景下，我们需要的其实是局部有序。如果要保持全局有序，那就只保留一个MessageQueue。性能显然非常低。</div><div class="notion-text notion-block-a780ae58c8ed42878e3780a849501751">	2、生产者端尽可能将有序消息打散到不同的MessageQueue上，避免过于几种导致数据热点竞争。</div><div class="notion-text notion-block-f9e8528407b745f4915b7d3ee7d2d789">	2、消费者端只能用同步的方式处理消息，不要使用异步处理。更不能自行使用批量处理。</div><div class="notion-text notion-block-b3cae46030314b3995f686f1b9a0ffd6">	3、消费者端只进行有限次数的重试。如果一条消息处理失败，RocketMQ会将后续消息阻塞住，让消费者进行重试。但是，如果消费者一直处理失败，超过最大重试次数，那么RocketMQ就会跳过这一条消息，处理后面的消息，这会造成消息乱序。</div><div class="notion-text notion-block-260a617d54be48c7bcb8463001dadc21">	4、消费者端如果确实处理逻辑中出现问题，不建议抛出异常，可以返回ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT作为替代。</div><div class="notion-blank notion-block-dc963e68dc4b40538de5f824e69b0ab7"> </div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-f7ba7ba47ec94f6faca9050ef16dc7a3" data-id="f7ba7ba47ec94f6faca9050ef16dc7a3"><span><div id="f7ba7ba47ec94f6faca9050ef16dc7a3" class="notion-header-anchor"></div><a class="notion-hash-link" href="#f7ba7ba47ec94f6faca9050ef16dc7a3" title="5、延迟消息"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>5、延迟消息</b></span></span></h3><div class="notion-text notion-block-f12c356a5682460687606c422af80372"><b>应用场景：</b></div><div class="notion-text notion-block-04953809f083420391b5689f4aa1ea63">延迟消息发送是指消息发送到Apache RocketMQ后，并不期望立马投递这条消息，而是延迟一定时间后才投递到Consumer进行消费。</div><blockquote class="notion-quote notion-block-b08835be022640d4b8d7bbfe21bfff1f"><div>虽然不太起眼，但是这是RocketMQ非常有特色的一个功能。对比下RabbitMQ和Kafka。RabbitMQ中只能通过使用死信队列变相实现延迟消息，或者加装一个插件来支持延迟消息。 Kafka则不太好实现延迟消息。</div></blockquote><div class="notion-text notion-block-edb783bd94fd4bdc94eca9cc1dff28e3"><b>示例代码：</b></div><div class="notion-text notion-block-790951b632864d728769a976d24ed143">	生产者端核心代码：</div><div class="notion-text notion-block-b8f8a7b548ac47e590101ac354db7701">只要给消息设定一个延迟级别就行了，无比简单。</div><div class="notion-text notion-block-8fdf5b7406c7411f852588a433201681">RocketMQ给消息定制了18个默认的延迟级别，分别对应18个不同的预设好的延迟时间。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-337b1eef22134b44bb42b9304342d7cf"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F654145df-c7cb-42ca-b73f-a31009dc57e5%2Fde9cbd8b-579b-42d4-8bee-87d1f95037de%2FUntitled.png?table=block&amp;id=337b1eef-2213-4b44-bb42-b9304342d7cf" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-a956091601854cf1a8eca5302875a856"><span class="notion-red">备注：商业版可以设置具体时间，并且最大时间也有1个星期以上</span></div><div class="notion-blank notion-block-81386408370b4dbc95ed231615e413e9"> </div><div class="notion-text notion-block-a0fc078c1a414efeaef8b50e11042115"><b>实现思路：</b></div><div class="notion-text notion-block-2d86e18bace34bf998f1bf6d358b5f43">延迟消息的难点其实是性能，需要不断进行定时轮训。全部扫描所有消息是不可能的，RocketMQ的实现方式是预设一个系统Topic，名字叫做<span class="notion-red">SCHEDULE_TOPIC_XXXX</span>。在这个Topic下，预设18个延迟队列。然后每次只针对这18个队列里的消息进行延迟操作，这样就不用一直扫描所有的消息了。</div><div class="notion-text notion-block-0fa10cbf15554a98ab1caf344418116f"><b>注意点：</b></div><div class="notion-text notion-block-35edc7a7899a484689c41b802d809e7b">这样预设延迟时间其实是不太灵活的。5.x版本已经支持预设一个具体的时间戳，按秒的精度进行定时发送。</div><div class="notion-text notion-block-d2277bda09354f24a0286dc0c1b0414d">但是可以看到，这18个延迟级别虽然无法调整，但是每个延迟级别对应的延迟时间其实是可以调整的。不过通常不建议这么做。</div><div class="notion-blank notion-block-b0fcf249d5b041b4b1125df9e6f071da"> </div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-6777ee8a64584e84adb76f523b08648e" data-id="6777ee8a64584e84adb76f523b08648e"><span><div id="6777ee8a64584e84adb76f523b08648e" class="notion-header-anchor"></div><a class="notion-hash-link" href="#6777ee8a64584e84adb76f523b08648e" title="6、批量消息"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>6、批量消息</b></span></span></h3><div class="notion-text notion-block-82ebbb2e96c948c891c4b196a2ccf71e"><b>应用场景：</b></div><div class="notion-text notion-block-2a17632ee2be42f38be7eae5defac08d">	生产者要发送的消息比较多时，可以将多条消息合并成一个批量消息，一次性发送出去。这样可以减少网络IO，提升消息发送的吞吐量。</div><div class="notion-text notion-block-66629735b0454f1eb84bda0f1e643b28"><b>示例代码：</b></div><div class="notion-text notion-block-84c343dc51de45969ee3a7762763b935">	生产者核心代码：</div><div class="notion-text notion-block-c744467f51c24268b65d3a0103bcfb7d"><b>注意点：</b></div><div class="notion-text notion-block-769d3fa1798442a5bd18be51c3d4cafd">批量消息的使用非常简单，但是要注意RocketMQ做了限制。同一批消息的Topic必须相同，另外，不支持延迟消息。</div><div class="notion-text notion-block-96272a56abff4f71ae402909a8981d6b">还有批量消息的大小不要超过1M，如果太大就需要自行分割。</div><div class="notion-blank notion-block-4db7b678f328434190473f3e74564af7"> </div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-ef0a0b144e0a4e41a4a07dd18ed11d92" data-id="ef0a0b144e0a4e41a4a07dd18ed11d92"><span><div id="ef0a0b144e0a4e41a4a07dd18ed11d92" class="notion-header-anchor"></div><a class="notion-hash-link" href="#ef0a0b144e0a4e41a4a07dd18ed11d92" title="7、过滤消息"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>7、过滤消息</b></span></span></h3><div class="notion-text notion-block-da598ab2b3de47b6a175cf6ce3f937cf"><b>应用场景：</b></div><div class="notion-text notion-block-e7e9e563678b42c7bdcef1ee5c4f830c">同一个Topic下有多种不同的消息，消费者只希望关注某一类消息。</div><div class="notion-text notion-block-da6ba5b85f30460b9381c06361a45cf0">例如，某系统中给仓储系统分配一个Topic，在Topic下，会传递过来入库、出库等不同的消息，仓储系统的不同业务消费者就需要过滤出自己感兴趣的消息，进行不同的业务操作。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-a8c5bac13674439bb2e45bd1f9e5afce"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F654145df-c7cb-42ca-b73f-a31009dc57e5%2F4e596c6c-8f72-4705-8119-e9335ea43b2e%2FUntitled.png?table=block&amp;id=a8c5bac1-3674-439b-b2e4-5bd1f9e5afce" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-9b4a9f8f11954f54bc5733af47be4fdb"><b>示例代码1：简单过滤</b></div><div class="notion-text notion-block-6b4576a34ee2413c938ee9d34f7bbed1">生产者端需要在发送消息时，增加Tag属性。比如我们上面举例当中的入库、出库。核心代码：</div><div class="notion-blank notion-block-daffea2e8f1d48fdb68fc51aa897ffcd"> </div><div class="notion-text notion-block-360dfae1dde64a98b9dad5a4d40c00f9">消费者端就可以通过这个Tag属性订阅自己感兴趣的内容。核心代码：</div><div class="notion-blank notion-block-1e017ae69d2047a5af4641c4da56c093"> </div><div class="notion-text notion-block-5133ce7994da4e5c9c5e3f8ca2592fa2">这样，后续Consumer就只会出处理TagA的消息。</div><div class="notion-blank notion-block-3b042ecaff5944b796e3a3f75b2c3a6f"> </div><div class="notion-text notion-block-732c50619dc649628a67ce9dbadcbe47"><b>示例代码2：SQL过滤</b></div><div class="notion-text notion-block-7da475a544d74ebcaddd502611922be2">通过Tag属性，只能进行简单的消息匹配。如果要进行更复杂的消息过滤，比如数字比较，模糊匹配等，就需要使用SQL过滤方式。SQL过滤方式可以通过Tag属性以及用户自定义的属性一起，以标准SQL的方式进行消息过滤。</div><div class="notion-text notion-block-1e08deab4c0e4059a3c17a390cd8ad2b">生产者端在发送消息时，出了Tag属性外，还可以增加自定义属性。核心代码：</div><div class="notion-text notion-block-1111bef347b84133aab32449835ddb1f">消费者端在进行过滤时，可以指定一个标准的SQL语句，定制复杂的过滤规则。核心代码：</div><div class="notion-text notion-block-204d4cec408344f08c508c161b79b647"><b>注意点：</b></div><div class="notion-text notion-block-25c770f1409a4d74b2b76c32673c4bbc">1、使用Tag过滤时，如果希望匹配多个Tag，可以使用两个竖线(||)连接多个Tag值。另外，也可以使用星号(*)匹配所有。</div><div class="notion-text notion-block-03ffdd47bfdf4347abae95dd7c6c1cde">2、使用SQL顾虑时，SQL语句是按照SQL92标准来执行的。SQL语句中支持一些常见的基本操作：</div><ul class="notion-list notion-list-disc notion-block-04aeec60a78245aeb34952bc8f78976b"><li>数值比较，比如：<b>&gt;，&gt;=，&lt;，&lt;=，BETWEEN，=；</b></li></ul><ul class="notion-list notion-list-disc notion-block-189b3746eeda4546a053def4aa8b4dd2"><li>字符比较，比如：<b>=，&lt;&gt;，IN；</b></li></ul><ul class="notion-list notion-list-disc notion-block-7ea1ae443ecb483fb958a7b3d4e6b731"><li><b>IS NULL</b> 或者 <b>IS NOT NULL；</b></li></ul><ul class="notion-list notion-list-disc notion-block-2fa074ec3e37472584500759f9575ba6"><li>逻辑符号 <b>AND，OR，NOT；</b></li></ul><div class="notion-text notion-block-411ad94e93284a6ebcf4fd2600d5fafb">2、消息过滤，其实在Broker端和在Consumer端都可以做。Consumer端也可以自行获取用户属性，不感兴趣的消息，直接返回不成功的状态，跳过该消息就行了。<span class="notion-red">但是RocketMQ会在Broker端完成过滤条件的判断，只将Consumer感兴趣的消息推送给Consumer</span>。这样的好处是减少了不必要的网络IO，但是缺点是加大了服务端的压力。不过在RocketMQ的良好设计下，更建议使用消息过滤机制。</div><div class="notion-text notion-block-90b1b37abb544551b8d8af7ac6812367">3、Consumer不感兴趣的消息并不表示直接丢弃。通常是需要在同一个消费者组，定制另外的消费者实例，消费那些剩下的消息。但是，如果一直没有另外的Consumer，那么，Broker端还是会推进Offset。</div><div class="notion-blank notion-block-4c161c16862444e0bcb5c33581ebad24"> </div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-d5125225d77d48b39ff970f124e3544c" data-id="d5125225d77d48b39ff970f124e3544c"><span><div id="d5125225d77d48b39ff970f124e3544c" class="notion-header-anchor"></div><a class="notion-hash-link" href="#d5125225d77d48b39ff970f124e3544c" title="8、事务消息"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>8、事务消息</b></span></span></h3><div class="notion-text notion-block-a45adebb6ab44ff9b38f90dd2463940d"><b>应用场景：</b></div><div class="notion-text notion-block-249c771438e84660ab99536933023a10">事务消息是RocketMQ非常有特色的一个高级功能。他的基础诉求是通过RocketMQ的事务机制，来保证上下游的数据一致性。</div><div class="notion-text notion-block-34c2afc1286243a2a54b1430ebfc9c8f">以电商为例，用户支付订单这一核心操作的同时会涉及到下游物流发货、积分变更、购物车状态清空等多个子系统的变更。这种场景，非常适合使用RocketMQ的解耦功能来进行串联。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-51ef790a1cc6422a90847f6294dc04f3"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F654145df-c7cb-42ca-b73f-a31009dc57e5%2Fb267be62-b1f3-4c52-95be-5b9a1d4ea0c9%2FUntitled.png?table=block&amp;id=51ef790a-1cc6-422a-9084-7f6294dc04f3" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-blank notion-block-45262b0bf7794b948950e888a09b55af"> </div><div class="notion-text notion-block-d6b4d44478164176b23b56f6775a3173">考虑到事务的安全性，即要保证相关联的这几个业务一定是同时成功或者同时失败的。如果要将四个服务一起作为一个分布式事务来控制，可以做到，但是会非常麻烦。而使用RocketMQ在中间串联了之后，事情可以得到一定程度的简化。由于RocketMQ与消费者端有失败重试机制，所以，只要消息成功发送到RocketMQ了，那么可以认为Branch2.1，Branch2.2，Branch2.3这几个分支步骤，是可以保证最终的数据一致性的。这样，一个复杂的分布式事务问题，就变成了MinBranch1和Branch2两个步骤的分布式事务问题。</div><div class="notion-text notion-block-08d9476421954830b8be9cc7ea74c9fe">然后，在此基础上，RocketMQ提出了事务消息机制，采用两阶段提交的思路，保证Main Branch1和Branch2之间的事务一致性。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-5bc86eda530d42679ec06554c36db495"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F654145df-c7cb-42ca-b73f-a31009dc57e5%2F81c7ca13-b3e7-4f92-90cf-3ca58ca60348%2FUntitled.png?table=block&amp;id=5bc86eda-530d-4267-9ec0-6554c36db495" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-blank notion-block-9ae856fa6e094579acbebb94eab2cf3d"> </div><div class="notion-text notion-block-dfa2128c33e449db81ae13fd3f49aa25">具体的实现思路是这样的：</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-24d083050ccb4b95816fdbe310104301"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F654145df-c7cb-42ca-b73f-a31009dc57e5%2F23c1b35e-e51b-4fcd-9d2e-22b529eb3385%2FUntitled.png?table=block&amp;id=24d08305-0ccb-4b95-816f-dbe310104301" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-blank notion-block-152eb3e4fff945c59e68b32bd3d57a54"> </div><ol start="1" class="notion-list notion-list-numbered notion-block-6eec8bdfd4124f639daf7af50841a3e1"><li>生产者将消息发送至RocketMQ服务端。</li></ol><ol start="2" class="notion-list notion-list-numbered notion-block-5cee7b2ac30548819d6c7bce2044fc97"><li>Apache RocketMQ服务端将消息持久化成功之后，向生产者返回Ack确认消息已经发送成功，此时消息被标记为&quot;暂不能投递&quot;，这种状态下的消息即为半事务消息。</li></ol><ol start="3" class="notion-list notion-list-numbered notion-block-921df1ea62294a6089799f87baf40fe6"><li>生产者开始执行本地事务逻辑。</li></ol><ol start="4" class="notion-list notion-list-numbered notion-block-6e17719f3407445bb455a08c7331fa91"><li>生产者根据本地事务执行结果向服务端提交二次确认结果（Commit或是Rollback），服务端收到确认结果后处理逻辑如下：</li><ol class="notion-list notion-list-numbered notion-block-6e17719f3407445bb455a08c7331fa91"><ul class="notion-list notion-list-disc notion-block-bd010b1d9e8649dabaa2a958bd3f7a98"><li>二次确认结果为Commit：服务端将半事务消息标记为可投递，并投递给消费者。</li></ul><ul class="notion-list notion-list-disc notion-block-a44794abc4604c34932df44d366178e5"><li>二次确认结果为Rollback：服务端将回滚事务，不会将半事务消息投递给消费者。</li></ul></ol></ol><div class="notion-text notion-block-75e805f4e34e449888412a4f3507ae14">5.在断网或者是生产者应用重启的特殊情况下，若服务端未收到发送者提交的二次确认结果，或服务端收到的二次确认结果为Unknown未知状态，经过固定时间后，服务端将对消息生产者即生产者集群中任一生产者实例发起消息回查。</div><div class="notion-text notion-block-29aaf43ff43a44e5b4e350094a5f93b7">6.生产者收到消息回查后，需要检查对应消息的本地事务执行的最终结果。</div><div class="notion-text notion-block-530e9a37c32c44d6879e5732033d11cc">7.生产者根据检查到的本地事务的最终状态再次提交二次确认，服务端仍按照步骤4对半事务消息进 行处理。</div><div class="notion-text notion-block-81ca965369434744b2d52fc11717055e"><b>示例代码：</b></div><blockquote class="notion-quote notion-block-f5173b2e67e04ef9b2347ec13e84b65e"><div>参见 org.apache.rocketmq.example.transaction.TransactionProducer</div></blockquote><div class="notion-text notion-block-059abb6cb2744be09050de04f391bb46">实现时的重点是使用RocketMQ提供的TransactionMQProducer事务生产者，在TransactionMQProducer中注入一个TransactionListener事务监听器来执行本地事务，以及后续对本地事务的检查。</div><div class="notion-text notion-block-32a486e3b112477bb226683c0491fb3a"><b>注意点：</b></div><div class="notion-text notion-block-74da0298860b464ea72feb4aad920750">1、半消息是对消费者不可见的一种消息。实际上，RocketMQ的做法是将消息转到了一个系统Topic，RMQ_SYS_TRANS_HALF_TOPIC。</div><div class="notion-text notion-block-6013c4b7e6e14720b121ef89090dd835">2、事务消息中，本地事务回查次数通过参数transactionCheckMax设定，默认15次。本地事务回查的间隔通过参数transactionCheckInterval设定，默认60秒。超过回查次数后，消息将会被丢弃。</div><div class="notion-text notion-block-0fc244151ad249c4a99aa88bad75f6a3">3、其实，了解了事务消息的机制后，在具体执行时，可以对事务流程进行适当的调整。</div><div class="notion-blank notion-block-f710041a08f246e982f78be82aaef2e4"> </div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-42c0ffa2e24e45388430355f633b48c6"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F654145df-c7cb-42ca-b73f-a31009dc57e5%2Fcc9f7446-3752-4e91-9fa4-923358402a54%2FUntitled.png?table=block&amp;id=42c0ffa2-e24e-4538-8430-355f633b48c6" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-blank notion-block-f5841cc90d2a41b79a365bb6aaebf668"> </div><div class="notion-blank notion-block-3be63b7decc74ae4b6b7c3f838421f60"> </div><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-afb30af1d50b44e4ae89d77adebfd28a" data-id="afb30af1d50b44e4ae89d77adebfd28a"><span><div id="afb30af1d50b44e4ae89d77adebfd28a" class="notion-header-anchor"></div><a class="notion-hash-link" href="#afb30af1d50b44e4ae89d77adebfd28a" title="三、SpringBoot整合RocketMQ"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>三、SpringBoot整合RocketMQ</b></span></span></h2><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-f23eb20fd5884dfcb894be62c8ad877f" data-id="f23eb20fd5884dfcb894be62c8ad877f"><span><div id="f23eb20fd5884dfcb894be62c8ad877f" class="notion-header-anchor"></div><a class="notion-hash-link" href="#f23eb20fd5884dfcb894be62c8ad877f" title="1、快速入门"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>1、快速入门</b></span></span></h3><div class="notion-text notion-block-412aeb5276e64d8dad5ff23ef951b888">按照SpringBoot三板斧，快速创建RocketMQ的客户端。创建Maven工程，引入关键依赖：</div><div class="notion-text notion-block-2ee36811bc214a6ab142bdaaea44a85e">使用SpringBoot集成时，要非常注意版本！！！</div><div class="notion-blank notion-block-d212c8a5b8c44facba5409062c5c9ba9"> </div><div class="notion-text notion-block-d7079243b3c94f989d162b51189ef5be">配置文件：</div><div class="notion-blank notion-block-7ddbf153abf8404dbf197959990799bb"> </div><div class="notion-text notion-block-b9323afa87554d74945804689c548d05">接下来就可以声明生产者，直接使用RocketMQTemplate进行消息发送。</div><div class="notion-text notion-block-8a8da7a97d7f448faf9f2e5086d4e134">另外，这个rocketMQTemplate不光可以发消息，还可以主动拉消息。</div><div class="notion-blank notion-block-2c9f8c472e1941459a767c1058df4d80"> </div><div class="notion-text notion-block-0f23693aabb34bf78527b57b878e4ce8">消费者的声明也很简单。所有属性通过@RocketMQMessageListener注解声明。</div><div class="notion-blank notion-block-76aa85cff4d6430b9b5d20c42a726f3b"> </div><div class="notion-text notion-block-caecb6640c0f40fdb2b30bce8f2f2420">这里唯一需要注意下的，就是消息了。SpringBoot框架中对消息的封装与原生API的消息封装是不一样的。</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-525806ca4fc44427b62fae02fc1edea7" data-id="525806ca4fc44427b62fae02fc1edea7"><span><div id="525806ca4fc44427b62fae02fc1edea7" class="notion-header-anchor"></div><a class="notion-hash-link" href="#525806ca4fc44427b62fae02fc1edea7" title="2、如何处理各种消息类型"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>2、如何处理各种消息类型</b></span></span></h3><div class="notion-text notion-block-2d4844f75f44421c99f97c2457f96a66">1、各种基础的消息发送机制参见单元测试类：com.roy.rocketmq.SpringRocketTest</div><div class="notion-text notion-block-acce2b559a684400a4dd6eb4b6a16402">2、一个RocketMQTemplate实例只能包含一个生产者，也就只能往一个Topic下发送消息。如果需要往另外一个Topic下发送消息，就需要通过@ExtRocketMQTemplateConfiguration()注解另外声明一个子类实例。</div><div class="notion-text notion-block-e9ec654999a047629fbdfefa0878fb4c">3、对于事务消息机制，最关键的事务监听器需要通过@RocketMQTransactionListener注解注入到Spring容器当中。在这个注解当中可以通过rocketMQTemplateBeanName属性，指向具体的RocketMQTemplate子类。</div><div class="notion-blank notion-block-1bebf3f09875417fbd7fad747b579900"> </div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-b2cba66ebf3a47e6a5a46bfd9984e33d" data-id="b2cba66ebf3a47e6a5a46bfd9984e33d"><span><div id="b2cba66ebf3a47e6a5a46bfd9984e33d" class="notion-header-anchor"></div><a class="notion-hash-link" href="#b2cba66ebf3a47e6a5a46bfd9984e33d" title="3、实现原理"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>3、实现原理</b></span></span></h3><div class="notion-text notion-block-e02b6b6e1eac469eb630ddff51c1fece"><b>1、Push模式</b></div><div class="notion-text notion-block-52b0dd4c8f8a4f0dac9189ed09d39f80">Push模式对于@RocketMQMessageListener注解的处理方式，入口在rocketmq-spring-boot-2.2.2.jar中的org.apache.rocketmq.spring.autoconfigure.ListenerContainerConfiguration类中。</div><div class="notion-text notion-block-fc53d4e8a8a04bde9c8acce944e1bb65">这个ListenerContainerConfiguration类继承了Spring当中的SmartInitializingSingleton接口，当Spring容器当中所有非懒加载的实例加载完成后，就会触发他的afterSingletonsInstantiated方法进行初始化。在这个方法中会去扫描所有带有注解@RocketMQMessageListener注解的类，将他注册到内部一个Container容器当中。</div><div class="notion-text notion-block-558a46280b2e4be88e56870c2d330b8a">这里这个Container可以认为是客户端实例的一个容器，通过这个容器来封装RocketMQ的原生API。</div><div class="notion-text notion-block-9894b89f8ed9439482cb2676d9ef577f">registerContainer的方法挺长的，这里截取出几行重要的源码：</div><div class="notion-text notion-block-fdd672baa7234460802af05dab0a4db9">这其中最关注的，当然是创建容器的createRocketMQListenerContainer方法中。而在这个方法中，你基本看不到RocketMQ的原生API，都是在创建并维护一个DefaultRocketMQListenerContainer对象。而这个DefaultRocketMQListenerContainer类，就是我们今天关注的重点。</div><div class="notion-text notion-block-c628047f54e04a9c94ee7fcf1458fc7e">DefaultRocketMQListenerContainer类实现了InitializingBean接口，自然要先关注他的afterPropertiesSet方法。这是Spring提供的对象初始化的扩展机制。</div><div class="notion-text notion-block-cf5bef543477479bae795935b67736bc">这个方法就是用来初始化RocketMQ消费者的。在这个方法里就会创建一个RocketMQ原生的DefaultMQPushConsumer消费者。同样，方法很长，抽取出比较关注的重点源码。</div><div class="notion-blank notion-block-a2f9266f4ddc412abae05941ac275f0c"> </div><div class="notion-text notion-block-22c4af79c57a4f398b91a1a3449b3f0d"><b>2、Pull模式</b></div><div class="notion-text notion-block-bd8cece324da48fea9145e3c58f154fc">Pull模式的实现其实是通过在RocketMQTemplate实例中注入一个DefaultLitePullConsumer实例来实现的。只要注入并启动了这个DefaultLitePullConsumer示例后，后续就可以通过template实例的receive方法，来调用DefaultLitePullConsumer的poll方法，主动去Pull获取消息了。</div><div class="notion-text notion-block-5f677130a179479cb49d0bcab6a763a0">初始化DefaultLitePullConsumer的代码依然是在rocketmq-spring-boot-2.2.2.jar包中。不过处理类是org.apache.rocketmq.spring.autoconfigure.RocketMQAutoConfiguration。这个配置类会配置在jar包中的spring.factories文件中，通过SpringBoot的自动装载机制加载进来。</div><div class="notion-blank notion-block-611cb1dd450c4e03a09dc48d2fd0f126"> </div><div class="notion-text notion-block-dca9940274ac4f23868eb4af77bf7e16">RocketMQUtil.createDefaultLitePullConsumer方法中，就是在维护一个DefaultLitePullConsumer实例。这个实例就是RocketMQ的原生API当中提供的拉模式客户端。</div><div class="notion-blank notion-block-31cfc7a7e4a1460995a854bd763c8fd9"> </div><div class="notion-text notion-block-fff5335a1b15445e98748d4ac180e472"><span class="notion-red">实际开发中，拉模式用得比较少</span>。但是，其实RocketMQ针对拉模式也做了非常多的优化。原本提供了一个DefaultMQPullConsumer类，进行拉模式消息消费，DefaultLitePullConsumer在此基础上做了很多优化。有兴趣可以自己研究一下。</div><div class="notion-blank notion-block-c5c3759b77b747c09c91716992349aae"> </div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-c436e6d237a9451c81a22c3b25f22c45" data-id="c436e6d237a9451c81a22c3b25f22c45"><span><div id="c436e6d237a9451c81a22c3b25f22c45" class="notion-header-anchor"></div><a class="notion-hash-link" href="#c436e6d237a9451c81a22c3b25f22c45" title="四、RocketMQ最佳实践"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>四、RocketMQ最佳实践</b></span></span></h3><div class="notion-text notion-block-87cd60c7e1274bdda6c0abbabfea5a18"><b>1、合理分配Topic、Tag</b></div><div class="notion-text notion-block-864b089d236b415b9fdbe4c5801ad148">一个应用尽可能用一个Topic，而消息子类型则可以用tags来标识。tags可以由应用自由设置，只有生产者在发送消息设置了tags，消费方在订阅消息时才可以利用tags通过broker做消息过滤：message.setTags(&quot;TagA&quot;)。</div><blockquote class="notion-quote notion-block-28695c5d7c4d4dd086cab8a118072149"><div>Kafka的一大问题是Topic过多，会造成Partition文件过多，影响性能。而RocketMQ中的Topic完全不会对消息转发性能有影响。但是Topic过多，还是会加大RocketMQ的元数据维护的性能消耗。所以，在使用时，还是需要对Topic进行合理的分配。</div><div class="notion-text notion-block-28c4ab197c1d43c085119433766d77c7">使用Tag区分消息时，尽量直接使用Tag过滤，不要使用复杂的SQL过滤。因为消息过滤机制虽然可以减少网络IO，但是毕竟会加大Broker端的消息处理压力。所以，消息过滤的逻辑，还是越简单越好。</div></blockquote><div class="notion-text notion-block-b3e5403eb5fb4e1da1da1b816e4ca24d"><b>2、使用Key加快消息索引</b></div><div class="notion-text notion-block-4999dfba42254262a75500cb1653c5c7">分配好Topic和Tag之后，自然就需要优化Key属性了，因为Key也可以参与消息过滤。通常建议每个消息要分配一个在业务层面的唯一标识码，设置到Key属性中。这有两个方面的作用：</div><div class="notion-text notion-block-92c39157315b403fa7cdcc7b2570c046">一是可以配合Tag进行更精确的消息过滤。</div><div class="notion-text notion-block-a5bc9ca3dfc141259264298d1301851b">另一个更重要的方面是，RocketMQ的Broker端会为每个消息创建一个哈希索引。应用可以通过topic、key来查询某一条历史的消息内容，以及消息在集群内的处理情况。在管理控制台就可以看到。为了减少哈希索引潜在的哈希冲突问题，所有官方建议，客户端要尽量保证key的唯一性。</div><div class="notion-blank notion-block-7954fc8ae93848f195e32ef15451f602"> </div><div class="notion-text notion-block-0f9e8865d5cf429089cc747dd03d30ac"><b>3、关注错误消息重试</b>
<code class="notion-inline-code">我们已经知道RocketMQ的消费者端，如果处理消息失败了，Broker是会将消息重新进行投送的。而在重试时，RocketMQ实际上会为每个消费者组创建一个对应的重试队列。重试的消息会进入一个 “%RETRY%”+ConsumeGroup  的队列中。</code></div><div class="notion-text notion-block-1ec60f2c43074e40afef279992c4c2f8">多关注重试队列，可以及时了解消费者端的运行情况。这个队列中出现了大量的消息，就意味着消费者的运行出现了问题，要及时跟踪进行干预。</div><div class="notion-text notion-block-eab3e976de874ff5aefec7256800994a">然后RocketMQ默认允许每条消息最多重试16次，每次重试的间隔时间如下：</div><table class="notion-simple-table notion-block-d579ddbb99b9427e8ebb132e392cf10a"><tbody><tr class="notion-simple-table-row notion-block-eed88c085a2346a99d0a678374230702"><td class="" style="width:120px"><div class="notion-simple-table-cell">重试次数</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">与上次重试的间隔时间</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">重试次数</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">与上次重试的间隔时间</div></td></tr><tr class="notion-simple-table-row notion-block-ec7277c88a6b4a08bd14c8028d7d8198"><td class="" style="width:120px"><div class="notion-simple-table-cell">1</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">10 秒</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">9</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">7 分钟</div></td></tr><tr class="notion-simple-table-row notion-block-e20e54d3323e4239a1350b099e0f9faf"><td class="" style="width:120px"><div class="notion-simple-table-cell">2</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">30 秒</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">10</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">8 分钟</div></td></tr><tr class="notion-simple-table-row notion-block-87977aa0abb44562a68b366a0764cf37"><td class="" style="width:120px"><div class="notion-simple-table-cell">3</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">1 分钟</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">11</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">9 分钟</div></td></tr><tr class="notion-simple-table-row notion-block-06417bd789b4494589d28029f0f83fa2"><td class="" style="width:120px"><div class="notion-simple-table-cell">4</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">2 分钟</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">12</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">10 分钟</div></td></tr><tr class="notion-simple-table-row notion-block-74fbb680113e41478fad30491444f395"><td class="" style="width:120px"><div class="notion-simple-table-cell">5</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">3 分钟</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">13</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">20 分钟</div></td></tr><tr class="notion-simple-table-row notion-block-ea0cd37cc3f04c159862bb55d8022a56"><td class="" style="width:120px"><div class="notion-simple-table-cell">6</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">4 分钟</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">14</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">30 分钟</div></td></tr><tr class="notion-simple-table-row notion-block-2a36ab13ba774547883b7e4df3591184"><td class="" style="width:120px"><div class="notion-simple-table-cell">7</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">5 分钟</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">15</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">1 小时</div></td></tr><tr class="notion-simple-table-row notion-block-a373568ace42475f8f2cb746fae76827"><td class="" style="width:120px"><div class="notion-simple-table-cell">8</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">6 分钟</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">16</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">2 小时</div></td></tr></tbody></table><blockquote class="notion-quote notion-block-35226b7292374d7ca466c5daade5cca0"><div>这个重试时间跟延迟消息的延迟级别是对应的。不过取的是延迟级别的后16级别。</div><div class="notion-text notion-block-ece5644858b44c939d14ca22e9adb2a6">messageDelayLevel=1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h</div><div class="notion-text notion-block-811c1305091d4f909e79b5f51d84e62b">这个重试时间可以将源码中的org.apache.rocketmq.example.quickstart.Consumer里的消息监听器返回状态改为RECONSUME_LATER测试一下。</div></blockquote><div class="notion-text notion-block-0e68ad6d9b3f49bbb0af782525a7e1cf"><b>重试次数：</b></div><div class="notion-text notion-block-c3c6b5cf47b04f0d909e6501b1e50743">如果消息重试16次后仍然失败，消息将不再投递。转为进入死信队列。</div><div class="notion-text notion-block-6dc403c9008246a78c4a90f66a45f324">然后关于这个重试次数，RocketMQ可以进行定制。例如通过consumer.setMaxReconsumeTimes(20);将重试次数设定为20次。当定制的重试次数超过16次后，消息的重试时间间隔均为2小时。</div><div class="notion-text notion-block-853893d18675470b8fa67b3ae4a9f331"><b>配置覆盖：</b></div><div class="notion-text notion-block-b2474f6b9a6b40388f48c20a06bfbb41">消息最大重试次数的设置对相同GroupID下的所有Consumer实例有效。并且最后启动的Consumer会覆盖之前启动的Consumer的配置。</div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-14f0d82680dc45988f373796723b002d" data-id="14f0d82680dc45988f373796723b002d"><span><div id="14f0d82680dc45988f373796723b002d" class="notion-header-anchor"></div><a class="notion-hash-link" href="#14f0d82680dc45988f373796723b002d" title="4、手动处理死信队列"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>4、手动处理死信队列</b></span></span></h3><div class="notion-text notion-block-ed0b071f188943f3b3f476756cf6d00c">当一条消息消费失败，RocketMQ就会自动进行消息重试。而如果消息超过最大重试次数，RocketMQ就会认为这个消息有问题。但是此时，RocketMQ不会立刻将这个有问题的消息丢弃，而会将其发送到这个消费者组对应的一种特殊队列：死信队列。</div><div class="notion-text notion-block-df9ca5d6644d436abee9d7f2ff76d1c1">通常，一条消息进入了死信队列，意味着消息在消费处理的过程中出现了比较严重的错误，并且无法自行恢复。此时，一般需要人工去查看死信队列中的消息，对错误原因进行排查。然后对死信消息进行处理，比如转发到正常的Topic重新进行消费，或者丢弃。</div><div class="notion-text notion-block-134413dc957a4fea941a09baed3821b1">死信队列的名称是%DLQ%+ConsumGroup</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-5788b7c264f14c78b0d954a1a7f45957"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F654145df-c7cb-42ca-b73f-a31009dc57e5%2F6b3e3380-e27b-476c-95e1-023092902582%2FUntitled.png?table=block&amp;id=5788b7c2-64f1-4c78-b0d9-54a1a7f45957" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-f6fff2326ed846a4857fc8cc7e6eac90"><b>死信队列的特征：</b></div><ul class="notion-list notion-list-disc notion-block-541f22a599bf4839b59f316b3c227853"><li>一个死信队列对应一个ConsumGroup，而不是对应某个消费者实例。</li></ul><ul class="notion-list notion-list-disc notion-block-52cd2be3e9b5424f950c698f73f433b4"><li>如果一个ConsumeGroup没有产生死信队列，RocketMQ就不会为其创建相应的死信队列。</li></ul><ul class="notion-list notion-list-disc notion-block-4974cc7de60c4364a7aee2a2543650e0"><li>一个死信队列包含了这个ConsumeGroup里的所有死信消息，而不区分该消息属于哪个Topic。</li></ul><ul class="notion-list notion-list-disc notion-block-fa6978f6594d4f86adf5081027ed62c0"><li>死信队列中的消息不会再被消费者正常消费。</li></ul><ul class="notion-list notion-list-disc notion-block-57c2f529d69f492ab321f66cb3a07bea"><li>死信队列的有效期跟正常消息相同。默认3天，对应broker.conf中的fileReservedTime属性。超过这个最长时间的消息都会被删除，而不管消息是否消费过。</li></ul><blockquote class="notion-quote notion-block-d07cce5055f74414bd637385d43dc933"><div>注：默认创建出来的死信队列，他里面的消息是无法读取的，在控制台和消费者中都无法读取。这是因为这些默认的死信队列，他们的权限perm被设置成了2:禁读(这个权限有三种 2:禁读，4:禁写,6:可读可写)。需要手动将死信队列的权限配置成6，才能被消费(可以通过mqadmin指定或者web控制台)。</div></blockquote><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-213bdb4368c54fe2a1e7a0b30e71b7a6" data-id="213bdb4368c54fe2a1e7a0b30e71b7a6"><span><div id="213bdb4368c54fe2a1e7a0b30e71b7a6" class="notion-header-anchor"></div><a class="notion-hash-link" href="#213bdb4368c54fe2a1e7a0b30e71b7a6" title="5、消费者端进行幂等控制"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>5、消费者端进行幂等控制</b></span></span></h3><div class="notion-text notion-block-f8f47dfafb7442ea937e58cba2ed0a83">在MQ系统中，对于消息幂等有三种实现语义：</div><ul class="notion-list notion-list-disc notion-block-a804f6594c854587b2c246d9a941d5fc"><li>at most once 最多一次：每条消息最多只会被消费一次</li></ul><ul class="notion-list notion-list-disc notion-block-05202a3778ce438996219ec905752c39"><li>at least once 至少一次：每条消息至少会被消费一次</li></ul><ul class="notion-list notion-list-disc notion-block-62ea38645caa466182e086ccb62bf4cd"><li>exactly once 刚刚好一次：每条消息都只会确定的消费一次</li></ul><div class="notion-text notion-block-7a632de1a24047609d99754b59995c66">这三种语义都有他适用的业务场景。</div><div class="notion-text notion-block-e3749ac70e0b4e36ade6c345b67726d2">其中，at most once是最好保证的。RocketMQ中可以直接用异步发送、sendOneWay等方式就可以保证。</div><div class="notion-text notion-block-058356956c4c492ab076647751c86a2c">而at least once这个语义，RocketMQ也有同步发送、事务消息等很多方式能够保证。</div><div class="notion-text notion-block-63db4398753c42bcbcdaa7decbcc629c">而这个exactly once是MQ中最理想也是最难保证的一种语义，需要有非常精细的设计才行。RocketMQ只能保证at least once，保证不了exactly once。所以，使用RocketMQ时，<span class="notion-red">需要由业务系统自行保证消息的幂等性。</span></div><div class="notion-text notion-block-cf57a713cc934d3abdfdfa30704695df"><b>消息幂等的必要性</b></div><div class="notion-text notion-block-2063c6dc672649088b846505447a96d2">在互联网应用中，尤其在网络不稳定的情况下，消息队列 RocketMQ 的消息有可能会出现重复，这个重复简单可以概括为以下情况：</div><ul class="notion-list notion-list-disc notion-block-462871a418a14cef89ed57ce0e05c21a"><li>发送时消息重复</li><ul class="notion-list notion-list-disc notion-block-462871a418a14cef89ed57ce0e05c21a"><div class="notion-text notion-block-a4553b33dac84fd48508a906fea0e2c6">当一条消息已被成功发送到服务端并完成持久化，此时出现了网络闪断或者客户端宕机，导致服务端对客户端应答失败。 如果此时生产者意识到消息发送失败并尝试再次发送消息，消费者后续会收到两条内容相同并且 Message ID 也相同的消息。</div></ul></ul><ul class="notion-list notion-list-disc notion-block-2110e84dba214abe8ac4665388c2b312"><li>投递时消息重复</li><ul class="notion-list notion-list-disc notion-block-2110e84dba214abe8ac4665388c2b312"><div class="notion-text notion-block-efa1d43b8cd34ff09a2c01b22c72d3b4">消息消费的场景下，消息已投递到消费者并完成业务处理，当客户端给服务端反馈应答的时候网络闪断。 为了保证消息至少被消费一次，消息队列 RocketMQ 的服务端将在网络恢复后再次尝试投递之前已被处理过的消息，消费者后续会收到两条内容相同并且 Message ID 也相同的消息。</div></ul></ul><ul class="notion-list notion-list-disc notion-block-2744c7b3e3d245beb51701a1eb301c79"><li>负载均衡时消息重复（包括但不限于网络抖动、Broker 重启以及订阅方应用重启）</li><ul class="notion-list notion-list-disc notion-block-2744c7b3e3d245beb51701a1eb301c79"><div class="notion-text notion-block-0ce3cbe7aad1431e972f120dcad38d7c">当消息队列 RocketMQ 的 Broker 或客户端重启、扩容或缩容时，会触发 Rebalance，此时消费者可能会收到重复消息。</div></ul></ul><div class="notion-text notion-block-7adebe09045643d7b5385fe8fdffda71"><b>处理方式</b></div><div class="notion-text notion-block-347b36e38593411d8d1f3223a4d72d6b">从上面的分析中，我们知道，在RocketMQ中，是无法保证每个消息只被投递一次的，所以要在业务上自行来保证消息消费的幂等性。</div><div class="notion-text notion-block-24bcd44ed010400a8aec31b836aa60d6">而要处理这个问题，RocketMQ的每条消息都有一个唯一的MessageId，这个参数在多次投递的过程中是不会改变的，所以业务上可以用这个MessageId来作为判断幂等的关键依据。</div><div class="notion-text notion-block-71f7d46933a649889ba1e04084b71658">但是，这个MessageId是无法保证全局唯一的，也会有冲突的情况。所以在一些对幂等性要求严格的场景，最好是使用业务上唯一的一个标识比较靠谱。例如订单ID。而这个业务标识可以使用Message的Key来进行传递。</div><div class="notion-blank notion-block-5fb5168c91b04e7d8fd52c4885047c36"> </div><div class="notion-text notion-block-2c5522b7cc624764a707f1764f673db4">致谢：</div><div class="notion-callout notion-gray_background_co notion-block-c255417788c546f0972f76d560d7a239"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="💡">💡</span></div><div class="notion-callout-text">有关Notion安装或者使用上的问题，欢迎您在底部评论区留言，一起交流~</div></div><div class="notion-blank notion-block-36c4a62fbd4f4b86be6cabb91e019a85"> </div><div class="notion-blank notion-block-471886a3aa8142f2a30d026aa832af27"> </div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[rocketMq 快速实战和集群架构原理详解]]></title>
            <link>https://blog.juanshen.eu.org/article/126029c1-d761-469c-a4a6-1648984b6bae</link>
            <guid>https://blog.juanshen.eu.org/article/126029c1-d761-469c-a4a6-1648984b6bae</guid>
            <pubDate>Sun, 23 Jul 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[rocketMq 认识]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-126029c1d761469ca4a61648984b6bae"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><blockquote class="notion-quote notion-block-4713047b45a34870b4dc2078c938f9b6"><div>文章来源说明</div></blockquote><h2 class="notion-h notion-h1 notion-h-indent-0 notion-block-ed64423632a24473aaed1d229226c625" data-id="ed64423632a24473aaed1d229226c625"><span><div id="ed64423632a24473aaed1d229226c625" class="notion-header-anchor"></div><a class="notion-hash-link" href="#ed64423632a24473aaed1d229226c625" title="一、MQ简介"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b><b>一、MQ简介</b></b></span></span></h2><div class="notion-text notion-block-bf3994901c7640b58d93e332333c2fcf">MQ：MessageQueue，消息队列。是在互联网中使用非常广泛的一系列服务中间件。 这个词可以分两个部分来看，一是Message：消息。消息是在不同进程之间传递的数据。这些进程可以部署在同一台机器上，也可以分布在不同机器上。二是Queue：队列。队列原意是指一种具有FIFO(先进先出)特性的数据结构，是用来缓存数据的。对于消息中间件产品来说，能不能保证FIFO特性，尚值得考量。但是，所有消息队列都是需要具备存储消息，让消息排队的能力。</div><div class="notion-text notion-block-ce8bda8051894670a075e2c9655b529b"> 广义上来说，只要能够实现消息跨进程传输以及队列数据缓存，就可以称之为消息队列。例如我们常用的QQ、微信、阿里旺旺等就都具备了这样的功能。只不过他们对接的使用对象是人，而我们这里讨论的MQ产品需要对接的使用对象是应用程序。</div><div class="notion-text notion-block-8436829e8ea248f1967bb782bf49bcaf">MQ的作用主要有以下三个方面：</div><ul class="notion-list notion-list-disc notion-block-4ee9c9dfe3c9405aba04ad748e9a894b"><li>异步</li><ul class="notion-list notion-list-disc notion-block-4ee9c9dfe3c9405aba04ad748e9a894b"><div class="notion-text notion-block-5ea3bdefdd594640bfce416b5cf9f2cd">例子：快递员发快递，直接到客户家效率会很低。引入菜鸟驿站后，快递员只需要把快递放到菜鸟驿站，就可以继续发其他快递去了。客户再按自己的时间安排去菜鸟驿站取快递。</div><div class="notion-text notion-block-2f3896108ab845b8b1a111e76b405953">作用：异步能提高系统的响应速度、吞吐量。</div></ul></ul><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-48787ace17fa4ce5ad3988ab31309c03"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:447px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F654145df-c7cb-42ca-b73f-a31009dc57e5%2Fd6dd7680-a92f-4412-9221-d4e371fbafaf%2FUntitled.png?table=block&amp;id=48787ace-17fa-4ce5-ad39-88ab31309c03" alt="notion image" loading="lazy" decoding="async"/></div></figure><ul class="notion-list notion-list-disc notion-block-b766cc0f454f436ab125fe2b88fd5045"><li>解耦</li><ul class="notion-list notion-list-disc notion-block-b766cc0f454f436ab125fe2b88fd5045"><div class="notion-text notion-block-19430c7f9c564074affd1d818693c07c">例子：《Thinking in JAVA》很经典，但是都是英文，我们看不懂，所以需要编辑社，将文章翻译成其他语言，这样就可以完成英语与其他语言的交流。</div><div class="notion-text notion-block-d3fd601035d64570bade9abe2d52004d">作用：</div><div class="notion-text notion-block-8a471e8a0dae4d13a9599f201e554da0">1、服务之间进行解耦，才可以减少服务之间的影响。提高系统整体的稳定性以及可扩展性。</div><div class="notion-text notion-block-255ded680d204b86a48d3f8fe7006f35">2、另外，解耦后可以实现数据分发。生产者发送一个消息后，可以由一个或者多个消费者进行消费，并且消费者的增加或者减少对生产者没有影响。</div></ul></ul><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-0a840f2a1c12416b9fb4b88b66a035eb"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:418px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F654145df-c7cb-42ca-b73f-a31009dc57e5%2F7d119656-dd17-4bda-b208-c7490be2f1b1%2FUntitled.png?table=block&amp;id=0a840f2a-1c12-416b-9fb4-b88b66a035eb" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-blank notion-block-9095852222b64802b0c4e4bf8212cec9"> </div><ul class="notion-list notion-list-disc notion-block-5ad01344a01c4ee18498b10b3071454d"><li>削峰</li><ul class="notion-list notion-list-disc notion-block-5ad01344a01c4ee18498b10b3071454d"><div class="notion-text notion-block-dee463dd82174640b5cfecdb332c060a">例子：长江每年都会涨水，但是下游出水口的速度是基本稳定的，所以会涨水。引入三峡大坝后，可以把水储存起来，下游慢慢排水。</div><div class="notion-text notion-block-97b745d74caf4b35924d4052478cf0ae">作用：以稳定的系统资源应对突发的流量冲击。</div></ul></ul><div class="notion-blank notion-block-bf8a6a3581a042489a01a71b71c3764b"> </div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-1e2648c628b44ff1b3afcbc7c32424d3"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:307px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F654145df-c7cb-42ca-b73f-a31009dc57e5%2Fe9bc47d4-bc36-4f91-b91e-1cdd6cef1894%2FUntitled.png?table=block&amp;id=1e2648c6-28b4-4ff1-b3af-cbc7c32424d3" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-blank notion-block-1752862b5966494a82cb27df6d204838"> </div><div class="notion-blank notion-block-246bcc2dc7204273a8361da584d99504"> </div><div class="notion-text notion-block-df1422f69bcc481a9127becc220399b5"><b><b>二、RocketMQ产品特点</b></b></div><h3 class="notion-h notion-h2 notion-h-indent-1 notion-block-926e75a15df34e4e910dbc19221c95e3" data-id="926e75a15df34e4e910dbc19221c95e3"><span><div id="926e75a15df34e4e910dbc19221c95e3" class="notion-header-anchor"></div><a class="notion-hash-link" href="#926e75a15df34e4e910dbc19221c95e3" title="1、RocketMQ介绍"><svg viewBox="0 0 16 16" width="16" height="16"><path fill-rule="evenodd" d="M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z"></path></svg></a><span class="notion-h-title"><b>1、RocketMQ介绍</b></span></span></h3><div class="notion-text notion-block-b89ed1ac3715462ea5424171eda0d2a9"> RocketMQ是阿里巴巴开源的一个消息中间件，在阿里内部历经了双十一等很多高并发场景的考验，能够处理亿万级别的消息。2016年开源后捐赠给Apache，现在是Apache的一个顶级项目。</div><div class="notion-text notion-block-e781a4b94bf243ca97b36e4a445a94c4"> 早期阿里使用ActiveMQ，但是，当消息开始逐渐增多后，ActiveMQ的IO性能很快达到了瓶颈。于是，阿里开始关注Kafka。但是Kafka是针对日志收集场景设计的，他的高级功能并不是很贴合阿里的业务场景。尤其当他的Topic过多时，由于Partition文件也会过多，这就会加大文件索引的耗时，会严重影响IO性能。于是阿里才决定自研中间件，最早叫做MetaQ，后来改名成为RocketMQ。最早他所希望解决的最大问题就是多Topic下的IO性能压力。但是产品在阿里内部的不断改进，RocketMQ开始体现出一些不一样的优势。</div><div class="notion-text notion-block-7a326ce1741b42d0898537281a5ef821"><b><b>2、RocketMQ特点</b></b></div><div class="notion-text notion-block-77ca51cb98f842429b50a63520a31dbe"> 当今互联网MQ产品众多，其中，影响力和使用范围最大的当数Apache Kafka、RabbitMQ、Apache RocketMQ以及Apache Plusar。这几大产品虽然都是典型的MQ产品，但是由于设计和实现上的一些差异，造成他们适合于不同的细分场景。</div><table class="notion-simple-table notion-block-70e48ba8ea164b0393117c6f5d69d3e4"><tbody><tr class="notion-simple-table-row notion-block-376828f51f6d44ac8b5e32e453a68d5b"><td class="" style="width:120px"><div class="notion-simple-table-cell"><b>优点</b></div></td><td class="" style="width:120px"><div class="notion-simple-table-cell"><b>缺点</b></div></td><td class="" style="width:120px"><div class="notion-simple-table-cell"><b>适合场景</b></div></td></tr><tr class="notion-simple-table-row notion-block-42bc425a4e7f4d09ba64d79db02f0688"><td class="" style="width:120px"><div class="notion-simple-table-cell">Apache Kafka</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">吞吐量非常大，性能非常好，集群高可用。</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">会有丢数据的可能，功能比较单一</div></td></tr><tr class="notion-simple-table-row notion-block-b0e6b5a5b76840fd96d71557de0d6054"><td class="" style="width:120px"><div class="notion-simple-table-cell">RabbitMQ</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">消息可靠性高，功能全面。</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">erlang语言不好定制。吞吐量比较低。</div></td></tr><tr class="notion-simple-table-row notion-block-7b749e45e8e24bb59fb323246e93c2aa"><td class="" style="width:120px"><div class="notion-simple-table-cell">Apache Pulsar</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">基于Bookeeper构建，消息可靠性非常高。</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">周边生态还有差距，目前使用的公司比较少。</div></td></tr><tr class="notion-simple-table-row notion-block-119e2d9bf14a4b7b93260536e88e1f6d"><td class="" style="width:120px"><div class="notion-simple-table-cell">Apache RocketMQ</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">高吞吐、高性能、高可用。功能全面。客户端协议丰富。使用java语言开发，方便定制。</div></td><td class="" style="width:120px"><div class="notion-simple-table-cell">服务加载比较慢。</div></td></tr></tbody></table><div class="notion-blank notion-block-698f46e79f4247bd86351cd254cdd2e6"> </div><div class="notion-text notion-block-2bf85e39f07f48ab9800808d418a11eb">其中RocketMQ，孵化自阿里巴巴。历经阿里多年双十一的严格考验，RocketMQ可以说是从全世界最严苛的高并发场景中摸爬滚打出来的过硬产品，也是少数几个在金融场景比较适用的MQ产品。从横向对比来看，RocketMQ与Kafka和RabbitMQ相比。RocketMQ的消息吞吐量虽然和Kafka相比还是稍有差距，但是却比RabbitMQ高很多。在阿里内部，RocketMQ集群每天处理的请求数超过5万亿次，支持的核心应用超过3000个。而RocketMQ最大的优势就是他天生就为金融互联网而生。他的消息可靠性相比Kafka也有了很大的提升，而消息吞吐量相比RabbitMQ也有很大的提升。另外，RocketMQ的高级功能也越来越全面，广播消费、延迟队列、死信队列等等高级功能一应俱全，甚至某些业务功能比如事务消息，已经呈现出领先潮流的趋势。</div><div class="notion-blank notion-block-5470add66951472da2af862fcb4e7494"> </div><div class="notion-text notion-block-85ea35a99cb646ccbf52b13b17a540b4"><b><b>三、RocketMQ快速实战</b></b></div><div class="notion-text notion-block-ad938079e3424e22a9775426804aabc5">搭建</div><div class="notion-text notion-block-8ae5bc7d3f414fcb9ba9d13686d70da6"> RocketMQ的官网地址： <a target="_blank" rel="noopener noreferrer" class="notion-link" href="http://rocketmq.apache.org/">http://rocketmq.apache.org</a> 。在下载页面可以获取RocketMQ的源码包以及运行包。下载页面地址：<a target="_blank" rel="noopener noreferrer" class="notion-link" href="https://rocketmq.apache.org/download%E3%80%82">https://rocketmq.apache.org/download。</a></div><div class="notion-text notion-block-fe67c068fced4948bed2fa585c897477"> 接下来，RocketMQ建议的运行环境需要至少12G的内存，这是生产环境比较理想的资源配置。但是，学习阶段，如果你的服务器没有这么大的内存空间，那么就需要做一下调整。进入bin目录，对其中的runserver.sh和runbroker.sh两个脚本进行一下修改。</div><div class="notion-text notion-block-543040f92a5047679d53e67ce0b8e6c6"> 使用vi runserver.sh指令，编辑这个脚本，找到下面的一行配置，调整Java进程的内存大小。</div><div class="notion-text notion-block-2c3e74eda6ac4ad5880e7d1b11cbbc5c">接下来，同样调整runbroker.sh中的内存大小。</div><div class="notion-text notion-block-9478f4f50fb7444492dbc53f1e27a8db">调整完成后，就可以启动RocketMQ服务了。 RocketMQ服务基于Java开发，所以需要提前安装JDK。JDK建议采用1.8版本即可。</div><div class="notion-blank notion-block-1cc7ec39b5a341a29df5e31d5e14bfd7"> </div><div class="notion-text notion-block-c63c9fc2b7d84e2caf3b9cad7b147fc7">RocketMQ的后端服务分为nameserver和broker两个服务，关于他们的作用，后面会给你分享。接下来我们先将这两个服务启动起来。</div><div class="notion-text notion-block-7e2e106e4c044f3e88012f01644afe84"><b>第一步：启动nameserver服务。</b></div><div class="notion-text notion-block-941aad4d78f441388446373d65a15214">指令执行后，会生成一个nohup.out的日志文件。在这个日志文件里如果看到下面这一条关键日志，就表示nameserver服务启动成功了。</div><div class="notion-text notion-block-be56d6ca679a4b14b134b9ba4221d2b5">接下来，可以通过jsp指令进行验证。使用jps指令后，可以看到有一个NamesrvStartup的进程运行，也表示nameserver服务启动完成。</div><div class="notion-text notion-block-3340b80dcc80466faea34604a5517026"><b>第二步：启动broker服务。</b></div><div class="notion-text notion-block-15863826d4f24979a7bc24293baae271"> 启动broker服务之前，要做一个小小的配置。进入RocketMQ安装目录下的conf目录，修改broker.conf文件，在文件最后面加入一个配置：</div><blockquote class="notion-quote notion-block-8a61c4b59552401ea3ada155a1f136af"><div>这个选项是为了便于进行后续实验。他的作用是允许 broker 端自动创建新的 Topic。</div><div class="notion-text notion-block-a879c3870ca14b87be9fa869e93605bd">另外，如果你的服务器配置了多张网卡，比如阿里云，腾讯云这样的云服务器，他们通常有内网网卡和外网网卡两张网卡，那么需要增加配置brokerIP1属性，指向服务器的外网IP 地址，这样才能确保从其他服务器上访问到RocketMQ 服务。</div></blockquote><div class="notion-text notion-block-0039553810ac4dc7abec9f80fd8af096">然后也可以用之前的方式启动broker服务。启动broker服务的指令是mqbroker</div><div class="notion-blank notion-block-90851a50f4b241a89157e41b755e3bc2"> </div><div class="notion-text notion-block-f08f29c3370743fc8c4b4aee254da860">启动完成后，同样检查nohup.out日志文件，有如下一条关键日志，就表示broker服务启动正常了。</div><div class="notion-blank notion-block-6d87806a79d942a3bc39228e37f83ac5"> </div><div class="notion-text notion-block-9a56f49edbb44b458e970dc85867524a"><b><b>2、快速实现消息收发</b></b></div><div class="notion-text notion-block-d8e248e730b14f06819c8a354d6c128b"> RocketMQ后端服务启动完成后，就可以启动客户端的消息生产者和消息消费者进行消息转发了。接下来，我们会先通过RocketMQ提供的命令行工具快速体验一下RocketMQ消息收发的功能。然后，再动手搭建一个Maven项目，在项目中使用RocketMQ进行消息收发。</div><div class="notion-text notion-block-c6224efe0316488fa4d0c7f158742241"><b>1、命令行快速实现消息收发</b></div><div class="notion-text notion-block-62c409c409904003b22f46241fb27092"><b>第一步</b>：需要配置一个环境变量NAMESRV_ADDR，只想我们之前启动的nameserver服务。</div><div class="notion-text notion-block-ea15725b65184bf4b89ed512e030ace9">通过vi ~/.bash_profile添加以下配置。然后使用source ~/.bash_profile让配置生效。</div><div class="notion-text notion-block-7d5985b13014456aac4ca62a0432de15"><b>第二步</b>：通过指令启动RocketMQ的消息生产者发送消息。</div><div class="notion-text notion-block-167c8487b0d54c5cbebae6f7d0857bb6">这个指令会默认往RocketMQ中发送1000条消息。在命令行窗口可以看到发送消息的日志：</div><div class="notion-text notion-block-565374edf7ef498dab56adfd8aea19cc">这部分日志中，并没有打印出发送了什么消息。上面SendResult开头部分是消息发送到Broker后的结果。最后两行日志表示消息生产者发完消息后，服务正常关闭了。</div><div class="notion-text notion-block-1d890711a6c0464fa6080165d8c8b329"><b>第三步</b>：可以启动消息消费者接收之前发送的消息</div><div class="notion-text notion-block-2fc5b5f3be564a54a1e225def48b7959">消费者启动完成后，可以看到消费到的消息</div><div class="notion-blank notion-block-1ce680ae003548369dbdab768a7144dd"> </div><div class="notion-text notion-block-f8e409030e374451baf866cbc19a0afd"><b>2、搭建Maven客户端项目</b></div><div class="notion-text notion-block-9cb75060437141cc94c170dc4de3b912"> 之前的步骤实际上是在服务器上快速验证RocketMQ的服务状态，接下来我们动手搭建一个RocketMQ的客户端应用，在实际应用中集成使用RocketMQ。</div><div class="notion-text notion-block-2755e05e19094614a63a792862032a3e"><b>第一步</b>：创建一个标准的maven项目，在pom.xml中引入以下核心依赖</div><div class="notion-blank notion-block-b3b6131ed2074d58ac0f4edc0c226235"> </div><div class="notion-text notion-block-0828afe682ef4c8da53a2112b30f2775"><b>第二步</b>：就可以直接创建一个简单的消息生产者</div><div class="notion-blank notion-block-7db3afa5a74148ea931f0fffa50c9c95"> </div><div class="notion-text notion-block-2a3e76fc800a4a0d8ab475d619b67b6b">运行其中的main方法，就会往RocketMQ中发送两条消息。在这个实现过程中，需要注意一下的是对于生产者，需要指定对应的nameserver服务的地址，这个地址需要指向你自己的服务器。</div><div class="notion-blank notion-block-f6000c28f1664a0282296676f17e4710"> </div><div class="notion-text notion-block-67b89f5e4f414d1497096aa743fcce59"><b>第三步</b>：创建一个消息消费者接收RocketMQ中的消息。</div><div class="notion-blank notion-block-45d03d594fd24dbd8a8af83629343261"> </div><div class="notion-text notion-block-3fe94ea83f4449bfa24627f87a1a82be">运行其中的main方法后，就可以启动一个RocketMQ消费者，接收之前发到RocketMQ上的消息，并将消息内容打印出来。在这个实现过程中，需要重点关注的有两点。一是对于消费者，同样需要指定nameserver的地址。二是消费者需要在RocketMQ中订阅具体的Topic，只有发送到这个Topic上的消息才会被这个消费者接收到。</div><div class="notion-blank notion-block-c3ad91681b8e4367839526187b34d0b0"> </div><div class="notion-blank notion-block-5a7ef687bd3e4e89b3b9c532047ae40e"> </div><div class="notion-text notion-block-84d8c140214841beaaa91c39449eb2a5"><b><b>3、搭建RocketMQ可视化管理服务</b></b></div><div class="notion-text notion-block-d783222f53164e0bbc4073f66852023b">在之前的简单实验中，RocketMQ都是以后台服务的方式在运行，我们并不很清楚RocketMQ是如何运行的。RocketMQ的社区就提供了一个图形化的管理控制台Dashboard，可以用可视化的方式直接观测并管理RocketMQ的运行过程。</div><div class="notion-text notion-block-8e8fbd66b65948d292bbf3ea03e7b9a8"> Dashboard服务并不在RocketMQ的运行包中，需要到RocketMQ的官网下载页面单独下载。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-cd41355bfc4b40fe974893f11d9ac947"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:696px;max-width:100%;flex-direction:column"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F654145df-c7cb-42ca-b73f-a31009dc57e5%2Fdb03b4c4-d0dc-4f29-a602-1ec9fae126e1%2FUntitled.png?table=block&amp;id=cd41355b-fc4b-40fe-9748-93f11d9ac947" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-8121f9e0e52143839243b939b4192cf8">这里只提供了源码，并没有提供直接运行的jar包。将源码下载下来后，需要解压并进入对应的目录，使用maven进行编译。(需要提前安装maven客户端)</div><div class="notion-blank notion-block-6ca08e2eee0d46abb43f1951157b0483"> </div><div class="notion-text notion-block-96b0336eade54161bda2e57dcdd8f2e7">编译完成后，在源码的target目录下会生成可运行的jar包rocketmq-dashboard-1.0.1-SNAPSHOT.jar。接下来可以将这个jar包上传到服务器上。我们上传到/app/rocketmq/rocketmq-dashboard目录下</div><div class="notion-text notion-block-364bfbdadb114ac89e8634a306b8b813">接下来我们需要在jar包所在的目录下创建一个application.yml配置文件，在配置文件中做如下配置：</div><div class="notion-blank notion-block-04563f8c6d8c4ab5bb333cdc9bb3b4eb"> </div><div class="notion-text notion-block-a02210ac6375423fbed001e40e7a7bca">接下来就可以通过java指令执行这个jar包，启动管理控制台服务。</div><div class="notion-blank notion-block-a777f83991474543b48c34ede5f5bb0f"> </div><div class="notion-text notion-block-3b1f57aaf4a9451d8cee749174309f13">应用启动完成后，会在服务器上搭建起一个web服务，我们就可以通过访问http://192.168.232.128:8080查看到管理页面。</div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-cd7ae5cabf104bfaba203d7ec2339e55"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F654145df-c7cb-42ca-b73f-a31009dc57e5%2F4b13fbca-f021-45e2-88a4-9e131a70cd3a%2FUntitled.png?table=block&amp;id=cd7ae5ca-bf10-4bfa-ba20-3d7ec2339e55" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-blank notion-block-e9c83f24e04343659722e0223ec56ee3"> </div><div class="notion-text notion-block-7e88a8bcbacb4c358b22789f3c87a98d">这个管理控制台的功能非常全面。驾驶舱页面展示RocketMQ近期的运行情况。运维页面主要是管理nameserver服务。集群页面主要管理RocketMQ的broker服务。</div><div class="notion-blank notion-block-650b6ce981174dc0ab432f8051d95803"> </div><div class="notion-text notion-block-46b5be59629e42d4887da0cf6717835b"><b><b>升级高可用集群(略)</b></b></div><div class="notion-blank notion-block-dd125fd05ebc4b629d04cc7e610c09e5"> </div><div class="notion-text notion-block-f2ff71c98ab34715afa20f651cdf22cb"><b><b>总结RocketMQ的运行架构</b></b></div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-8466e0509a5d4b2a81a5b694379eb0ed"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F654145df-c7cb-42ca-b73f-a31009dc57e5%2Fba384279-0fec-4567-b38c-e84fac4fd28a%2FUntitled.png?table=block&amp;id=8466e050-9a5d-4b2a-81a5-b694379eb0ed" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-3dcadb4f1a4b4b719360666a70786533">1<b>、nameServer 命名服务</b></div><div class="notion-text notion-block-bf1658547f3240ee9c20c09f07f05ae9"> 在我们之前的实验过程中，你会发现，nameServer不依赖于任何其他的服务，自己独立就能启动。并且，不管是broker还是客户端，都需要明确指定nameServer的服务地址。以一台电脑为例，nameServer可以理解为是整个RocketMQ的CPU，整个RocketMQ集群都要在CPU的协调下才能正常工作。</div><div class="notion-text notion-block-dd09655cd1bf4f22b85aad460204f886"><b>2、broker 核心服务</b></div><div class="notion-text notion-block-26fd3c8383c24e10b33a1cf4aaef0daa"> 从之前的实验过程中你会发现，broker是RocketMQ中最为娇贵的一个组件。RockeMQ提供了各种各样的重要设计来保护broker的安全。同时broker也是RocketMQ中配置最为繁琐的部分。同样以电脑为例，broker就是整个RocketMQ中的硬盘、显卡这一类的核心硬件。RocketMQ最核心的消息存储、传递、查询等功能都要由broker提供。</div><div class="notion-text notion-block-488100214af94b2f81d5725b65d6634b"><b>3、client 客户端</b></div><div class="notion-text notion-block-71b8c2fe820444e48b23dbc3e48afe91"> Client包括消息生产者和消息消费者。同样以电脑为例，Client可以认为是RocketMQ中的键盘、鼠标、显示器这类的输入输出设备。鼠标、键盘输入的数据需要传输到硬盘、显卡等硬件才能进行处理。但是键盘、鼠标是不能直接将数据输入到硬盘、显卡的，这就需要CPU进行协调。通过CPU，鼠标、键盘就可以将输入的数据最终传输到核心的硬件设备中。经过硬件设备处理完成后，再通过CPU协调，显示器这样的输出设备就能最终从核心硬件设备中获取到输出的数据。</div><div class="notion-blank notion-block-d73a09318ead43b4836cc5d5dc6d77fa"> </div><div class="notion-text notion-block-63339a01e1b04b8ba1f4e0ca7d2b4510"><b><b>五、理解RocketMQ的消息模型</b></b></div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-fb4b75aba2964269abc80fb85c977970"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F654145df-c7cb-42ca-b73f-a31009dc57e5%2F97e6a53d-809b-4095-bdb0-8029d5af36be%2FUntitled.png?table=block&amp;id=fb4b75ab-a296-4269-abc8-0fb85c977970" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-blank notion-block-498001468e964595a30cb90847c04be6"> </div><div class="notion-text notion-block-5f461681f18a4ccda193b65ee0b17602">生产者和消费者都可以指定一个Topic发送消息或者拉取消息。而Topic是一个逻辑概念。Topic中的消息会分布在后面多个MessageQueue当中。这些MessageQueue会分布到一个或者多个broker中。</div><div class="notion-text notion-block-d4f0fe2edbc54066adf6b805b4afc221">在RocketMQ的这个消息模型当中，最为核心的就是Topic。对于客户端，Topic代表了一类有相同业务规则的消息。对于Broker，Topic则代表了系统中一系列存储消息的资源。所以，RocketMQ对于Topic是需要做严格管理的。如果任由客户端随意创建Topic，那么服务端的资源管理压力就会非常大。默认情况下，Topic都需要由管理员在RocketMQ的服务端手动进行创建，然后才能给客户端使用的。而我们之前在broker.conf中手动添加的autoCreateTopic=true，就是表示可以由客户端自行创建Topic。这种配置方式显然只适用于测试环境，在生产环境不建议打开这个配置项。如果需要创建 Topic，可以交由运维人员提前创建 Topic。</div><div class="notion-text notion-block-de71f5f09fb34a38918ff3baebf0cc5e">而对于业务来说，最为重要的就是消息Message了。生产者发送到某一个Topic下的消息，最终会保存在Topic下的某一个MessageQueue中。而消费者来消费消息时，RocketMQ会在Broker端给每个消费者组记录一个消息的消费位点Offset。通过Offset控制每个消费者组的消息处理进度。这样，每一条消息，在一个消费者组当中只被处理一次。</div><div class="notion-blank notion-block-167c2f8c7f334291b015ed9a7ac0f8fc"> </div><div class="notion-blank notion-block-10ca13d2c0504cf195dce824a6fe70cc"> </div><div class="notion-text notion-block-ffa6934f262d4f46ab0751084ed7f849">致谢：</div><div class="notion-callout notion-gray_background_co notion-block-25bde9c8deaa4ad9987a60037e2ce13f"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="💡">💡</span></div><div class="notion-callout-text">有关Notion安装或者使用上的问题，欢迎您在底部评论区留言，一起交流~</div></div><div class="notion-blank notion-block-f19e238731874d1a8cf1ba0f2a93048c"> </div><div class="notion-blank notion-block-91afa0b48beb46b6b0deb0f89bbaef71"> </div></main></div>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[redis高可用集群]]></title>
            <link>https://blog.juanshen.eu.org/article/dbb31a8c-72e9-4a2f-b1ed-16d437375240</link>
            <guid>https://blog.juanshen.eu.org/article/dbb31a8c-72e9-4a2f-b1ed-16d437375240</guid>
            <pubDate>Tue, 11 Jul 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[redis高可用集群]]></description>
            <content:encoded><![CDATA[<div id="notion-article" class="mx-auto overflow-hidden "><main class="notion light-mode notion-page notion-block-dbb31a8c72e94a2fb1ed16d437375240"><div class="notion-viewport"></div><div class="notion-collection-page-properties"></div><div class="notion-text notion-block-e401805c73b84675985e34ad80306863"><b>1、Redis集群方案比较</b></div><div class="notion-text notion-block-58266b4e70b748d4bd012617907cc053">•<span class="notion-red"> </span><span class="notion-red"><b>哨兵模式</b></span></div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-82b05c9b25cf4e0286808175b70a3856"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F654145df-c7cb-42ca-b73f-a31009dc57e5%2Fa0dbb228-44be-4ccf-89ad-c0226e9d1af3%2FUntitled.png?table=block&amp;id=82b05c9b-25cf-4e02-8680-8175b70a3856" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-text notion-block-6e12ca2491de44c2ab0e93980d1d2267">在redis3.0以前的版本要实现集群一般是借助哨兵sentinel工具来监控master节点的状态，如果master节点异常，则会做主从切换，将某一台slave作为master，哨兵的配置略微复杂，并且性能和高可用性等各方面表现一般，特别是在主从切换的瞬间存在访问瞬断的情况，而且哨兵模式只有一个主节点对外提供服务，没法支持很高的并发，且单个主节点内存也不宜设置得过大，否则会导致持久化文件过大，影响数据恢复或主从同步的效率</div><div class="notion-blank notion-block-2c648f7f73a34ba0b2cbc29cbb7ba685"> </div><div class="notion-text notion-block-f55dff9be3ba40299d5484a5be769b42">•<span class="notion-red"> </span><span class="notion-red"><b>高可用集群模式</b></span></div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-7d725e83cf684afcbafd325d37280e1c"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F654145df-c7cb-42ca-b73f-a31009dc57e5%2F32968d7e-8571-4892-9fc5-41e78de8eb6e%2FUntitled.png?table=block&amp;id=7d725e83-cf68-4afc-bafd-325d37280e1c" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-blank notion-block-872d843ccc6a44228cdaff93d40c7d1d"> </div><div class="notion-text notion-block-7d3db6f505394e5197d07ff18aaa6e3f">redis集群是一个由多个主从节点群组成的分布式服务器群，它具有复制、高可用和分片特性。Redis集群不需要sentinel哨兵·也能完成节点移除和故障转移的功能。需要将每个节点设置成集群模式，这种集群模式没有中心节点，可水平扩展，据官方文档称可以线性扩展到上万个节点(<b>官方推荐不超过1000个节点</b>)。redis集群的性能和高可用性均优于之前版本的哨兵模式，且集群配置非常简单</div><div class="notion-blank notion-block-19fcd51c23384413ac256209560ff29f"> </div><div class="notion-text notion-block-c8c45a4047a84edbae28311edf424610"><b>2、Redis高可用集群搭建</b></div><ul class="notion-list notion-list-disc notion-block-538a133daee04e1391fd91a12b498ec8"><li><span class="notion-red"><b>redis集群搭建</b></span></li></ul><div class="notion-text notion-block-e26ce388cfbd4578a0d737192c94d83d">redis集群需要至少三个master节点，我们这里搭建三个master节点，并且给每个master再搭建一个slave节点，总共6个redis节点，这里用三台机器部署6个redis实例，每台机器一主一从，搭建集群的步骤如下：</div><div class="notion-blank notion-block-583495912dce420099e45eee84038aeb"> </div><div class="notion-text notion-block-a8d9dd86a1164dcd98a934bae6b0a935"><b>3、Java操作redis集群</b></div><div class="notion-text notion-block-af28052634ca4ba894d63c7134538e2c">借助redis的java客户端jedis可以操作以上集群，引用jedis版本的maven坐标如下：</div><div class="notion-blank notion-block-55bc08166e3f4064bcae60ccb9acbb26"> </div><div class="notion-blank notion-block-44c9225b7bb14dd395e81c17d59cffef"> </div><div class="notion-text notion-block-003014f735014fd7842e0ed672fc8040">集群的Spring Boot整合Redis</div><div class="notion-text notion-block-9c18ba3c06ae4efbaa80df25a26c5d66">1、引入相关依赖：</div><div class="notion-text notion-block-26df8ec576174e3b818028fcbac6834c">springboot项目核心配置：</div><div class="notion-text notion-block-a187c6ad91a84fc4b35095094e63372d">访问代码</div><div class="notion-text notion-block-f1f89b5f31d04d28a90255ecb2782354"><b>4、Redis集群原理分析</b></div><div class="notion-text notion-block-e251ab0c1a994f5bbc6010dcd3212adc">Redis Cluster 将所有数据划分为 16384 个 slots(槽位)，每个节点负责其中一部分槽位。槽位的信息存储于每个节点中。</div><div class="notion-text notion-block-836cb3466643442b89431456b0cbe70f">当 Redis Cluster 的客户端来连接集群时，它也会得到一份集群的槽位配置信息并将其缓存在客户端本地。这样当客户端要查找某个 key 时，可以直接定位到目标节点。同时因为槽位的信息可能会存在客户端与服务器不一致的情况，还需要纠正机制来实现槽位信息的校验调整。</div><div class="notion-text notion-block-4c92498e35a04d0c9da389245f302b46"><b>槽位定位算法</b></div><div class="notion-text notion-block-807443f87cbd4b508bed6d74c9ef3305">Cluster 默认会对 key 值使用 crc16 算法进行 hash 得到一个整数值，然后用这个整数值对 16384 进行取模来得到具体槽位。</div><div class="notion-text notion-block-359dd69681af4a9a93975bb23a4261fa">HASH_SLOT = CRC16(key) mod 16384</div><div class="notion-text notion-block-0c927d9495b848a391c643a819923bd0"><b>Redis集群节点间的通信机制</b></div><div class="notion-text notion-block-7f038017190a421a999dd71cdc7be300">redis cluster节点间采取gossip协议进行通信</div><ul class="notion-list notion-list-disc notion-block-7ecd0b0f8573415ca8785901f36ad78a"><li>维护集群的元数据(集群节点信息，主从角色，节点数量，各节点共享的数据等)有两种方式：集中式和gossip</li></ul><div class="notion-text notion-block-d02bf18b296b4ea6a7ffe36dfbf53785"><b>集中式：</b></div><div class="notion-text notion-block-f4823f25c196474dac3f2146a356960c">优点在于元数据的更新和读取，时效性非常好，一旦元数据出现变更立即就会更新到集中式的存储中，其他节点读取的时候立即就可以立即感知到；不足在于所有的元数据的更新压力全部集中在一个地方，可能导致元数据的存储压力。 很多中间件都会借助zookeeper集中式存储元数据。</div><div class="notion-text notion-block-c17d8d6df3374581bf685c77b2b52af8"><b>gossip：</b></div><figure class="notion-asset-wrapper notion-asset-wrapper-image notion-block-b138bea3c94a4306b069862129a624f2"><div style="position:relative;display:flex;justify-content:center;align-self:center;width:100%;max-width:100%;flex-direction:column;height:100%"><img style="object-fit:cover" src="https://www.notion.so/image/https%3A%2F%2Fprod-files-secure.s3.us-west-2.amazonaws.com%2F654145df-c7cb-42ca-b73f-a31009dc57e5%2F144f3b4f-78b5-45bb-a7b5-dd4eb30af92b%2FUntitled.png?table=block&amp;id=b138bea3-c94a-4306-b069-862129a624f2" alt="notion image" loading="lazy" decoding="async"/></div></figure><div class="notion-blank notion-block-4e468cb90ba14773bf6c09953c86fcc4"> </div><div class="notion-text notion-block-7f9a41ce630842649ca97955821dfcb9"><b>Redis集群选举原理分析</b></div><div class="notion-text notion-block-6c8976df909e41f598c91f2075febaeb">当slave发现自己的master变为FAIL状态时，便尝试进行Failover，以期成为新的master。由于挂掉的master可能会有多个slave，从而存在多个slave竞争成为master节点的过程， 其过程如下：</div><div class="notion-text notion-block-275a19e4c2d346b59f42aaa7ea1241b4">1.slave发现自己的master变为FAIL</div><div class="notion-text notion-block-68829e31f20b43be85cc79e13a23510d">2.将自己记录的集群currentEpoch加1，并广播FAILOVER_AUTH_REQUEST 信息</div><div class="notion-text notion-block-425d6ad858bb4fa1a0e5b83485c4861e">3.其他节点收到该信息，只有master响应，判断请求者的合法性，并发送FAILOVER_AUTH_ACK，对每一个epoch只发送一次ack</div><div class="notion-text notion-block-db4d53ba022443c1a1b34b5f220a8c07">4.尝试failover的slave收集master返回的FAILOVER_AUTH_ACK</div><div class="notion-text notion-block-10246f2cc2a34f2c86b6104eace1f0be">5.slave收到超过半数master的ack后变成新Master(这里解释了集群为什么至少需要三个主节点，如果只有两个，当其中一个挂了，只剩一个主节点是不能选举成功的)</div><div class="notion-text notion-block-37d67818aa464a558ee8322a007823aa">6.slave广播Pong消息通知其他集群节点。</div><div class="notion-blank notion-block-877c0753b51d4810bff220a68c4f67e8"> </div><div class="notion-text notion-block-89e8e72d83824c4f8053abc76e9f82af">从节点并不是在主节点一进入 FAIL 状态就马上尝试发起选举，而是有一定延迟，一定的延迟确保我们等待FAIL状态在集群中传播，slave如果立即尝试选举，其它masters或许尚未意识到FAIL状态，可能会拒绝投票
•延迟计算公式：</div><div class="notion-blank notion-block-837dcd11fac448febbc3e8cd324d0920"> </div><div class="notion-text notion-block-b410bd17da6c4d67a3aa487597acea2f"><b>集群是否完整才能对外提供服务</b></div><div class="notion-text notion-block-64875b26782245a1888d779b92d646b1">当redis.conf的配置cluster-require-full-coverage为no时，表示当负责一个插槽的主库下线且没有相应的从库进行故障恢复时，集群仍然可用，如果为yes则集群不可用。</div><div class="notion-blank notion-block-08da6dea192f46f3b858fb83a0aa04a4"> </div><div class="notion-text notion-block-ce8b0e20d8bf4e4e9d4119d03ae72f38"><b>Redis集群为什么至少需要三个master节点，并且推荐节点数为奇数？</b></div><div class="notion-text notion-block-ee08fb263b854276bfd3a2dbece1ea51">因为新master的选举需要大于半数的集群master节点同意才能选举成功，如果只有两个master节点，当其中一个挂了，是达不到选举新master的条件的。</div><div class="notion-text notion-block-aa28f86086a64d60abaa1d9924cb94af">奇数个master节点可以在满足选举该条件的基础上节省一个节点，比如三个master节点和四个master节点的集群相比，大家如果都挂了一个master节点都能选举新master节点，如果都挂了两个master节点都没法选举新master节点了，所以奇数的master节点更多的是<b>从节省机器资源角度出发</b>说的。</div><div class="notion-blank notion-block-a93378056b42498d8ec185454092b6ea"> </div><div class="notion-blank notion-block-9bc53facfe9b4994a9b7073b247bf65f"> </div><div class="notion-text notion-block-9776d11af59d4f6a8061ca83908df63c">致谢：</div><div class="notion-callout notion-gray_background_co notion-block-b7b258f71d96478ea3dfd3668ece9149"><div class="notion-page-icon-inline notion-page-icon-span"><span class="notion-page-icon" role="img" aria-label="💡">💡</span></div><div class="notion-callout-text">有关Notion安装或者使用上的问题，欢迎您在底部评论区留言，一起交流~</div></div><div class="notion-blank notion-block-20e9ceed5248493fbbd039a52e70eb89"> </div><div class="notion-blank notion-block-08758de136e64e61ad00131ec1196f45"> </div></main></div>]]></content:encoded>
        </item>
    </channel>
</rss>