{"id":807,"date":"2025-03-21T23:41:49","date_gmt":"2025-03-21T15:41:49","guid":{"rendered":"http:\/\/www.cmd137blog.top\/?p=807"},"modified":"2025-08-19T22:41:03","modified_gmt":"2025-08-19T14:41:03","slug":"%e9%bb%91%e9%a9%ac%e7%82%b9%e8%af%84%e7%ac%94%e8%ae%b0","status":"publish","type":"post","link":"http:\/\/www.cmd137blog.top\/?p=807","title":{"rendered":"\u9ed1\u9a6c\u70b9\u8bc4\u7b14\u8bb0"},"content":{"rendered":"\n<h1 class=\"wp-block-heading\">0 \u51c6\u5907\u5de5\u4f5c\u53ca\u524d\u7f6e\u77e5\u8bc6<\/h1>\n\n\n\n<p>\u5bfc\u51fa\u521d\u59cb\u5316\u6587\u4ef6\u5e76\u4e0a\u4f20\u5230gitee<\/p>\n\n\n\n<p><a href=\"https:\/\/gitee.com\/CMD137\/hm-dianping\">CMD137\/hm-dianping<\/a><\/p>\n\n\n\n<h1 class=\"wp-block-heading\">1 \u77ed\u4fe1\u767b\u5f55<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">1.1 \u53d1\u9001\u9a8c\u8bc1\u7801<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>public Result sendcode(String phone, HttpSession session) {\n        \/\/1.\u6821\u9a8c\u624b\u673a\u53f7\u7801\n        if(RegexUtils.isPhoneInvalid(phone))\n            return Result.fail(\"\u624b\u673a\u53f7\u7801\u683c\u5f0f\u9519\u8bef\");\n        \/\/2.\u751f\u6210\u9a8c\u8bc1\u7801\n        String code = RandomUtil.randomNumbers(6);\n        \/\/3.\u5b58\u5165session\u5e76\u8fd4\u56de\n        session.setAttribute(\"code\",code);\n        \/\/4.\u8c03\u7528\u7b2c\u4e09\u65b9api\u5411\u624b\u673a\u53d1\u9001\u9a8c\u8bc1\u7801\n        log.info(\"\u53d1\u9001\u9a8c\u8bc1\u7801\uff1a{}\",code);\n\n        return Result.ok();\n    }<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">1.2 \u7528\u6237\u767b\u5f55\u6821\u9a8c<\/h2>\n\n\n\n<p>\u6ce8\u610f\u6b64\u5904\u4f7f\u7528UserDTO\u7c7b\u578b\u5b58\u5165session\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u6570\u636e\u8131\u654f<\/li>\n\n\n\n<li>\u51cf\u8f7bThraedLocal\u548cSession\u7684\u5185\u5b58\u538b\u529b\u3002<\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>public Result login(LoginFormDTO loginForm, HttpSession session) {<br>    \/\/1.\u6821\u9a8c\u624b\u673a\u53f7<br>    String phone = loginForm.getPhone();<br>    if(RegexUtils.<em>isPhoneInvalid<\/em>(phone))<br>        return Result.<em>fail<\/em>(\"\u624b\u673a\u53f7\u7801\u683c\u5f0f\u9519\u8bef\");<br><br>    \/\/2.\u6821\u9a8c\u9a8c\u8bc1\u7801<br>    String trueCode = (String) session.getAttribute(\"code\");<br>    if (trueCode == null) {<br>        return Result.<em>fail<\/em>(\"\u9a8c\u8bc1\u7801\u5df2\u5931\u6548\uff0c\u8bf7\u91cd\u65b0\u83b7\u53d6\");<br>    }<br>    if (!trueCode.equals(loginForm.getCode()))<br>        return Result.<em>fail<\/em>(\"\u9a8c\u8bc1\u7801\u9519\u8bef\");<br><br>    \/\/3.\u67e5\u8be2phone\uff0c\u5982\u679c\u4e0d\u5b58\u5728\uff0c\u76f4\u63a5\u521b\u5efa\u65b0\u7528\u6237<br>    User user = query().eq(\"phone\", phone).one();<br>    if (user==null){<br>        user = new User();<br>        user.setPhone(phone);<br>        user.setNickName(<em>USER_NICK_NAME_PREFIX<\/em>+RandomUtil.<em>randomString<\/em>(10));<br>        save(user);<br>    }<br>    \/\/4.\u5c06\u7528\u6237\u4fe1\u606f\u5b58\u5165session<br>    session.setAttribute(\"user\", BeanUtil.copyProperties(user, UserDTO.class));<br><br>    return Result.<em>ok<\/em>();<br>}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\u77e5\u8bc6\u70b9\uff1a<\/h3>\n\n\n\n<p>session\u8fc7\u671f\u540e\u5982\u679c\u4f60\u4ece Session \u4e2d\u83b7\u53d6\u67d0\u4e2a\u5c5e\u6027\uff08\u5982\u9a8c\u8bc1\u7801\u6216\u7528\u6237\u4fe1\u606f\uff09\uff0c\u4f1a\u8fd4\u56de <code>null<\/code>\uff0c\u524d\u7aef\u5f97\u5230\u7684sessionid\u53d1\u8fc7\u6765\u627e\u4e0d\u5230\u5bf9\u5e94\u7684session\u3002<\/p>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">1.3 \u767b\u5f55\u6821\u9a8c\u62e6\u622a\u5668<\/h2>\n\n\n\n<p>\u8bbe\u7f6e\u62e6\u622a\u5668<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public class LoginInterceptor implements HandlerInterceptor {<br>    @Override<br>    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {<br>        \/\/1.\u83b7\u53d6\u7528\u6237<br>        Object user =request.getSession().getAttribute(\"user\");<br>        \/\/2.\u82e5\u7528\u6237\u4e3a\u7a7a\uff0c\u8fdb\u884c\u62e6\u622a<br>        if (user==null){<br>            response.setStatus(401);<br>            return false;<br>        }<br>        \/\/3.\u4e0d\u7a7a\uff0c\u5b58\u5165threadLocal\uff0c\u653e\u884c<br>        UserHolder.<em>saveUser<\/em>((UserDTO) user);<br>        return true;<br>    }<br>    @Override<br>    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {<br>        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);<br>    }<br>}<\/code><\/pre>\n\n\n\n<p>\u6ce8\u518c\u62e6\u622a\u5668<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@Configuration<br>public class MvcConfig implements WebMvcConfigurer {<br>    @Override<br>    public void addInterceptors(InterceptorRegistry registry) {<br>        registry.addInterceptor(new LoginInterceptor())<br>                .excludePathPatterns(<br>                        \"\/shop\/**\",<br>                        \"\/voucher\/**\",<br>                        \"\/shop-type\/**\",<br>                        \"\/upload\/**\",<br>                        \"\/blog\/hot\",<br>                        \"\/user\/cod e\",<br>                        \"\/user\/login\"<br>                );<br>    }<br>}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\u77e5\u8bc6\u70b9\uff1a<\/h3>\n\n\n\n<p>\u6bcf\u4e00\u4e2atomcat\u7684\u8bf7\u6c42\u90fd\u662f\u4e00\u4e2a\u72ec\u7acb\u7684\u7ebf\u7a0b\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">1.4 \u57fa\u4e8e Redis \u89e3\u51b3 Session \u5171\u4eab\u95ee\u9898<\/h2>\n\n\n\n<p>\u5728\u96c6\u7fa4\u73af\u5883\u4e0b\uff0cSession \u7684\u96c6\u7fa4\u5171\u4eab\u4e3b\u8981\u8981\u89e3\u51b3\u4ee5\u4e0b\u95ee\u9898\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u6570\u636e\u4e00\u81f4\u6027\u95ee\u9898<\/strong>\uff1a\u786e\u4fdd\u5728\u591a\u4e2a\u670d\u52a1\u5668\u4e4b\u95f4\uff0cSession \u6570\u636e\u80fd\u591f\u4fdd\u6301\u4e00\u81f4\uff0c\u907f\u514d\u51fa\u73b0\u6570\u636e\u51b2\u7a81\u6216\u4e0d\u4e00\u81f4\u7684\u60c5\u51b5\uff0c\u4ee5\u514d\u5bfc\u81f4\u7528\u6237\u4f53\u9a8c\u53d7\u635f\u6216\u4e1a\u52a1\u903b\u8f91\u51fa\u73b0\u9519\u8bef\u3002<\/li>\n\n\n\n<li><strong>\u5206\u5e03\u5f0f\u5b58\u50a8\u4e0e\u8bbf\u95ee\u95ee\u9898<\/strong>\uff1a\u9700\u8981\u627e\u5230\u5408\u9002\u7684\u65b9\u5f0f\u5c06 Session \u6570\u636e\u5206\u5e03\u5f0f\u5b58\u50a8\u5728\u96c6\u7fa4\u4e2d\u7684\u591a\u4e2a\u8282\u70b9\u4e0a\uff0c\u5e76\u80fd\u8ba9\u5404\u4e2a\u8282\u70b9\u5feb\u901f\u3001\u9ad8\u6548\u5730\u8bbf\u95ee\u548c\u66f4\u65b0\u8fd9\u4e9b\u6570\u636e\uff0c\u4ee5\u63d0\u9ad8\u7cfb\u7edf\u7684\u6027\u80fd\u548c\u53ef\u6269\u5c55\u6027\u3002<\/li>\n\n\n\n<li><strong>\u8d1f\u8f7d\u5747\u8861\u95ee\u9898<\/strong>\uff1a\u5f53\u7528\u6237\u8bf7\u6c42\u5728\u4e0d\u540c\u670d\u52a1\u5668\u4e4b\u95f4\u5206\u53d1\u65f6\uff0c\u8981\u4fdd\u8bc1\u65e0\u8bba\u8bf7\u6c42\u88ab\u8def\u7531\u5230\u54ea\u4e2a\u670d\u52a1\u5668\uff0c\u90fd\u80fd\u6b63\u786e\u5730\u83b7\u53d6\u548c\u5904\u7406\u8be5\u7528\u6237\u7684 Session \u6570\u636e\uff0c\u5b9e\u73b0\u8d1f\u8f7d\u5747\u8861\u7684\u540c\u65f6\u4e0d\u5f71\u54cd Session \u7684\u6b63\u5e38\u4f7f\u7528\u3002<\/li>\n\n\n\n<li><strong>\u6545\u969c\u8f6c\u79fb\u95ee\u9898<\/strong>\uff1a\u5982\u679c\u96c6\u7fa4\u4e2d\u7684\u67d0\u4e2a\u670d\u52a1\u5668\u51fa\u73b0\u6545\u969c\uff0c\u8981\u80fd\u591f\u5c06 Session \u6570\u636e\u65e0\u7f1d\u5730\u8f6c\u79fb\u5230\u5176\u4ed6\u5065\u5eb7\u7684\u670d\u52a1\u5668\u4e0a\uff0c\u786e\u4fdd\u7528\u6237\u7684\u64cd\u4f5c\u4e0d\u4f1a\u4e2d\u65ad\uff0c\u4fdd\u8bc1\u7cfb\u7edf\u7684\u9ad8\u53ef\u7528\u6027\u3002<\/li>\n\n\n\n<li><strong>\u5e76\u53d1\u8bbf\u95ee\u63a7\u5236\u95ee\u9898<\/strong>\uff1a\u591a\u4e2a\u7528\u6237\u540c\u65f6\u8bbf\u95ee\u548c\u4fee\u6539\u540c\u4e00\u4e2a Session \u6570\u636e\u65f6\uff0c\u9700\u8981\u8fdb\u884c\u6709\u6548\u7684\u5e76\u53d1\u63a7\u5236\uff0c\u9632\u6b62\u6570\u636e\u88ab\u7834\u574f\u6216\u51fa\u73b0\u4e0d\u4e00\u81f4\u7684\u72b6\u6001\uff0c\u786e\u4fdd\u6570\u636e\u7684\u5b8c\u6574\u6027\u548c\u51c6\u786e\u6027\u3002<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">\u89e3\u51b3\u65b9\u6848\uff1a<\/h3>\n\n\n\n<p><strong>Redis\u4ee3\u66ffsession\u7684\u4e1a\u52a1\u6d41\u7a0b<\/strong>\uff1a<\/p>\n\n\n\n<p><strong>key\u7684\u7ed3\u6784<\/strong>\uff1a<br>\u4f7f\u7528<strong>hash<\/strong>\uff0c\u65e2\u53ef\u4ee5<strong>\u8282\u7ea6\u5185\u5b58<\/strong>\uff0c\u8fd8<strong>\u4fbf\u4e8e\u5355\u5b57\u6bb5\u7684crud<\/strong>\u3002<\/p>\n\n\n\n<p><br>\u8bbe\u8ba1key\uff0c\u9700\u8981\u6ee1\u8db3\u4e24\u70b9\uff1a<br>1\u3001key\u8981\u5177\u6709\u552f\u4e00\u6027<br>2\u3001key\u8981\u65b9\u4fbf\u643a\u5e26<br><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><s>\u4f7f\u7528phone\uff1a \u654f\u611f\u6570\u636e\u4e0d\u5b9c\u5e26\u5230\u524d\u7aef<\/s><\/li>\n\n\n\n<li>\u4f7f\u7528\u968f\u673atoken<\/li>\n<\/ul>\n\n\n\n<p>\u4fee\u6539\u540e\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public Result sendcode(String phone, HttpSession session) {<br>    \/\/1.\u6821\u9a8c\u624b\u673a\u53f7\u7801<br>    if(RegexUtils.<em>isPhoneInvalid<\/em>(phone))<br>        return Result.<em>fail<\/em>(\"\u624b\u673a\u53f7\u7801\u683c\u5f0f\u9519\u8bef\");<br>    \/\/2.\u751f\u6210\u9a8c\u8bc1\u7801<br>    String code = RandomUtil.<em>randomNumbers<\/em>(6);<br>    \/\/3.\u5b58\u5165redis,\u6709\u6548\u671f2\u5206\u949f<br>    redisTemplate.opsForValue().set(<em>LOGIN_CODE_KEY<\/em>+phone,code,<em>LOGIN_CODE_TTL<\/em>, TimeUnit.<em>MINUTES<\/em>);<br>    \/\/4.\u8c03\u7528\u7b2c\u4e09\u65b9api\u5411\u624b\u673a\u53d1\u9001\u9a8c\u8bc1\u7801<br>    <em>log<\/em>.info(\"\u53d1\u9001\u9a8c\u8bc1\u7801\uff1a{}\",code);<br><br>    return Result.<em>ok<\/em>();<br>}<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>public Result login(LoginFormDTO loginForm, HttpSession session) {<br>    \/\/1.\u6821\u9a8c\u624b\u673a\u53f7<br>    String phone = loginForm.getPhone();<br>    if(RegexUtils.<em>isPhoneInvalid<\/em>(phone))<br>        return Result.<em>fail<\/em>(\"\u624b\u673a\u53f7\u7801\u683c\u5f0f\u9519\u8bef\");<br><br>    \/\/2.\u6821\u9a8c\u9a8c\u8bc1\u7801<br>    String trueCode = (String) redisTemplate.opsForValue().get(<em>LOGIN_CODE_KEY<\/em>+phone);<br>    if (trueCode == null) {<br>        return Result.<em>fail<\/em>(\"\u9a8c\u8bc1\u7801\u5df2\u5931\u6548\uff0c\u8bf7\u91cd\u65b0\u83b7\u53d6\");<br>    }<br>    if (!trueCode.equals(loginForm.getCode()))<br>        return Result.<em>fail<\/em>(\"\u9a8c\u8bc1\u7801\u9519\u8bef\");<br><br>    \/\/3.\u67e5\u8be2phone\uff0c\u5982\u679c\u4e0d\u5b58\u5728\uff0c\u76f4\u63a5\u521b\u5efa\u65b0\u7528\u6237<br>    User user = query().eq(\"phone\", phone).one();<br>    if (user==null){<br>        user = new User();<br>        user.setPhone(phone);<br>        user.setNickName(<em>USER_NICK_NAME_PREFIX<\/em>+RandomUtil.<em>randomString<\/em>(10));<br>        save(user);<br>    }<br><br>    \/\/4.\u751f\u6210token,\u4f5c\u4e3akey<br>    String token = UUID.<em>randomUUID<\/em>().toString();<br><br>    \/\/5.\u5c06\u7528\u6237\u4fe1\u606f\u5b58\u5165redis<br>    UserDTO userForRedis =new UserDTO();<br>    BeanUtil.<em>copyProperties<\/em>(user,userForRedis);<br><br>    Map&lt;String, Object&gt; map=BeanUtil.<em>beanToMap<\/em>(userForRedis);<br><br>    redisTemplate.opsForHash().putAll(<em>LOGIN_USER_KEY<\/em>+token,map);<br>    redisTemplate.expire(<em>LOGIN_USER_KEY<\/em>+token,30,TimeUnit.<em>MINUTES<\/em>);<br><br>    \/\/6.\u4f20\u56de\u4f5c\u4e3a\u767b\u5f55\u51ed\u8bc1<br>    return Result.<em>ok<\/em>(token);<br>}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\u56e0redis\u66ff\u4ee3\u4e86session\uff0c\u5bf9\u62e6\u622a\u5668\u4e5f\u8981\u4fee\u6539\uff1a<\/h3>\n\n\n\n<p>\u4e4b\u524d\u7684\u62e6\u622a\u5668\u65e0\u6cd5\u5bf9\u4e0d\u9700\u8981\u62e6\u622a\u7684\u8def\u5f84\u751f\u6548\uff0c\u90a3\u4e48\u6211\u4eec\u53ef\u4ee5\u6dfb\u52a0\u4e00\u4e2a\u62e6\u622a\u5668\uff0c\u5728\u7b2c\u4e00\u4e2a\u62e6\u622a\u5668\u4e2d\u62e6\u622a\u6240\u6709\u7684\u8def\u5f84\uff0c\u628a\u7b2c\u4e8c\u4e2a\u62e6\u622a\u5668\u505a\u7684\u4e8b\u60c5\u653e\u5165\u5230\u7b2c\u4e00\u4e2a\u62e6\u622a\u5668\u4e2d\uff0c\u540c\u65f6\u5237\u65b0\u4ee4\u724c\uff0c\u56e0\u4e3a\u7b2c\u4e00\u4e2a\u62e6\u622a\u5668\u6709\u4e86threadLocal\u7684\u6570\u636e\uff0c\u6240\u4ee5\u6b64\u65f6\u7b2c\u4e8c\u4e2a\u62e6\u622a\u5668\u53ea\u9700\u8981\u5224\u65ad\u62e6\u622a\u5668\u4e2d\u7684user\u5bf9\u8c61\u662f\u5426\u5b58\u5728\u5373\u53ef\uff0c\u5b8c\u6210\u6574\u4f53\u5237\u65b0\u529f\u80fd\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u7b2c\u4e00\u4e2a\u62e6\u622a\u5668\uff1a\uff08\u6ce8\u610fredisTemplate\u7684\u6ce8\u5165\u95ee\u9898\uff09<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>public class RefreshTokenInterceptor implements HandlerInterceptor {<br><br>    \/\/\u901a\u8fc7\u6784\u9020\u51fd\u6570\u6ce8\u5165redisTemplate\uff0c\u56e0\u4e3aRefreshTokenInterceptor\u5e76\u6ca1\u6709\u88abspring\u5bb9\u5668\u7ba1\u7406<br>    \/\/\u53e6\u5916\uff0c\u76f4\u63a5\u52a0\u4e0a@component\u6ce8\u89e3\u4e5f\u4e0d\u884c\uff0c\u56e0\u4e3aRefreshTokenInterceptor\u662f\u88ab\u624b\u52a8new\u51fa\u6765\u7684<br>    private final RedisTemplate&lt;String, Object&gt; redisTemplate;<br><br>    public RefreshTokenInterceptor(RedisTemplate&lt;String, Object&gt; redisTemplate) {<br>        this.redisTemplate = redisTemplate;<br>    }<br><br>    @Override<br>    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {<br>    \/\/ 1.\u83b7\u53d6\u8bf7\u6c42\u5934\u4e2d\u7684token<br>        String token = request.getHeader(\"authorization\");<br>        if (token==null) {<br>            return true;<br>        }<br>        \/\/ 2.\u57fa\u4e8eTOKEN\u83b7\u53d6redis\u4e2d\u7684\u7528\u6237<br>        String key  = <em>LOGIN_USER_KEY <\/em>+ token;<br>        Map&lt;Object, Object&gt; userMap = redisTemplate.opsForHash().entries(key);<br>        \/\/ 3.\u5224\u65ad\u7528\u6237\u662f\u5426\u5b58\u5728<br>        if (userMap.isEmpty()) {<br>            return true;<br>        }<br>        \/\/ 5.\u5c06\u67e5\u8be2\u5230\u7684hash\u6570\u636e\u8f6c\u4e3aUserDTO<br>        UserDTO userDTO = BeanUtil.<em>fillBeanWithMap<\/em>(userMap, new UserDTO(), false);<br>        \/\/ 6.\u5b58\u5728\uff0c\u4fdd\u5b58\u7528\u6237\u4fe1\u606f\u5230 ThreadLocal<br>        UserHolder.<em>saveUser<\/em>(userDTO);<br>        \/\/ 7.\u5237\u65b0token\u6709\u6548\u671f<br>        redisTemplate.expire(key, <em>LOGIN_USER_TTL<\/em>, TimeUnit.<em>MINUTES<\/em>);<br>        \/\/ 8.\u653e\u884c<br>        return true;<br>    }<br><br>    @Override<br>    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {<br>        \/\/ \u79fb\u9664\u7528\u6237<br>        UserHolder.<em>removeUser<\/em>();<br>    }<br>}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\u7b2c\u4e8c\u4e2a\u62e6\u622a\u5668\uff1a<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>public class LoginInterceptor implements HandlerInterceptor {<br>    @Override<br>    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {<br>        \/\/1.\u83b7\u53d6\u7528\u6237<br>        Object user =UserHolder.<em>getUser<\/em>();<br>        \/\/2.\u82e5\u7528\u6237\u4e3a\u7a7a\uff0c\u8fdb\u884c\u62e6\u622a<br>        if (user==null){<br>            response.setStatus(401);<br>            return false;<br>        }<br>        \/\/3.\u4e0d\u7a7a\uff0c\u653e\u884c<br>        return true;<br>    }<br><br>}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\u62e6\u622a\u5668\u6ce8\u518c\uff1a<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>@Configuration<br>public class MvcConfig implements WebMvcConfigurer {<br>    @Autowired<br>    RedisTemplate redisTemplate;<br>    @Override<br>    public void addInterceptors(InterceptorRegistry registry) {<br>        <br>        \/\/1<br>        registry.addInterceptor(new RefreshTokenInterceptor(redisTemplate));<br>        \/\/2<br>        registry.addInterceptor(new LoginInterceptor())<br>                .excludePathPatterns(<br>                        \"\/shop\/**\",<br>                        \"\/voucher\/**\",<br>                        \"\/shop-type\/**\",<br>                        \"\/upload\/**\",<br>                        \"\/blog\/hot\",<br>                        \"\/user\/code\",<br>                        \"\/user\/login\"<br>                );<br>    }<br>}<\/code><\/pre>\n\n\n\n<h1 class=\"wp-block-heading\">2 \u5e97\u94fa\u6570\u636e\u67e5\u8be2\u7f13\u5b58<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">2.1 \u7f13\u5b58\uff1a<\/h2>\n\n\n\n<p><strong>\u7c7b\u6bd4\u7406\u89e3\uff1aCPU &#8212; Cache &#8212; \u5185\u5b58  \uff1b tomcat &#8212; Redis &#8212;Mysql<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u7f13\u5b58\u4f5c\u7528\uff1a\n<ul class=\"wp-block-list\">\n<li>\u964d\u4f4e\u540e\u7aef\uff08\u6570\u636e\u5e93\uff09\u8d1f\u8f7d<\/li>\n\n\n\n<li>\u63d0\u9ad8\u8bfb\u5199\u6548\u7387\uff0c\u964d\u4f4e\u54cd\u5e94\u65f6\u95f4<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>\u7f13\u5b58\u6210\u672c\uff1a\n<ul class=\"wp-block-list\">\n<li>\u6570\u636e\u4e00\u81f4\u6027<\/li>\n\n\n\n<li>\u4ee3\u7801\u7ef4\u62a4\u3001\u8fd0\u7ef4<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">2.2 \u6dfb\u52a0Redis\u7f13\u5b58\uff1a<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>public Result queryById(Long id) {\n        String key = CACHE_SHOP_KEY+id;\n        \/\/1.\u5728\u7f13\u5b58\u4e2d\u5bfb\u627e\n        String shopJson = stringRedisTemplate.opsForValue().get(key);\n        \/\/2.\u6ca1\u627e\u5230\uff0c\u67e5\u6570\u636e\u5e93\n        if (StrUtil.isNotBlank(shopJson)){\n            \/\/\u627e\u5230\u5c31\u76f4\u63a5\u8fd4\u56de\n            Shop shop = JSONUtil.toBean(shopJson,Shop.class);\n            return Result.ok(shop);\n        }\n\n        Shop shop= getById(id);\n        \/\/3.\u82e5\u4e0d\u5b58\u5728\uff0c\u8fd4\u56de\u9519\u8bef\n        if (shop==null){\n            return  Result.fail(\"\u5e97\u94fa\u4e0d\u5b58\u5728\uff01\");\n        }\n\n        \/\/4.\u5199\u5165\u7f13\u5b58\n        stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop));\n\n        \/\/5.\u8fd4\u56de\n        return Result.ok(shop);\n    }<\/code><\/pre>\n\n\n\n<p>\u6ce8\u610f\u53d1\u751f\u7f13\u5b58\u672a\u547d\u4e2d\uff0c\u67e5\u5b8c\u6570\u636e\u5e93\u8981\u518d\u6b21\u5199\u5165\u7f13\u5b58\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u7ec3\u4e60\uff1a\u7ed9\u5546\u94fa\u7c7b\u578b\u6dfb\u52a0\u7f13\u5b58\uff1a<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>public List&lt;ShopType&gt; queryList() {<br>    String key = \"cache:shopTypeList\";<br>    \/\/1.\u5728\u7f13\u5b58\u4e2d\u67e5\u627e<br>    String typeListJson = stringRedisTemplate.opsForValue().get(key);<br><br>    \/\/2.\u6ca1\u627e\u5230\u4ece\u6570\u636e\u5e93\u627e\uff0c\u627e\u5230\u76f4\u63a5\u8fd4\u56de<br>    if (StrUtil.<em>isNotBlank<\/em>(typeListJson)){<br>        return JSONUtil.<em>toList<\/em>(typeListJson, ShopType.class);<br>    }<br><br>    List&lt;ShopType&gt; typeList=query().orderByAsc(\"sort\").list();<br>    \/\/3.\u5199\u5165\u7f13\u5b58\u5e76\u8fd4\u56de<br>    stringRedisTemplate.opsForValue().set(key,JSONUtil.<em>toJsonStr<\/em>(typeList));<br><br>    return typeList;<br>}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">2.3 \u7f13\u5b58\u66f4\u65b0\u7b56\u7565\uff1a<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th><\/th><th>\u5185\u5b58\u6dd8\u6c70<\/th><th>\u8d85\u65f6\u5254\u9664<\/th><th>\u4e3b\u52a8\u66f4\u65b0<\/th><\/tr><\/thead><tbody><tr><td><strong>\u8bf4\u660e<\/strong><\/td><td>\u4e0d\u7528\u81ea\u5df1\u7ef4\u62a4\uff0c\u5229\u7528Redis\u7684\u5185\u5b58\u6dd8\u6c70\u673a\u5236\uff0c\u5f53\u5185\u5b58\u4e0d\u8db3\u65f6\u81ea\u52a8\u6dd8\u6c70\u90e8\u5206\u6570\u636e\u3002\u4e0b\u6b21\u67e5\u8be2\u65f6\u66f4\u65b0\u7f13\u5b58\u3002<\/td><td>\u7ed9\u7f13\u5b58\u6570\u636e\u6dfb\u52a0TTL\u65f6\u95f4\uff0c\u5230\u671f\u540e\u81ea\u52a8\u5220\u9664\u7f13\u5b58\u3002\u4e0b\u6b21\u67e5\u8be2\u65f6\u66f4\u65b0\u7f13\u5b58\u3002<\/td><td>\u7f16\u5199\u4e1a\u52a1\u903b\u8f91\uff0c\u5728\u4fee\u6539\u6570\u636e\u5e93\u7684\u540c\u65f6\uff0c\u66f4\u65b0\u7f13\u5b58\u3002<\/td><\/tr><tr><td><strong>\u4e00\u81f4\u6027<\/strong><\/td><td>\u5dee<\/td><td>\u4e00\u822c<\/td><td>\u597d<\/td><\/tr><tr><td><strong>\u7ef4\u62a4\u6210\u672c<\/strong><\/td><td>\u65e0<\/td><td>\u4f4e<\/td><td>\u9ad8<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p><br>\u4e3b\u52a8\u66f4\u65b0\u7684\u7c7b\u578b\uff1a<br><\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u7b56\u7565\u7c7b\u578b<\/th><th>\u5b9e\u73b0\u65b9\u5f0f<\/th><th>\u4e00\u81f4\u6027<\/th><th>\u7ef4\u62a4\u6210\u672c<\/th><th>\u9002\u7528\u573a\u666f<\/th><\/tr><\/thead><tbody><tr><td><strong>Cache Aside<\/strong><br><strong>\u65c1\u8def\u7f13\u5b58\u6a21\u5f0f<\/strong><\/td><td>\u5e94\u7528\u4ee3\u7801\u4e2d\u663e\u5f0f\u5904\u7406\uff1a1. \u8bfb\u65f6\u5148\u67e5\u7f13\u5b58\uff0c\u672a\u547d\u4e2d\u5219\u8bfbDB\u5e76\u56de\u586b 2. \u5199\u65f6\u66f4\u65b0DB\u540e<strong>\u5220\u9664<\/strong>\u7f13\u5b58<\/td><td>\u6700\u597d<\/td><td>\u9ad8<\/td><td>\u901a\u7528\u573a\u666f<\/td><\/tr><tr><td><strong>Write Through<\/strong><br>\u5199\u7a7f\u6cd5<\/td><td>\u7f13\u5b58\u4e0eDB\u7ed1\u5b9a\uff1a\u6240\u6709\u5199\u64cd\u4f5c\u5fc5\u987b\u540c\u65f6\u66f4\u65b0\u7f13\u5b58\u548cDB\uff08\u901a\u5e38\u901a\u8fc7\u5b58\u50a8\u5c42\u63d2\u4ef6\u5b9e\u73b0\uff09<\/td><td>\u597d<\/td><td>\u975e\u5e38\u9ad8<\/td><td>\u5199\u5bc6\u96c6\u578b\u7cfb\u7edf<\/td><\/tr><tr><td><strong>Write Behind<\/strong><br>\u5199\u56de\u6cd5<\/td><td>\u5148\u66f4\u65b0\u7f13\u5b58\uff0c\u5f02\u6b65\u6279\u91cf\u5199DB\uff08\u9700\u7ef4\u62a4\u64cd\u4f5c\u65e5\u5fd7\u9632\u4e22\u5931\uff09<\/td><td>\u4e00\u822c<\/td><td>\u6781\u9ad8<\/td><td>\u8d85\u9ad8\u541e\u5410\u91cf\u7cfb\u7edf\uff08\u5982\u7535\u5546\u79d2\u6740\uff09<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p><strong>Cache Aside<\/strong> \u5173\u6ce8\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u5220\u9664\u7f13\u5b58<\/strong>\uff1a\u66f4\u65b0\u6570\u636e\u5e93\u65f6\u8ba9\u7f13\u5b58\u5931\u6548\uff0c\u67e5\u8be2\u65f6\u518d\u5199\u5165\u7f13\u5b58\u3002<\/li>\n\n\n\n<li>\u7f13\u5b58\u4e0e\u6570\u636e\u5e93\u64cd\u4f5c\u539f\u5b50\u6027\uff1a\n<ul class=\"wp-block-list\">\n<li>\u5355\u4f53\u7cfb\u7edf\uff1a\u653e\u5728\u4e00\u4e2a\u4e8b\u7269<\/li>\n\n\n\n<li>\u5206\u5e03\u5f0f\uff1aTCC\u7b49\u5206\u5e03\u5f0f\u4e8b\u52a1\u65b9\u6848<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>\u5148\u5220\u7f13\u5b58\u8fd8\u662f\u5148\u6539\u6570\u636e\u5e93\uff1f\n<ul class=\"wp-block-list\">\n<li>\u5148\u64cd\u4f5c\u6570\u636e\u5e93\uff0c\u518d\u5220\u9664\u7f13\u5b58\u3002\uff08\u4e00\u81f4\u6027\u95ee\u9898\u53ef\u80fd\u6027\u4f4e\uff09<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<p><\/p>\n\n\n\n<p>\u4e00\u822c\u5e94\u7528\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u4f4e\u4e00\u81f4\u6027\u9700\u6c42\uff1a\u4f7f\u7528Redis\u81ea\u5e26\u7684\u5185\u5b58\u6dd8\u6c70\u673a\u5236<\/li>\n\n\n\n<li>\u9ad8\u4e00\u81f4\u6027\u9700\u6c42\uff1a<strong>\u4e3b\u52a8\u66f4\u65b0\u7684Cache-Aside \u4e3a\u4e3b\uff0c\u4e3b\u52a8\u5220\u9664\u7f13\u5b58\u4e3a\u6838\u5fc3\uff0c\u8fc7\u671f\u65f6\u95f4\u4e3a\u515c\u5e95\uff0c\u7279\u6b8a\u573a\u666f\u52a0\u9501 \/ \u5f02\u6b65\u8865\u507f<\/strong>\n<ul class=\"wp-block-list\">\n<li>\u8bfb\u64cd\u4f5c\uff1a\n<ul class=\"wp-block-list\">\n<li>\u7f13\u5b58\u547d\u4e2d\u76f4\u63a5\u8fd4\u56de\u3002<\/li>\n\n\n\n<li>\u7f13\u5b58\u672a\u547d\u4e2d\u5219\u67e5\u8be2\u6570\u636e\u5e93\uff0c\u5e76\u5199\u5165\u7f13\u5b58\uff0c\u8bbe\u5b9a\u8d85\u65f6\u65f6\u95f4\u3002<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>\u5199\u64cd\u4f5c\uff1a\n<ul class=\"wp-block-list\">\n<li>\u5148\u5199\u6570\u636e\u5e93\uff0c\u5728\u5220\u9664\u7f13\u5b58\u3002<\/li>\n\n\n\n<li>\u8981\u786e\u4fdd\u6570\u636e\u5e93\u4e0e\u7f13\u5b58\u64cd\u4f5c\u7684\u539f\u5b50\u6027<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u7ec3\u4e60\uff1a\u7ed9\u67e5\u8be2\u3001\u4fee\u6539\u5546\u94fa\u5e94\u7528\u4e3b\u52a8\u66f4\u65b0+\u8d85\u65f6\u5254\u9664\u7684\u7f13\u5b58\u66f4\u65b0\u7b56\u7565\uff1a<\/h2>\n\n\n\n<pre class=\"wp-block-preformatted\">queryById\u4e2d\uff1a<\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/4.\u5199\u5165\u7f13\u5b58\n        stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop),CACHE_SHOP_TTL, TimeUnit.MINUTES);<\/code><\/pre>\n\n\n\n<p>\u4fee\u6539\u5546\u94fa\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public Result updateShopById(Shop shop) {<br>    if (shop.getId()==null){<br>        return Result.<em>fail<\/em>(\"shopId\u4e0d\u5b58\u5728\");<br>    }<br>    String key = <em>CACHE_SHOP_KEY<\/em>+shop.getId();<br>    \/\/1.\u66f4\u65b0\u6570\u636e\u5e93<br>    updateById(shop);<br>    \/\/2.\u5220\u9664\u7f13\u5b58<br>    stringRedisTemplate.delete(key);<br><br>    return Result.<em>ok<\/em>();<br>}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">2.4 \u7f13\u5b58\u7a7f\u900f<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udccc \u573a\u666f\u63cf\u8ff0\uff1a<\/h3>\n\n\n\n<p>\u7528\u6237\u8bf7\u6c42\u4e00\u4e2a <strong>\u6570\u636e\u5e93\u4e2d\u6839\u672c\u4e0d\u5b58\u5728<\/strong> \u7684\u6570\u636e\uff0c\u4f8b\u5982 <code>GET \/user?id=999999<\/code>\u3002\u7531\u4e8e Redis \u4e2d\u4e5f\u6ca1\u6709\u7f13\u5b58\uff0c\u7cfb\u7edf\u4f1a\u7ee7\u7eed\u67e5\u6570\u636e\u5e93\uff0c\u53d1\u73b0\u4e5f\u6ca1\u6709\uff0c\u5c31\u8fd4\u56de\u7a7a\u3002<\/p>\n\n\n\n<p>\u8fd9\u672c\u6ca1\u95ee\u9898\uff0c\u4f46\u5982\u679c<strong>\u6076\u610f\u8bf7\u6c42\u8005\u9891\u7e41\u8bf7\u6c42\u5927\u91cf\u4e0d\u5b58\u5728\u7684\u6570\u636e<\/strong>\uff0cRedis \u7f13\u5b58\u6bcf\u6b21\u90fd miss\uff0c\u800c\u6570\u636e\u5e93\u8981\u4e00\u6b21\u6b21\u201c\u767d\u5fd9\u6d3b\u201d\u5904\u7406\u8fd9\u4e9b\u8bf7\u6c42\uff0c\u7b49\u4e8e\u7f13\u5b58\u5b8c\u5168\u6ca1\u7528\uff01<\/p>\n\n\n\n<p>\u8fd9\u5c31\u662f\u7f13\u5b58\u7a7f\u900f\uff1a<strong>\u8bf7\u6c42\u7ed5\u8fc7\u7f13\u5b58\u76f4\u51fb\u6570\u636e\u5e93\uff0c\u5f62\u6210\u7a7f\u900f\u653b\u51fb\u6216\u8d44\u6e90\u6d6a\u8d39\u3002<\/strong><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u5e38\u89c1\u7684\u51e0\u79cd\u89e3\u51b3\u65b9\u5f0f<\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u65b9\u6848<\/th><th>\u63cf\u8ff0<\/th><th>\u4f18\u7f3a\u70b9<\/th><\/tr><\/thead><tbody><tr><td>\u7f13\u5b58 \u7a7a\u5bf9\u8c61\uff1anull \u503c<\/td><td>\u5728\u6570\u636e\u5e93\u4e2d\u672a\u627e\u5230\u65f6\u5728\u7f13\u5b58\u4e2d\u5b58\u50a8null<\/td><td>\u4f18\u70b9\uff1a\u5b9e\u73b0\u3001\u7ef4\u62a4\u7b80\u5355<br>\u7f3a\u70b9\uff1a\u989d\u5916\u5185\u5b58\u6d88\u8017\u3001\u77ed\u671f\u4e0d\u4e00\u81f4<\/td><\/tr><tr><td>\u5e03\u9686\u8fc7\u6ee4\u5668<\/td><td>\u4e8b\u5148\u628a\u6240\u6709\u5408\u6cd5 key \u653e\u8fdb\u53bb\uff0c\u975e\u6cd5\u7684\u76f4\u63a5\u8fc7\u6ee4\u6389\uff0c\u672c\u8d28\u662f          <strong>\u591a\u4e2ahash+bitmap<\/strong><\/td><td>\u4f18\u70b9\uff1a\u5185\u5b58\u5360\u7528\u5c11\uff0c\u6ca1\u6709\u591a\u4f59key<br>\u7f3a\u70b9\uff1a\u5b9e\u73b0\u590d\u6742\uff0c\u5b58\u5728\u8bef\u5224\u53ef\u80fd<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">\u7ec3\u4e60\uff1a\u5e97\u94fa\u67e5\u8be2\u5e94\u7528\u7f13\u5b58\u7a7a\u5bf9\u8c61\u4ee5\u9632\u6b62\u7f13\u5b58\u7a7f\u900f<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>public Result queryById(Long id) {<br>    String key = <em>CACHE_SHOP_KEY<\/em>+id;<br>    \/\/1.\u5728\u7f13\u5b58\u4e2d\u5bfb\u627e<br>    String shopJson = stringRedisTemplate.opsForValue().get(key);<br><br><br><br>    \/\/2.\u5224\u65ad\u7f13\u5b58\u4e2d\u662f\u5426\u5b58\u5728<br>    if (StrUtil.<em>isNotBlank<\/em>(shopJson)){<br>        \/\/\u627e\u5230\u5c31\u76f4\u63a5\u8fd4\u56de<br>        Shop shop = JSONUtil.<em>toBean<\/em>(shopJson,Shop.class);<br>        return Result.<em>ok<\/em>(shop);<br>    }<br><br>    \/\/3.\u5904\u7406\u547d\u4e2d\u7f13\u5b58\u7a7f\u900f\u7684\u201c\u201d\u7a7a\u503c<br>    if (shopJson!=null&amp;&amp;shopJson.equals(\"\")){<br>        \/\/\u8fd4\u56de\u9519\u8bef\u4fe1\u606f<br>        return  Result.<em>fail<\/em>(\"\u5e97\u94fa\u4e0d\u5b58\u5728\uff01\");<br>    }<br><br>    \/\/4.\u7f13\u5b58\u4e2d\u4e0d\u5b58\u5728\uff0c\u67e5\u627e\u6570\u636e\u5e93<br>    Shop shop= getById(id);<br><br>    \/\/5.\u82e5\u6839\u672c\u4e0d\u5b58\u5728\uff0c\u8fd4\u56de\u9519\u8bef<br>    if (shop==null){<br>        \/\/\u5c06\u7a7a\u503c\u5199\u5165redis\uff0c\u9632\u6b62\u7f13\u5b58\u7a7f\u900f<br>        stringRedisTemplate.opsForValue().set(key,\"\",<em>CACHE_NULL_TTL<\/em>,TimeUnit.<em>MINUTES<\/em>);<br>        \/\/\u8fd4\u56de\u9519\u8bef<br>        return  Result.<em>fail<\/em>(\"\u5e97\u94fa\u4e0d\u5b58\u5728\uff01\");<br>    }<br><br>    \/\/6.\u5199\u5165\u7f13\u5b58<br>    stringRedisTemplate.opsForValue().set(key,JSONUtil.<em>toJsonStr<\/em>(shop),<em>CACHE_SHOP_TTL<\/em>, TimeUnit.<em>MINUTES<\/em>);<br><br>    \/\/7.\u8fd4\u56de<br>    return Result.<em>ok<\/em>(shop);<br>}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">2.5 \u7f13\u5b58\u96ea\u5d29<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">\u2744\ufe0f \u573a\u666f\u63cf\u8ff0\uff1a<\/h3>\n\n\n\n<p>\u201c\u672c\u6765\u4e00\u5207\u5b89\u597d\uff0c\u7a81\u7136 Redis \u6302\u4e86\u6216\u67d0\u4e00\u7c7b\u7f13\u5b58\u540c\u4e00\u65f6\u95f4\u5168\u90e8\u5931\u6548\uff0c\u5927\u91cf\u8bf7\u6c42\u540c\u65f6\u6d8c\u5165\u6570\u636e\u5e93\uff0c\u538b\u529b\u76f4\u63a5\u96ea\u5d29\uff01\u201d<\/p>\n\n\n\n<p><strong>\u5927\u91cf\u7f13\u5b58\u540c\u4e00\u65f6\u523b\u5931\u6548\uff0c\u5bfc\u81f4\u6570\u636e\u5e93\u538b\u529b\u9aa4\u589e\u3001\u670d\u52a1\u53ef\u80fd\u5d29\u6e83\u7684\u73b0\u8c61<\/strong>\uff0c\u79f0\u4e3a <strong>\u7f13\u5b58\u96ea\u5d29\uff08Cache Avalanche\uff09<\/strong>\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u95ee\u9898\u5206\u7c7b\uff1a<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u5927\u91cf Key \u540c\u65f6\u8fc7\u671f\uff1a<\/strong><br>\u5f00\u53d1\u65f6\u6240\u6709\u7f13\u5b58\u8bbe\u7f6e\u4e86\u7edf\u4e00\u8fc7\u671f\u65f6\u95f4\uff0c\u5bfc\u81f4\u67d0\u4e00\u65f6\u523b\u5927\u9762\u79ef\u5931\u6548<\/li>\n\n\n\n<li><strong>\u70ed\u70b9\u6570\u636e\u96c6\u4e2d\u5931\u6548\uff1a<\/strong><br>\u70ed\u70b9\u6570\u636e\uff08\u5982\u9996\u9875\u63a8\u8350\u3001\u70ed\u95e8\u5546\u54c1\uff09\u7f13\u5b58\u7a81\u7136\u8fc7\u671f\uff0c\u8bbf\u95ee\u77ac\u65f6\u6253\u6ee1\u6570\u636e\u5e93\u3002<\/li>\n\n\n\n<li><strong>Redis \u670d\u52a1\u5b95\u673a<\/strong>\uff1a<br>Redis \u6574\u4f53\u4e0d\u53ef\u7528\uff0c\u6240\u6709\u7f13\u5b58\u5c42\u5931\u6548\uff0c\u8bf7\u6c42\u5168\u90e8\u6253\u5230\u6570\u636e\u5e93\u3002<\/li>\n<\/ul>\n\n\n\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u89e3\u51b3\u65b9\u6848\uff1a<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u7ed9\u4e0d\u540c\u7684 Key \u7684 TTL \u6dfb\u52a0\u968f\u673a\u503c<\/li>\n\n\n\n<li>\u5229\u7528 Redis \u96c6\u7fa4\u63d0\u9ad8\u670d\u52a1\u7684\u53ef\u7528\u6027<\/li>\n\n\n\n<li>\u7ed9\u7f13\u5b58\u4e1a\u52a1\u6dfb\u52a0\u964d\u7ea7\u9650\u6d41\u7b56\u7565<\/li>\n\n\n\n<li>\u7ed9\u4e1a\u52a1\u6dfb\u52a0\u591a\u7ea7\u7f13\u5b58<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">2.6 \u7f13\u5b58\u51fb\u7a7f<\/h2>\n\n\n\n<p><strong>\u7f13\u5b58\u51fb\u7a7f\u95ee\u9898<\/strong>\u4e5f\u53eb<strong>\u70ed\u70b9key\u95ee\u9898<\/strong>\uff0c\u5c31\u662f\u4e00\u4e2a\u88ab<strong>\u9ad8\u5e76\u53d1\u8bbf\u95ee<\/strong>\u5e76\u4e14<strong>\u7f13\u5b58\u91cd\u5efa\u4e1a\u52a1\u8f83\u590d\u6742<\/strong>\u7684key\u7a81\u7136\u5931\u6548\u4e86\uff0c\u65e0\u6570\u7684\u8bf7\u6c42\u8bbf\u95ee\u4f1a\u5728\u77ac\u95f4\u7ed9\u6570\u636e\u5e93\u5e26\u6765\u5de8\u5927\u7684\u6253\u51fb\u3002<\/p>\n\n\n\n<p>\u4e24\u79cd\u65b9\u6848\uff1a<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u5bf9\u6bd4\u9879<\/th><th>\u52a0\u9501\u65b9\u6848<\/th><th>\u903b\u8f91\u8fc7\u671f\u65b9\u6848<\/th><\/tr><\/thead><tbody><tr><td><strong>\u6838\u5fc3\u601d\u60f3<\/strong><\/td><td>\u9ad8\u5e76\u53d1\u65f6\u53ea\u6709\u4e00\u4e2a\u7ebf\u7a0b\u67e5\u8be2\u6570\u636e\u5e93\u5e76\u5199\u5165\u7f13\u5b58\uff0c\u5176\u4ed6\u7ebf\u7a0b\u7b49\u5f85<\/td><td>\u7f13\u5b58\u6570\u636e\u8bbe\u5b9a\u201c\u903b\u8f91\u8fc7\u671f\u65f6\u95f4\u201d\uff0c\u8fc7\u671f\u540e\u5f02\u6b65\u66f4\u65b0\uff0c\u65e7\u503c\u4ecd\u53ef\u8fd4\u56de<\/td><\/tr><tr><td><strong>\u5b9e\u73b0\u65b9\u5f0f<\/strong><\/td><td>\u4f7f\u7528\u4e92\u65a5\u9501\uff08\u5982\u5206\u5e03\u5f0f\u9501\uff1aRedis setnx + TTL\uff09<\/td><td>\u7f13\u5b58\u4e2d\u5305\u542b\u6570\u636e\u4e0e\u8fc7\u671f\u65f6\u95f4\uff0c\u540e\u53f0\u7ebf\u7a0b\u5b9a\u671f\u5f02\u6b65\u5237\u65b0<\/td><\/tr><tr><td><strong>\u662f\u5426\u963b\u585e\u8bf7\u6c42<\/strong><\/td><td>\u662f\uff0c\u53ef\u80fd\u4f1a\u77ed\u6682\u963b\u585e\u5176\u4ed6\u7ebf\u7a0b<\/td><td>\u5426\uff0c\u8bf7\u6c42\u6c38\u8fdc\u80fd\u8fd4\u56de\u65e7\u7f13\u5b58\uff0c\u66f4\u65b0\u5f02\u6b65\u8fdb\u884c<\/td><\/tr><tr><td><strong>\u9002\u5408\u6570\u636e\u7c7b\u578b<\/strong><\/td><td>\u5c0f\u89c4\u6a21\u3001\u8bbf\u95ee\u9891\u7387\u96c6\u4e2d\u3001\u5bf9\u4e00\u81f4\u6027\u8981\u6c42\u9ad8\u7684\u6570\u636e<\/td><td>\u8bbf\u95ee\u9891\u7e41\u3001\u5bf9\u65f6\u6548\u6027\u8981\u6c42\u4e0d\u9ad8\u3001\u5141\u8bb8\u77ed\u65f6\u95f4\u4f7f\u7528\u65e7\u6570\u636e<\/td><\/tr><tr><td><strong>\u6570\u636e\u5e93\u538b\u529b<\/strong><\/td><td>\u8f83\u4f4e\uff08\u53ea\u4e00\u4e2a\u7ebf\u7a0b\u8bbf\u95ee DB\uff09<\/td><td>\u975e\u9ad8\u5cf0\u671f\u540e\u53f0\u5f02\u6b65\u5237\u65b0\uff0c\u4e0d\u96c6\u4e2d\u8bbf\u95ee DB\uff0c\u538b\u529b\u66f4\u5e73\u7a33<\/td><\/tr><tr><td><strong>\u4e00\u81f4\u6027\u4fdd\u8bc1<\/strong><\/td><td>\u5f3a\u4e00\u81f4\u6027\uff08\u7f13\u5b58\u66f4\u65b0\u524d\u5176\u4ed6\u7ebf\u7a0b\u963b\u585e\uff09<\/td><td>\u6700\u7ec8\u4e00\u81f4\u6027\uff08\u65e7\u6570\u636e\u77ed\u65f6\u95f4\u5185\u53ef\u80fd\u5b58\u5728\uff09<\/td><\/tr><tr><td><strong>\u5b9e\u73b0\u590d\u6742\u5ea6<\/strong><\/td><td>\u8f83\u7b80\u5355\uff08\u52a0\u9501\u5904\u7406\u5373\u53ef\uff09<\/td><td>\u7565\u590d\u6742\uff08\u9700\u7ef4\u62a4\u8fc7\u671f\u65f6\u95f4\u3001\u540e\u53f0\u5237\u65b0\u7ebf\u7a0b\u6216\u4efb\u52a1\uff09<\/td><\/tr><tr><td><strong>\u70ed\u70b9\u6570\u636e\u8868\u73b0<\/strong><\/td><td>\u6709\u6548\u907f\u514d\u7f13\u5b58\u51fb\u7a7f<\/td><td>\u6709\u6548\u907f\u514d\u7f13\u5b58\u51fb\u7a7f<\/td><\/tr><tr><td><strong>\u793a\u4f8b\u5e94\u7528\u573a\u666f<\/strong><\/td><td>\u5546\u54c1\u8be6\u60c5\u9875\u3001\u79d2\u6740\u5e93\u5b58\u3001\u7528\u6237\u4fe1\u606f\u7b49<\/td><td>\u9996\u9875\u63a8\u8350\u3001\u6392\u884c\u699c\u3001\u5e97\u94fa\u8bc4\u5206\u7b49\u5141\u8bb8\u6570\u636e\u77ed\u6682\u8fc7\u671f\u7684\u573a\u666f<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">\u52a0\u9501\u65b9\u6848\uff1a<\/h3>\n\n\n\n<p>\u8fd9\u4e2a\u4e92\u65a5\u9501\u66f4\u9002\u5408\u4f7f\u7528<strong>\u975e\u963b\u585e\u7684\u81ea\u65cb\u9501\u903b\u8f91<\/strong>\uff08\u5982 Redis SETNX \u5b9e\u73b0\u7684\u5206\u5e03\u5f0f\u9501\uff09\u800c\u975ejava\u81ea\u5e26\u7684<code>synchronized<\/code> <strong>\u963b\u585e\u5f0f\u9501<\/strong>\u3002<\/p>\n\n\n\n<p><strong>\u9501\u600e\u4e48\u91ca\u653e\uff1f\u4e3b\u52a8\u91ca\u653e+\u6dfb\u52a0\u6709\u6548\u671f<\/strong><\/p>\n\n\n\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u903b\u8f91\u8fc7\u671f\u65b9\u6848\uff1a<\/h3>\n\n\n\n<p>\u201c\u8fc7\u671f\u201d\u662f\u5728\u4e1a\u52a1\u5c42\u4e2d\u800c\u975eredis\u4e2d\u5b9e\u73b0\u7684\uff0c\u8fd9\u4e2a\u503c\u4f1a\u63d0\u524d\u52a0\u8f7d\u5230\u7f13\u5b58\u4e2d\u5e76\u4e00\u76f4\u5b58\u5728\u4e8eredis\u4e2d\u3002\u82e5\u5728redis\u4e2d\u672a\u547d\u4e2d\uff0c\u76f4\u63a5\u8fd4\u56denull\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u7ec3\u4e60\uff1a\u5229\u7528\u4e92\u65a5\u9501\u89e3\u51b3\u7f13\u5b58\u51fb\u7a7f<\/h2>\n\n\n\n<p><strong>\u793a\u4f8b\uff1a\u4fee\u6539\u57fa\u4e8eid\u67e5\u8be2\u5e97\u94fa\u7684\u4e1a\u52a1\u3001\u57fa\u4e8e\u4e92\u65a5\u9501\u65b9\u5f0f\u89e3\u51b3\u7f13\u5b58\u51fb\u7a7f\u95ee\u9898\uff1a<\/strong><\/p>\n\n\n\n<p>\u83b7\u53d6\u9501\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>private boolean tryLock(String key){\n        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, \"1\", 10, TimeUnit.SECONDS);\n        \/\/\u4e0d\u76f4\u63a5\u8fd4\u56de\u662f\u4e3a\u4e86\u9632\u6b62\u81ea\u52a8\u89e3\u5305\u65f6\u51fa\u73b0\u5f02\u6837\u5bfc\u81f4\u8fd4\u56denull\n        return BooleanUtil.isTrue(flag);\n    }<\/code><\/pre>\n\n\n\n<p>\u91ca\u653e\u9501\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>private void unLock(String key){\n        stringRedisTemplate.delete(key);\n    }<\/code><\/pre>\n\n\n\n<p>\u4e1a\u52a1\u5b9e\u73b0\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/\u4e92\u65a5\u9501\u89e3\u51b3\u7f13\u5b58\u51fb\u7a7f\uff08\u5e26\u4e86\u7f13\u5b58\u7a7f\u900f\uff09\npublic Shop queryByIdWithMutex(Long id){\n    String key = <em>CACHE_SHOP_KEY<\/em>+id;\n    \/\/1.\u5728\u7f13\u5b58\u4e2d\u5bfb\u627e\n    String shopJson = stringRedisTemplate.opsForValue().get(key);\n\n    \/\/2.\u5224\u65ad\u7f13\u5b58\u4e2d\u662f\u5426\u5b58\u5728\n    if (StrUtil.<em>isNotBlank<\/em>(shopJson)){\n        \/\/\u627e\u5230\u5c31\u76f4\u63a5\u8fd4\u56de\n        Shop shop = JSONUtil.<em>toBean<\/em>(shopJson,Shop.class);\n        return shop;\n    }\n\n    \/\/\u5904\u7406\u547d\u4e2d\u7f13\u5b58\u7a7f\u900f\u7684\u201c\u201d\u7a7a\u503c\uff08\u7f13\u5b58\u7a7f\u900f\uff09\n    if (shopJson!=null&amp;&amp;shopJson.equals(\"\")){\n        \/\/\u8fd4\u56de\u9519\u8bef\u4fe1\u606f\n        return  null;\n    }\n\n    \/\/3.\u6ca1\u6709\u627e\u5230\uff0c\u5f00\u59cb\u7f13\u5b58\u91cd\u5efa\n    \/\/4\u83b7\u53d6\u4e92\u65a5\u9501\n    String lockKey=<em>LOCK_SHOP_KEY<\/em>+id;\n\n    Shop shop= null;\n    try {\n        if (!tryLock(lockKey)){\n            \/\/5.1\u6ca1\u6709\u83b7\u53d6\u5230\uff0c\u4f11\u7720\u4e00\u6bb5\u65f6\u95f4\u518d\u6b21\u67e5\u8be2\u7f13\u5b58\uff08\u81ea\u65cb\uff09\n            \/\/\u81ea\u65cb\u5b9e\u73b0\u5efa\u8bae\u4f7f\u7528\u5faa\u73af\u800c\u975e\u9012\u5f52\n            while (true){\n                Thread.<em>sleep<\/em>(50);\n                \/\/1.\u5728\u7f13\u5b58\u4e2d\u5bfb\u627e\n                shopJson = stringRedisTemplate.opsForValue().get(key);\n\n                \/\/2.\u5224\u65ad\u7f13\u5b58\u4e2d\u662f\u5426\u5b58\u5728\n                if (StrUtil.<em>isNotBlank<\/em>(shopJson)){\n                    \/\/\u627e\u5230\u5c31\u76f4\u63a5\u8fd4\u56de\n                    shop = JSONUtil.<em>toBean<\/em>(shopJson,Shop.class);\n                    return shop;\n                }\n            }\n        }\n\n        \/\/5.2.\u83b7\u53d6\u5230\u4e86lock\uff0c\u67e5\u6570\u636e\u5e93\n        shop = getById(id);\n        if (shop==null){\n            \/\/\u5c06\u7a7a\u503c\u5199\u5165redis\uff0c\u9632\u6b62\u7f13\u5b58\u7a7f\u900f\n            stringRedisTemplate.opsForValue().set(key,\"\",<em>CACHE_NULL_TTL<\/em>,TimeUnit.<em>MINUTES<\/em>);\n            \/\/\u8fd4\u56de\u9519\u8bef\n            return  null;\n        }\n        \/\/6.\u5199\u5165\u7f13\u5b58\n        stringRedisTemplate.opsForValue().set(key,JSONUtil.<em>toJsonStr<\/em>(shop),LOCK_SHOP_TTL, TimeUnit.<em>MINUTES<\/em>);\n\n\n    } catch (InterruptedException e) {\n        throw new RuntimeException(e);\n    } finally {\n        \/\/7.\u91ca\u653e\u9501\n        unLock(lockKey);\n    }\n\n    \/\/8.\u8fd4\u56de\n    return shop;\n}<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u7ec3\u4e60\uff1a\u5229\u7528\u903b\u8f91\u8fc7\u671f\u89e3\u51b3\u7f13\u5b58\u51fb\u7a7f<\/h2>\n\n\n\n<p><strong>\u793a\u4f8b\uff1a<strong>\u4fee\u6539\u57fa\u4e8eid\u67e5\u8be2\u5e97\u94fa\u7684\u4e1a\u52a1<\/strong>\u3001\u57fa\u4e8e\u903b\u8f91\u8fc7\u671f\u65b9\u5f0f\u89e3\u51b3\u7f13\u5b58\u51fb\u7a7f\u95ee\u9898\uff1a<\/strong><\/p>\n\n\n\n<p>\u7531\u4e8e\u9700\u8981\u4fdd\u5b58\u65f6\u95f4\uff0c\u6709\u5de5\u5177\u7c7b\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@Data\npublic class RedisData {\n    private LocalDateTime expireTime;\n    private Object data;\n}<\/code><\/pre>\n\n\n\n<p>\u4e1a\u52a1\u4ee3\u7801\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>...\n\/\/\u7f13\u5b58\u6c60\uff0c\u6ce8\u610f\uff1a\u6b64\u5904\u4e0d\u7b26\u5408\u300a\u963f\u91cc\u5df4\u5df4 Java \u5f00\u53d1\u624b\u518c\u300b\u7684\u63a8\u8350\uff08\u4e00\u822c\u4f7f\u7528 ThreadPoolExecutor \u660e\u786e\u53c2\u6570\uff09\nprivate ExecutorService cacheRebuildExecutor = Executors.newFixedThreadPool(10);\n...\n\n\/\/\u903b\u8f91\u8fc7\u671f\u89e3\u51b3\u7f13\u5b58\u51fb\u7a7f\n    private Shop queryByIdWithLogicExpire(Long id) {\n        String key = CACHE_SHOP_KEY+id;\n        \/\/1.\u5728\u7f13\u5b58\u4e2d\u5bfb\u627e\n        String shopJson = stringRedisTemplate.opsForValue().get(key);\n\n        \/\/2.\u5224\u65ad\u7f13\u5b58\u4e2d\u662f\u5426\u5b58\u5728\n        if (!StrUtil.isNotBlank(shopJson)){\n            \/\/\u6ca1\u627e\u5230\u5c31\u76f4\u63a5\u8fd4\u56denull\n            return null;\n        }\n\n        \/\/3.\u68c0\u67e5\u662f\u5426\u903b\u8f91\u8fc7\u671f\n        RedisData shopData = JSONUtil.toBean(shopJson, RedisData.class);\n        Shop shop = (Shop) shopData.getData();\n\n        if (LocalDateTime.now().isBefore(shopData.getExpireTime())){\n            \/\/\u6ca1\u8fc7\u671f\uff0c\u76f4\u63a5\u8fd4\u56de\n            return shop;\n        }\n\n        \/\/4.\u8fc7\u671f\u4e86\uff0c\u5c1d\u8bd5\u83b7\u53d6\u9501\n        String lockKey= LOCK_SHOP_KEY+id;\n        boolean isLock = tryLock(lockKey);\n\n        \/\/\u6ca1\u83b7\u53d6\u5230\uff0c\u76f4\u63a5\u8fd4\u56de\u65e7\u6570\u636e\n        if (!isLock){\n            return shop;\n        }\n        \/\/\u83b7\u53d6\u5230\u9501\uff0c\u5f02\u6b65\u5f00\u542f\u65b0\u7ebf\u7a0b\u91cd\u5efa\u7f13\u5b58\uff0c\u4e5f\u5148\u83b7\u53d6\u8fd4\u56de\u65e7\u6570\u636e\n        cacheRebuildExecutor.submit(() -&gt; {\n            try {\n                \/\/ double check\uff1a\u91cd\u65b0\u53d6\u4e00\u6b21\u7f13\u5b58\uff0c\u770b\u662f\u5426\u5df2\u88ab\u5176\u4ed6\u7ebf\u7a0b\u66f4\u65b0\n                String latestJson = stringRedisTemplate.opsForValue().get(key);\n                RedisData latestData = JSONUtil.toBean(latestJson, RedisData.class);\n                if (latestData.getExpireTime().isAfter(LocalDateTime.now())) {\n                    return; \/\/ \u5df2\u66f4\u65b0\uff0c\u65e0\u9700\u91cd\u590d\u6784\u5efa\n                }\n\n                \/\/ \u67e5\u8be2\u6570\u636e\u5e93\u5e76\u6784\u5efa\u65b0\u7f13\u5b58\n                this.saveShop2Redis(id,CACHE_SHOP_TTL);\n            } finally {\n                unLock(lockKey);\n            }\n        });\n\n        return shop;\n    }\n\n\/\/\u903b\u8f91\u8fc7\u671f\u7f13\u51fb\u7a7f\u52a0\u8f7d\u6570\u636e\n    public void  saveShop2Redis(Long id,Long expireMinutes){\n        Shop shop = getById(id);\n\n        RedisData redisData = new RedisData();\n        redisData.setData(shop);\n        redisData.setExpireTime(LocalDateTime.now().plusMinutes(expireMinutes));\n\n        stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY+id,JSONUtil.toJsonStr(redisData));\n    }<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">2.7 Redis\u7f13\u5b58\u5de5\u5177\u7c7b\u7684\u5c01\u88c5<\/h2>\n\n\n\n<p>\u6d89\u53ca\u77e5\u8bc6\u70b9\u8f83\u591a\uff0c\u6cdb\u578b\u3001\u3001lambda\u3001\u56de\u8c03\u51fd\u6570\u8bbe\u8ba1\u3002<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><em>\/**\n<\/em><em> * <\/em><em>\u7f13\u5b58\u5de5\u5177\u7c7b\n<\/em><em> <\/em><em>*\/\n<\/em>@Component\n@Slf4j\npublic class CacheClient {\n    private StringRedisTemplate stringRedisTemplate;\n\n    public CacheClient(StringRedisTemplate stringRedisTemplate){\n        this.stringRedisTemplate = stringRedisTemplate;\n    }\n\n    \/\/\u5bf9\u8c61\u8f6c JSON \u5b58 string \u7c7b\u578b key\uff0c\u53ef\u8bbe TTL \u8fc7\u671f\u65f6\u95f4\n    public void set(String key, Object value, Long time, TimeUnit unit){\n        stringRedisTemplate.opsForValue().set(key, JSONUtil.<em>toJsonStr<\/em>(value),time,unit);\n    }\n\n    \/\/\u5bf9\u8c61\u8f6c JSON \u5b58 string \u7c7b\u578b key\uff0c\u8bbe\u903b\u8f91\u8fc7\u671f\u65f6\u95f4\u5904\u7406\u7f13\u5b58\u51fb\u7a7f\u3002\n    public void setWithLogicalExpire(String key, Object value, Long time, TimeUnit unit){\n        \/\/\u8bbe\u7f6e\u903b\u8f91\u8fc7\u671f\n        RedisData redisData = new RedisData();\n        redisData.setData(value);\n        redisData.setExpireTime(LocalDateTime.<em>now<\/em>().plusSeconds(unit.toSeconds(time)));\n\n        \/\/\u5199\u5165Redis\n        stringRedisTemplate.opsForValue().set(key,JSONUtil.<em>toJsonStr<\/em>(redisData));\n    }\n\n    \/\/\u6309 key \u67e5\u7f13\u5b58\u5e76\u53cd\u5e8f\u5217\u5316\u4e3a\u6307\u5b9a\u7c7b\u578b\uff0c\u7528\u7f13\u5b58\u7a7a\u503c\u89e3\u51b3\u7f13\u5b58\u7a7f\u900f\n    public &lt;R,ID&gt; R queryWithPassThrough(\n            String keyPrefix, ID id, Class&lt;R&gt; type, Function&lt;ID,R&gt; dbFallback,Long time, TimeUnit unit\n    ){\n        String key = keyPrefix+id;\n        \/\/1.\u5728\u7f13\u5b58\u4e2d\u5bfb\u627e\n        String json = stringRedisTemplate.opsForValue().get(key);\n\n\n        \/\/2.\u5224\u65ad\u7f13\u5b58\u4e2d\u662f\u5426\u5b58\u5728\n        if (StrUtil.<em>isNotBlank<\/em>(json)){\n            \/\/\u627e\u5230\u5c31\u76f4\u63a5\u8fd4\u56de\n            R r = JSONUtil.<em>toBean<\/em>(json,type);\n            return r;\n        }\n\n        \/\/3.\u5904\u7406\u547d\u4e2d\u7f13\u5b58\u7a7f\u900f\u7684\u201c\u201d\u7a7a\u503c\n        if (json!=null&amp;&amp;json.equals(\"\")){\n            \/\/\u8fd4\u56de\u9519\u8bef\u4fe1\u606f\n            return  null;\n        }\n\n        \/\/4.\u7f13\u5b58\u4e2d\u4e0d\u5b58\u5728\uff0c\u67e5\u627e\u6570\u636e\u5e93\n        R r = dbFallback.apply(id);\n\n        \/\/5.\u82e5\u6839\u672c\u4e0d\u5b58\u5728\uff0c\u8fd4\u56de\u9519\u8bef\n        if (r==null){\n            \/\/\u5c06\u7a7a\u503c\u5199\u5165redis\uff0c\u9632\u6b62\u7f13\u5b58\u7a7f\u900f\n            stringRedisTemplate.opsForValue().set(key,\"\",<em>CACHE_NULL_TTL<\/em>,TimeUnit.<em>MINUTES<\/em>);\n            \/\/\u8fd4\u56de\u9519\u8bef\n            return  null;\n        }\n\n        \/\/6.\u5199\u5165\u7f13\u5b58\n        this.set(key,r,time,unit);\n\n        \/\/7.\u8fd4\u56de\n        return r;\n    }\n\n    \/\/\u6309 key \u67e5\u7f13\u5b58\u5e76\u53cd\u5e8f\u5217\u5316\u4e3a\u6307\u5b9a\u7c7b\u578b\uff0c\u7528\u903b\u8f91\u8fc7\u671f\u89e3\u51b3\u7f13\u5b58\u51fb\u7a7f\n    private ExecutorService cacheRebuildExecutor = Executors.<em>newFixedThreadPool<\/em>(10);\n    public &lt;R,ID&gt; R queryWithLogicExpire(\n            String keyPrefix,String lockKeyPrefix, ID id, Class&lt;R&gt; type, Function&lt;ID,R&gt; dbFallback,Long time, TimeUnit unit\n    ){\n        String key = keyPrefix+id;\n        \/\/1.\u5728\u7f13\u5b58\u4e2d\u5bfb\u627e\n        String json = stringRedisTemplate.opsForValue().get(key);\n\n        \/\/2.\u5224\u65ad\u7f13\u5b58\u4e2d\u662f\u5426\u5b58\u5728\n        if (!StrUtil.<em>isNotBlank<\/em>(json)){\n            \/\/\u6ca1\u627e\u5230\u5c31\u76f4\u63a5\u8fd4\u56denull\n            return null;\n        }\n\n        \/\/3.\u68c0\u67e5\u662f\u5426\u903b\u8f91\u8fc7\u671f\n        RedisData redisData = JSONUtil.<em>toBean<\/em>(json, RedisData.class);\n        R r = (R) redisData.getData();\n\n        if (LocalDateTime.<em>now<\/em>().isBefore(redisData.getExpireTime())){\n            \/\/\u6ca1\u8fc7\u671f\uff0c\u76f4\u63a5\u8fd4\u56de\n            return r;\n        }\n\n        \/\/4.\u8fc7\u671f\u4e86\uff0c\u5c1d\u8bd5\u83b7\u53d6\u9501\n        String lockKey= lockKeyPrefix+id;\n        boolean isLock = tryLock(lockKey);\n\n        \/\/\u6ca1\u83b7\u53d6\u5230\uff0c\u76f4\u63a5\u8fd4\u56de\u65e7\u6570\u636e\n        if (!isLock){\n            return r;\n        }\n        \/\/\u83b7\u53d6\u5230\u9501\uff0c\u5f02\u6b65\u5f00\u542f\u65b0\u7ebf\u7a0b\u91cd\u5efa\u7f13\u5b58\uff0c\u4e5f\u5148\u83b7\u53d6\u8fd4\u56de\u65e7\u6570\u636e\n        cacheRebuildExecutor.submit(() -&gt; {\n            try {\n                \/\/ double check\uff1a\u91cd\u65b0\u53d6\u4e00\u6b21\u7f13\u5b58\uff0c\u770b\u662f\u5426\u5df2\u88ab\u5176\u4ed6\u7ebf\u7a0b\u66f4\u65b0\n                String latestJson = stringRedisTemplate.opsForValue().get(key);\n                RedisData latestData = JSONUtil.<em>toBean<\/em>(latestJson, RedisData.class);\n                if (latestData.getExpireTime().isAfter(LocalDateTime.<em>now<\/em>())) {\n                    return; \/\/ \u5df2\u66f4\u65b0\uff0c\u65e0\u9700\u91cd\u590d\u6784\u5efa\n                }\n\n                \/\/ \u67e5\u8be2\u6570\u636e\u5e93\u5e76\u6784\u5efa\u65b0\u7f13\u5b58\n                R newR=dbFallback.apply(id);\n                setWithLogicalExpire(key,newR,time,unit);\n\n            } finally {\n                unLock(lockKey);\n            }\n        });\n\n        return r;\n    }\n\n    private boolean tryLock(String key){\n        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, \"1\", 10, TimeUnit.<em>SECONDS<\/em>);\n        \/\/\u4e0d\u76f4\u63a5\u8fd4\u56de\u662f\u4e3a\u4e86\u9632\u6b62\u81ea\u52a8\u89e3\u5305\u65f6\u51fa\u73b0\u5f02\u6837\u5bfc\u81f4\u8fd4\u56denull\n        return BooleanUtil.<em>isTrue<\/em>(flag);\n    }\n    private void unLock(String key){\n        stringRedisTemplate.delete(key);\n    }\n}<\/code><\/pre>\n\n\n\n<h1 class=\"wp-block-heading\">3 \u4f18\u60e0\u5238\u79d2\u6740<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">3.1\u5168\u5c40\u552f\u4e00ID<\/h2>\n\n\n\n<p>\u6570\u636e\u5e93\u81ea\u589eID\u7684\u95ee\u9898\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>id\u89c4\u5f8b\u6027\u660e\u663e<\/li>\n\n\n\n<li>\u53d7\u5355\u8868\u6570\u636e\u91cf\u7684\u9650\u5236<\/li>\n<\/ul>\n\n\n\n<p>\u5206\u5e03\u5f0f\u7cfb\u7edf\u4e0b\u751f\u6210\u5168\u5c40\u552f\u4e00ID\u7684\u5de5\u5177\uff0c\u7279\u6027\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u552f\u4e00\u6027<\/li>\n\n\n\n<li>\u9ad8\u53ef\u7528<\/li>\n\n\n\n<li>\u9ad8\u6027\u80fd<\/li>\n\n\n\n<li>\u5355\u8c03\u9012\u589e\u6027<\/li>\n\n\n\n<li>\u5b89\u5168\u6027<\/li>\n<\/ul>\n\n\n\n<p>\u5168\u5c40\u552f\u4e00ID\u751f\u6210\u7b56\u7565\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>UUID<\/li>\n\n\n\n<li>\u96ea\u82b1\u7b97\u6cd5<\/li>\n\n\n\n<li>Redis\u81ea\u589e<\/li>\n\n\n\n<li>\u6570\u636e\u5e93\u81ea\u589e<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">\u57fa\u4e8eredis\u81ea\u589e\u7684\u5168\u5c40ID\u751f\u6210\u5668\uff1a<\/h3>\n\n\n\n<p>\u8bbe\u8ba1\uff1a<\/p>\n\n\n\n<p>long\u578b\uff1a64\u4f4d=1\u4f4d\u7b26\u53f7\u4f4d+31\u4f4d\u65f6\u95f4\u6233\uff08\u4ee5\u79d2\u4e3a\u5355\u4f4d\uff09+32\u4f4d\u79d2\u5185\u81ea\u589e\u8ba1\u6570\u5668<\/p>\n\n\n\n<p>\u5e8f\u5217\u53f7\u56de\u6eda\u4ee5\u5929\u4e3a\u5355\u4f4d\uff08\u5373\u4e00\u5929\u4e00\u4e2akey\uff09\uff0c\u65b9\u4fbf\u7edf\u8ba1\u8ba2\u5355\u91cf<\/p>\n\n\n\n<p><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><em>\/**<br><\/em><em> * <\/em><em>\u5168\u5c40\u552f\u4e00<\/em><em>ID<\/em><em>\u751f\u6210\u5668<br><\/em><em> <\/em><em>*\/<br><\/em>@Component<br>public class RedisIdWorker {<br>    private static final long <em>BEGIN_TIMESTAMP <\/em>= 1640995200L;<br>    private static final int <em>COUNT_BITS <\/em>= 32;<br>    private StringRedisTemplate stringRedisTemplate;<br><br>    public RedisIdWorker(StringRedisTemplate stringRedisTemplate){<br>        this.stringRedisTemplate = stringRedisTemplate;<br>    }<br><br>    public long nextId(String keyPrefix){<br>        \/\/\u751f\u6210\u65f6\u95f4\u6233<br>        long timeStamp = LocalDateTime.<em>now<\/em>().toEpochSecond(ZoneOffset.<em>UTC<\/em>)-<em>BEGIN_TIMESTAMP<\/em>;<br>        \/\/\u751f\u6210\u5e8f\u5217\u53f7<br>        \/\/\u5e8f\u5217\u53f7\u56de\u6eda\u4ee5\u5929\u4e3a\u5355\u4f4d\uff08\u5373\u4e00\u5929\u4e00\u4e2akey\uff09<br>        String date = LocalDateTime.<em>now<\/em>().format(DateTimeFormatter.<em>ofPattern<\/em>(\"yyyy:MM:dd\"));<br>        \/\/redis\u81ea\u589e\u957f<br>        long count = stringRedisTemplate.opsForValue().increment(\"icr:\"+keyPrefix+\":\"+date);<br><br>        \/\/\u62fc\u63a5\u8fd4\u56de<br>        return timeStamp&lt;&lt;<em>COUNT_BITS<\/em>|count;<br>    }<br>}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">3.2 \u4f18\u60e0\u5238\u79d2\u6740\u4e0b\u5355<\/h2>\n\n\n\n<p>\u4e0b\u5355\u65f6\u5224\u65ad\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u79d2\u6740\u662f\u5426\u5f00\u59cb\u3001\u7ed3\u675f<\/li>\n\n\n\n<li>\u5e93\u5b58\u662f\u5426\u5145\u8db3<\/li>\n<\/ul>\n\n\n\n<p>\u6700\u57fa\u7840\u7684\u79d2\u6740\u5238\u4e0b\u5355\u5b9e\u73b0\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@Transactional\npublic Result seckillVoucher(Long voucherId) {\n    \/\/1.\u67e5\u8be2\u79d2\u6740\u7c7b\u4f18\u60e0\u5238\n    SeckillVoucher voucher = seckillVoucherService.getById(voucherId);\n    if (voucher == null){\n        return Result.<em>fail<\/em>(\"\u79d2\u6740\u5238\u4e0d\u5b58\u5728\");\n    }\n\n    \/\/2.\u5224\u65ad\u662f\u5426\u5728\u6d3b\u52a8\u65f6\u95f4\u5185\n    LocalDateTime now = LocalDateTime.<em>now<\/em>();\n    if (voucher.getBeginTime().isAfter(now)||voucher.getEndTime().isBefore(now)){\n        return Result.<em>fail<\/em>(\"\u79d2\u6740\u5238\u4e0d\u5728\u6d3b\u52a8\u65f6\u95f4\");\n    }\n\n    \/\/3.\u662f\u5426\u6709\u5e93\u5b58\n    if (voucher.getStock() &lt; 1){\n        return Result.<em>fail<\/em>(\"\u5e93\u5b58\u4e0d\u8db3\");\n    }\n\n    \/\/4.\u6263\u51cf\u5e93\u5b58\n    boolean success = seckillVoucherService.update()\n        .eq(\"voucher_id\", voucherId)\n        .setSql(\"stock = stock - 1\")\n        .update();\n\n    if (!success){\n            return Result.fail(\"\u5e93\u5b58\u4e0d\u8db3\");\n        }\n\n    \/\/5.\u521b\u5efa\u8ba2\u5355\n    idWorker=new RedisIdWorker(stringRedisTemplate);\n    VoucherOrder order = new VoucherOrder();\n    Long orderId = idWorker.nextId(\"order\");\n    \/\/\u8ba2\u5355id\n    order.setId(orderId);\n    \/\/\u7528\u6237id\n    order.setUserId(UserHolder.<em>getUser<\/em>().getId());\n    \/\/\u79d2\u6740\u5238id\n    order.setVoucherId(voucherId);\n\n    save(order);\n\n    \/\/6.\u8fd4\u56de\u8ba2\u5355id\n    return Result.<em>ok<\/em>(orderId);\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">3.3 \u5e93\u5b58\u8d85\u5356\u95ee\u9898<\/h2>\n\n\n\n<p>\u5178\u578b\u7684\u591a\u7ebf\u7a0b\u5b89\u5168\u95ee\u9898\uff0c\u5e38\u89c1\u89e3\u51b3\u65b9\u6848\uff1a\u52a0\u9501\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u60b2\u89c2\u9501\uff1a<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u6838\u5fc3\u601d\u60f3\uff1a\u5148\u83b7\u53d6\u9501\uff0c\u518d\u64cd\u4f5c<\/li>\n\n\n\n<li>Synchronized\u3001lock&#8230;<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">\u4e50\u89c2\u9501\uff1a<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u53ea\u5728\u66f4\u65b0\u6570\u636e\u65f6\u53bb\u5224\u65ad\u6709\u6ca1\u6709\u5176\u4ed6\u7ebf\u7a0b\u5bf9\u6570\u636e\u505a\u4e86\u4fee\u6539\n<ul class=\"wp-block-list\">\n<li>\u5982\u679c\u6ca1\u6709\u4fee\u6539\uff0c\u7ee7\u7eed\u6267\u884c<\/li>\n\n\n\n<li>\u53d1\u73b0\u88ab\u4fee\u6539\uff0c\u91cd\u8bd5\u6216\u5f02\u5e38<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u4e24\u79cd\u5e38\u89c1\u65b9\u5f0f\uff1a\n<ul class=\"wp-block-list\">\n<li>\u7248\u672c\u53f7\u6cd5\uff1a\n<ul class=\"wp-block-list\">\n<li>\u5728\u6570\u636e\u8868\u4e2d\u6dfb\u52a0\u4e00\u4e2a&nbsp;<code>version<\/code>&nbsp;\u5b57\u6bb5\uff0c\u7528\u4e8e\u8bb0\u5f55\u6570\u636e\u7684\u7248\u672c\u53f7\u3002\u6bcf\u6b21\u66f4\u65b0\u6570\u636e\u65f6\uff0c\u7248\u672c\u53f7\u9012\u589e\u3002<\/li>\n\n\n\n<li><strong>\u6d41\u7a0b<\/strong>\uff1a\n<ul class=\"wp-block-list\">\n<li><strong>\u8bfb\u53d6\u6570\u636e<\/strong>\u65f6\uff0c\u540c\u65f6\u83b7\u53d6\u5f53\u524d\u7684\u7248\u672c\u53f7\u3002<\/li>\n\n\n\n<li><strong>\u66f4\u65b0\u6570\u636e<\/strong>\u65f6\uff0c\u68c0\u67e5\u7248\u672c\u53f7\u662f\u5426\u4e0e\u8bfb\u53d6\u65f6\u4e00\u81f4\u3002<\/li>\n\n\n\n<li>\u5982\u679c\u4e00\u81f4\uff0c\u5219\u6267\u884c\u66f4\u65b0\u5e76\u9012\u589e\u7248\u672c\u53f7\uff1b\u5426\u5219\uff0c\u8868\u793a\u6570\u636e\u5df2\u88ab\u5176\u4ed6\u4e8b\u52a1\u4fee\u6539\uff0c\u64cd\u4f5c\u5931\u8d25\u3002<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>CAS\u6cd5\uff08Compare And Swap\uff09\uff1a\n<ul class=\"wp-block-list\">\n<li>\u4ee5\u8be5\u8d85\u5356\u573a\u666f\u4e3a\u4f8b\uff0c\u5c31\u662f\u628a\u5e93\u5b58\u6570\u636e\u540c\u65f6\u5f53\u7248\u672c\u53f7\u4f7f\u7528<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">\u4e50\u89c2\u9501CAS\u89e3\u51b3\u8d85\u5356\u95ee\u9898\u793a\u4f8b\uff1a<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/4.\u6263\u51cf\u5e93\u5b58\nboolean success = seckillVoucherService.update()\n    .eq(\"voucher_id\", voucherId)\n    .setSql(\"stock = stock - 1\")\n    .update();<\/code><\/pre>\n\n\n\n<p>\u6539\u4e3a\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/4.\u6263\u51cf\u5e93\u5b58\nboolean success = seckillVoucherService.update()\n    .eq(\"voucher_id\", voucherId).eq(\"stock\",voucher.getStock())\/\/cas\u4e50\u89c2\u9501\n    .setSql(\"stock = stock - 1\")\n    .update();<\/code><\/pre>\n\n\n\n<p>\u6b64\u65f6\u4f1a\u51fa\u73b0\u591a\u4e2a\u7ebf\u7a0b\u540c\u65f6\u5931\u6548\uff1a<\/p>\n\n\n\n<p>\u53ef\u4fee\u6539\u4e3a\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/4.\u6263\u51cf\u5e93\u5b58\nboolean success = seckillVoucherService.update()\n    .eq(\"voucher_id\", voucherId).eq(\"stock\",voucher.getStock())\/\/cas\u4e50\u89c2\u9501\n    .setSql(\"stock = stock - 1\")\n    .update();<\/code><\/pre>\n\n\n\n<p>\u8fd9\u6837\u4e5f\u662f\u4e50\u89c2\u9501\uff0c\u4f46\u4e0d\u518d\u662fcas\u6216\u7248\u672c\u53f7\u5f0f\u3002<\/p>\n\n\n\n<p>\u4e5f\u53ef\u4ee5\u52a0\u4e0a\u91cd\u5f0f\u673a\u5236\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>boolean success = false;\n        int retryTimes = 3;\n        while (retryTimes-- &gt; 0) {\n            success = seckillVoucherService.update()\n                    .setSql(\"stock = stock - 1\")\n                    .eq(\"voucher_id\", voucherId)\n                    .gt(\"stock\", 0) \/\/ \u786e\u4fdd\u5e93\u5b58\u5927\u4e8e0\n                    .update();\n            if (success) {\n                break;\n            }\n            \/\/ \u6bcf\u6b21\u5931\u8d25\u53ef\u4ee5\u8003\u8651 sleep \u4e00\u70b9\u65f6\u95f4\u7f13\u89e3\u5199\u51b2\u7a81\uff08\u53ef\u9009\uff09\n\n        }<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\u8d85\u5356\u95ee\u9898\u89e3\u51b3\u65b9\u6848\uff1a<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u60b2\u89c2\u9501\uff1a\u6dfb\u52a0\u540c\u6b65\u9501\uff0c\u8ba9\u7ebf\u7a0b\u4e32\u884c\u6267\u884c<br>\u30fb\u4f18\u70b9\uff1a\u7b80\u5355\u7c97\u66b4<br>\u30fb\u7f3a\u70b9\uff1a\u6027\u80fd\u4e00\u822c<\/li>\n\n\n\n<li>\u4e50\u89c2\u9501\uff1a\u4e0d\u52a0\u9501\uff0c\u5728\u66f4\u65b0\u65f6\u5224\u65ad\u662f\u5426\u6709\u5176\u5b83\u7ebf\u7a0b\u5728\u4fee\u6539<br>\u30fb\u4f18\u70b9\uff1a\u6027\u80fd\u597d<br>\u30fb\u7f3a\u70b9\uff1a\u5b58\u5728\u6210\u529f\u7387\u4f4e\u7684\u95ee\u9898<\/li>\n\n\n\n<li>Redis \u539f\u5b50\u811a\u672c\uff08Lua\u811a\u672c\uff09\u5e93\u5b58\u6263\u51cf + \u5f02\u6b65\u843d\u5e93 <br>\u30fb\u4f18\u70b9\uff1a\u6027\u80fd\u6700\u597d\u3001<strong>\u51cf\u8f7b\u6570\u636e\u5e93\u538b\u529b<\/strong><br>\u30fb\u7f3a\u70b9\uff1a\u5f00\u53d1\u96be\u5ea6\u8f83\u5927\u3001\u7ef4\u62a4\u56f0\u96be\uff0c<strong>Redis \u6545\u969c\u98ce\u9669<\/strong>\u3001\u590d\u6742\u6027\u9ad8<\/li>\n<\/ol>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">3.4 \u4e00\u4eba\u4e00\u5355\u95ee\u9898<\/h2>\n\n\n\n<p>\u573a\u666f\u63cf\u8ff0\uff1a\u9650\u5236\u540c\u4e00\u7528\u6237\u53ea\u80fd\u4e0b\u5355\u4e00\u6b21\uff08\u79d2\u6740\u53ea\u4e70\u4e00\u6b21\uff09<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u53ef\u4ee5\u7528\u4e50\u89c2\u9501\u5417\uff1f<\/h3>\n\n\n\n<p><strong>\u4e50\u89c2\u9501\u672c\u8d28\u662f\u57fa\u4e8e\u7248\u672c\u53f7\u6216\u6761\u4ef6\u66f4\u65b0\u7684\u5e76\u53d1\u63a7\u5236<\/strong>\uff0c\u5b83\u5bf9\u201c\u5e93\u5b58\u6263\u51cf\u201d\u8fd9\u79cd\u201c\u6570\u503c\u66f4\u65b0\u201d\u975e\u5e38\u9002\u7528\u3002<br>\u5bf9\u201c\u4e00\u4eba\u4e00\u5355\u201d\u8fd9\u79cd\u201c\u7528\u6237\u552f\u4e00\u6027\u9650\u5236\u201d\u95ee\u9898\uff0c\u4e50\u89c2\u9501\u4e0d\u662f\u6700\u76f4\u63a5\u7684\u89e3\u51b3\u65b9\u5f0f\u3002<\/p>\n\n\n\n<p>\u6240\u4ee5\u6b64\u5904\u9009\u62e9\u60b2\u89c2\u9501\u89e3\u51b3\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@Override\npublic Result seckillVoucher(Long voucherId) {\n    \/\/1.\u67e5\u8be2\u79d2\u6740\u7c7b\u4f18\u60e0\u5238\n    SeckillVoucher voucher = seckillVoucherService.getById(voucherId);\n    if (voucher == null){\n        return Result.<em>fail<\/em>(\"\u79d2\u6740\u5238\u4e0d\u5b58\u5728\");\n    }\n\n    \/\/2.\u5224\u65ad\u662f\u5426\u5728\u6d3b\u52a8\u65f6\u95f4\u5185\n    LocalDateTime now = LocalDateTime.<em>now<\/em>();\n    if (voucher.getBeginTime().isAfter(now)||voucher.getEndTime().isBefore(now)){\n        return Result.<em>fail<\/em>(\"\u79d2\u6740\u5238\u4e0d\u5728\u6d3b\u52a8\u65f6\u95f4\");\n    }\n\n    \/\/3.\u662f\u5426\u6709\u5e93\u5b58\n    if (voucher.getStock() &lt; 1){\n        return Result.<em>fail<\/em>(\"\u5e93\u5b58\u4e0d\u8db3\");\n    }\n\n\n\n    \/\/6.\u8fd4\u56de\u8ba2\u5355id\n    return createOrder(voucher);\n}\n\n@Transactional\npublic  Result createOrder(SeckillVoucher voucher){\n    \/\/\u4e00\u4eba\u4e00\u5355\u7528\u60b2\u89c2\u9501:synchronized\n    Long userId = UserHolder.<em>getUser<\/em>().getId();\n\n    \/\/userId.toString().intern()\uff1auserId.toString().intern() \u662f\u4e3a\u4e86\u786e\u4fdd\u76f8\u540c\u7528\u6237\u5728\u591a\u7ebf\u7a0b\u4e0b\u4f7f\u7528\u540c\u4e00\u628a\u9501\uff0c\u5b9e\u73b0\u7528\u6237\u7ea7\u4e92\u65a5\uff0c\u800c\u4e0d\u662f\u5168\u5c40\u9501\u6216\u5931\u6548\u9501\u3002\n    synchronized (userId.toString().intern()){\n        int count = query().eq(\"user_id\", userId).eq(\"voucher_id\", voucher.getVoucherId()).count();\n        if (count &gt; 0){\n            return Result.<em>fail<\/em>(\"\u540c\u4e00\u7528\u6237\u53ea\u80fd\u8d2d\u4e70\u4e00\u6b21\");\n        }\n\n        \/\/4.\u6263\u51cf\u5e93\u5b58\n        boolean success = seckillVoucherService.update()\n                .eq(\"voucher_id\", voucher.getVoucherId()).eq(\"stock\",voucher.getStock())\/\/cas\u4e50\u89c2\u9501\n                .setSql(\"stock = stock - 1\")\n                .update();\n\n        if (!success){\n            return Result.<em>fail<\/em>(\"\u5e93\u5b58\u4e0d\u8db3\");\n        }\n\n        \/\/5.\u521b\u5efa\u8ba2\u5355\n        idWorker=new RedisIdWorker(stringRedisTemplate);\n\n        VoucherOrder order = new VoucherOrder();\n        Long orderId = idWorker.nextId(\"order\");\n        \/\/\u8ba2\u5355id\n        order.setId(orderId);\n        \/\/\u7528\u6237id\n        order.setUserId(UserHolder.<em>getUser<\/em>().getId());\n        \/\/\u79d2\u6740\u5238id\n        order.setVoucherId(voucher.getVoucherId());\n\n        save(order);\n\n        return Result.<em>ok<\/em>(orderId);\n    }\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\u6ce8\u610f\u9501\uff1a<mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\">userId.toString().intern()<\/mark><br><\/h3>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u9501\u5bf9\u8c61<\/th><th>\u662f\u5426\u552f\u4e00\u5bf9\u8c61<\/th><th>\u662f\u5426\u9002\u5408\u5e76\u53d1\u63a7\u5236<\/th><th>\u8bf4\u660e<\/th><\/tr><\/thead><tbody><tr><td><code>this<\/code><\/td><td>\u662f<\/td><td>\u274c<\/td><td>\u6240\u6709\u4eba\u5171\u7528\u4e00\u628a\u9501\uff0c\u6027\u80fd\u5dee<\/td><\/tr><tr><td><code>userId<\/code><\/td><td>\u5426<\/td><td>\u274c<\/td><td>\u53ef\u80fd\u662f\u4e0d\u540c\u5bf9\u8c61<\/td><\/tr><tr><td><code>userId.toString()<\/code><\/td><td>\u5426<\/td><td>\u274c<\/td><td>\u6bcf\u6b21\u90fd\u662f\u65b0\u5bf9\u8c61<\/td><\/tr><tr><td><code>userId<\/code><br><code>.toString()<\/code><code>.intern()<\/code><\/td><td>\u2705<\/td><td>\u2705<\/td><td>\u5b57\u7b26\u4e32\u5e38\u91cf\u6c60\uff0c\u786e\u4fdd\u540c\u7528\u6237\u540c\u9501<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">\u65b0\u95ee\u9898\uff1a<\/h3>\n\n\n\n<p>\u7531\u4e8e\u201c<strong>Spring \u4e8b\u52a1\u662f\u63d0\u4ea4\u65f6\u751f\u6548\uff0c\u5728\u65b9\u6cd5\u6700\u540e\u624d\u63d0\u4ea4<\/strong>\uff1b\u201d\u3002\u8fd9\u91cc\u9501\u4e86create\u65b9\u6cd5\u5185\uff0c\u4f1a\u5bfc\u81f4\uff1a\u5148\u91ca\u653e\u4e86\u9501\uff0c\u518d\u63d0\u4ea4\u4e8b\u52a1\u7684\u4e2d\u95f4\u65f6\u671f\u53ef\u80fd\u6709\u5176\u4ed6\u7ebf\u7a0b\u62ff\u5230\u9501\u8fdb\u884c\u4e86\u4fee\u6539\u3002\u9020\u6210\u5e76\u53d1\u5b89\u5168\u95ee\u9898\u3002<\/p>\n\n\n\n<p>\u4fee\u6539\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@Override<br>public Result seckillVoucher(Long voucherId) {<br>    \/\/1.\u67e5\u8be2\u79d2\u6740\u7c7b\u4f18\u60e0\u5238<br>    SeckillVoucher voucher = seckillVoucherService.getById(voucherId);<br>    if (voucher == null){<br>        return Result.<em>fail<\/em>(\"\u79d2\u6740\u5238\u4e0d\u5b58\u5728\");<br>    }<br><br>    \/\/2.\u5224\u65ad\u662f\u5426\u5728\u6d3b\u52a8\u65f6\u95f4\u5185<br>    LocalDateTime now = LocalDateTime.<em>now<\/em>();<br>    if (voucher.getBeginTime().isAfter(now)||voucher.getEndTime().isBefore(now)){<br>        return Result.<em>fail<\/em>(\"\u79d2\u6740\u5238\u4e0d\u5728\u6d3b\u52a8\u65f6\u95f4\");<br>    }<br><br>    \/\/3.\u662f\u5426\u6709\u5e93\u5b58<br>    if (voucher.getStock() &lt; 1){<br>        return Result.<em>fail<\/em>(\"\u5e93\u5b58\u4e0d\u8db3\");<br>    }<br><br>    \/\/6.\u8fd4\u56de\u8ba2\u5355id<br>    Long userId = UserHolder.<em>getUser<\/em>().getId();<br>    \/\/userId.toString().intern()\u89e3\u91ca\uff1auserId.toString().intern() \u662f\u4e3a\u4e86\u786e\u4fdd\u76f8\u540c\u7528\u6237\u5728\u591a\u7ebf\u7a0b\u4e0b\u4f7f\u7528\u540c\u4e00\u628a\u9501\uff0c\u5b9e\u73b0\u7528\u6237\u7ea7\u4e92\u65a5\uff0c\u800c\u4e0d\u662f\u5168\u5c40\u9501\u6216\u5931\u6548\u9501\u3002<br><br>    synchronized (userId.toString().intern()) {\/\/\u83b7\u53d6\u9501<br>        return createOrder(voucher);\/\/\u63d0\u4ea4\u4e8b\u52a1<br>    }\/\/\u91ca\u653e\u9501<br>}<br><br>@Transactional<br>public  Result createOrder(SeckillVoucher voucher){<br>    \/\/\u4e00\u4eba\u4e00\u5355\u7528\u60b2\u89c2\u9501:synchronized<br>    Long userId = UserHolder.<em>getUser<\/em>().getId();<br><br>    int count = query().eq(\"user_id\", userId).eq(\"voucher_id\", voucher.getVoucherId()).count();<br>    if (count &gt; 0){<br>        return Result.<em>fail<\/em>(\"\u540c\u4e00\u7528\u6237\u53ea\u80fd\u8d2d\u4e70\u4e00\u6b21\");<br>    }<br><br>    \/\/4.\u6263\u51cf\u5e93\u5b58<br>    boolean success = seckillVoucherService.update()<br>            .eq(\"voucher_id\", voucher.getVoucherId()).eq(\"stock\",voucher.getStock())\/\/cas\u4e50\u89c2\u9501<br>            .setSql(\"stock = stock - 1\")<br>            .update();<br><br>    if (!success){<br>        return Result.<em>fail<\/em>(\"\u5e93\u5b58\u4e0d\u8db3\");<br>    }<br><br>    \/\/5.\u521b\u5efa\u8ba2\u5355<br>    idWorker=new RedisIdWorker(stringRedisTemplate);<br><br>    VoucherOrder order = new VoucherOrder();<br>    Long orderId = idWorker.nextId(\"order\");<br>    \/\/\u8ba2\u5355id<br>    order.setId(orderId);<br>    \/\/\u7528\u6237id<br>    order.setUserId(UserHolder.<em>getUser<\/em>().getId());<br>    \/\/\u79d2\u6740\u5238id<br>    order.setVoucherId(voucher.getVoucherId());<br>    save(order);<br>    return Result.<em>ok<\/em>(orderId);<br>}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\u8fd8\u662f\u6709\u95ee\u9898\uff1a<mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\">\u4e8b\u52a1\u5931\u6548\uff01\uff01\uff01<\/mark><\/h3>\n\n\n\n<p><strong>Spring \u4e8b\u52a1\u7684\u672c\u8d28\uff1a\u9760\u4ee3\u7406\u5bf9\u8c61\u5b9e\u73b0<\/strong>\u3002\u5f53\u4f60\u8c03\u7528 <code>@Transactional<\/code> \u65b9\u6cd5\u65f6\uff0c\u5176\u5b9e\u8c03\u7528\u7684\u662f<strong>\u4ee3\u7406\u5bf9\u8c61\u7684\u65b9\u6cd5<\/strong>\u3002<\/p>\n\n\n\n<p><code>seckillVoucher()<\/code> \u8c03\u7528\u7684\u662f\u5f53\u524d\u7c7b\u7684 <code>createOrder()<\/code> \u65b9\u6cd5\uff0c\u800c\u975e\u6709\u4e8b\u52a1\u7ba1\u7406\u80fd\u529b\u7684\u5f53\u524d\u7c7b\u7684\u4ee3\u7406\u7c7b\u7684\u65b9\u6cd5\uff0c\u6240\u4ee5\u4e8b\u52a1\u5931\u6548\u4e86\u3002<\/p>\n\n\n\n<p>\u90e8\u5206\u89e3\u51b3\u65b9\u6cd5\uff1a<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u89e3\u51b3\u65b9\u6cd5<\/th><th>\u7b80\u8ff0<\/th><th>\u7279\u70b9<\/th><\/tr><\/thead><tbody><tr><td><strong>\u65b9\u6cd5\u4e00\uff1a\u9501\u4e0e\u4e8b\u52a1\u5199\u5728\u540c\u4e00\u4e2a <code>@Transactional<\/code> \u65b9\u6cd5\u4e2d<\/strong><\/td><td>\u5728\u4e00\u4e2a\u65b9\u6cd5\u4e2d\u540c\u65f6\u52a0\u9501\u5e76\u6267\u884c\u4e0b\u5355\u903b\u8f91\uff0c\u907f\u514d\u5185\u90e8\u8c03\u7528<\/td><td>\u6700\u7b80\u5355\uff0c\u4e8b\u52a1\u751f\u6548\uff0c\u9002\u5408\u5355\u4f53\u5e94\u7528<\/td><\/tr><tr><td><strong>\u65b9\u6cd5\u4e8c\uff1a\u5c06\u4e8b\u52a1\u65b9\u6cd5\u62bd\u53d6\u5230\u53e6\u4e00\u4e2a Spring Bean \u4e2d<\/strong><\/td><td>\u628a <code>createOrder()<\/code> \u62c6\u51fa\u53bb\uff0c\u901a\u8fc7 Spring \u6ce8\u5165\u8c03\u7528<\/td><td>\u63a8\u8350\u505a\u6cd5\uff0c\u89e3\u8026\u6e05\u6670\uff0c\u4e8b\u52a1\u751f\u6548<\/td><\/tr><tr><td><strong>\u65b9\u6cd5\u4e09\uff1a\u4f7f\u7528 <code>AopContext.currentProxy()<\/code> \u83b7\u53d6\u4ee3\u7406\u5bf9\u8c61<\/strong><\/td><td>\u901a\u8fc7 Spring \u66b4\u9732\u7684\u4ee3\u7406\u5bf9\u8c61\u95f4\u63a5\u8c03\u7528\u81ea\u8eab\u65b9\u6cd5<\/td><td>\u539f\u7406\u6e05\u6670\uff0c\u4f46\u5199\u6cd5\u522b\u626d\uff0c\u4e0d\u63a8\u8350\u751f\u4ea7\u7528<\/td><\/tr><tr><td><strong>\u65b9\u6cd5\u56db\uff1a\u81ea\u5df1\u6ce8\u5165\u81ea\u5df1\u7684\u4ee3\u7406\u5bf9\u8c61<\/strong><\/td><td>\u5728\u7c7b\u4e2d\u6ce8\u5165\u81ea\u5df1\uff0c\u901a\u8fc7\u4ee3\u7406\u5bf9\u8c61\u8c03\u7528\u4e8b\u52a1\u65b9\u6cd5<\/td><td>\u53ef\u884c\u4f46\u4e0d\u4f18\u96c5\uff0c\u63a8\u8350\u7528\u63a5\u53e3\u6ce8\u5165\u65b9\u5f0f<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>\u6700\u7ec8\uff1a<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@Service\npublic class VoucherOrderServiceImpl extends ServiceImpl&lt;VoucherOrderMapper, VoucherOrder&gt; implements IVoucherOrderService {\n    @Autowired\n    private ISeckillVoucherService seckillVoucherService;\n\n    @Autowired\n    private StringRedisTemplate stringRedisTemplate;\n\n    @Autowired\n    private VoucherOrderServiceImpl proxySelf;\n\n    private RedisIdWorker idWorker;\n    @Override\n    public Result seckillVoucher(Long voucherId) {\n        \/\/1.\u67e5\u8be2\u79d2\u6740\u7c7b\u4f18\u60e0\u5238\n        SeckillVoucher voucher = seckillVoucherService.getById(voucherId);\n        if (voucher == null){\n            return Result.<em>fail<\/em>(\"\u79d2\u6740\u5238\u4e0d\u5b58\u5728\");\n        }\n\n        \/\/2.\u5224\u65ad\u662f\u5426\u5728\u6d3b\u52a8\u65f6\u95f4\u5185\n        LocalDateTime now = LocalDateTime.<em>now<\/em>();\n        if (voucher.getBeginTime().isAfter(now)||voucher.getEndTime().isBefore(now)){\n            return Result.<em>fail<\/em>(\"\u79d2\u6740\u5238\u4e0d\u5728\u6d3b\u52a8\u65f6\u95f4\");\n        }\n\n        \/\/3.\u662f\u5426\u6709\u5e93\u5b58\n        if (voucher.getStock() &lt; 1){\n            return Result.<em>fail<\/em>(\"\u5e93\u5b58\u4e0d\u8db3\");\n        }\n\n        \/\/6.\u8fd4\u56de\u8ba2\u5355id\n        Long userId = UserHolder.<em>getUser<\/em>().getId();\n        \/\/userId.toString().intern()\u89e3\u91ca\uff1auserId.toString().intern() \u662f\u4e3a\u4e86\u786e\u4fdd\u76f8\u540c\u7528\u6237\u5728\u591a\u7ebf\u7a0b\u4e0b\u4f7f\u7528\u540c\u4e00\u628a\u9501\uff0c\u5b9e\u73b0\u7528\u6237\u7ea7\u4e92\u65a5\uff0c\u800c\u4e0d\u662f\u5168\u5c40\u9501\u6216\u5931\u6548\u9501\u3002\n\n        synchronized (userId.toString().intern()) {\/\/\u83b7\u53d6\u9501\n            \/\/\u63d0\u4ea4\u4e8b\u52a1\n            \/\/return createOrder(voucher);,\u5728\u7c7b\u5185\u90e8\u201c\u81ea\u5df1\u8c03\u7528\u81ea\u5df1\u201d\uff0c\u5c31\u7ed5\u8fc7\u4e86\u4ee3\u7406\uff0c\u5bfc\u81f4\u4e8b\u52a1\u4e0d\u751f\u6548\n            \/\/ \u8c03\u7528\u7684\u662f\u88ab\u4ee3\u7406\u5bf9\u8c61\uff0c\u4e8b\u52a1\u751f\u6548\n            return proxySelf.createOrder(voucher);\n        }\/\/\u91ca\u653e\u9501\n    }\n\n    @Transactional\n    public  Result createOrder(SeckillVoucher voucher){\n        \/\/\u4e00\u4eba\u4e00\u5355\u7528\u60b2\u89c2\u9501:synchronized\n        Long userId = UserHolder.<em>getUser<\/em>().getId();\n\n        int count = query().eq(\"user_id\", userId).eq(\"voucher_id\", voucher.getVoucherId()).count();\n        if (count &gt; 0){\n            return Result.<em>fail<\/em>(\"\u540c\u4e00\u7528\u6237\u53ea\u80fd\u8d2d\u4e70\u4e00\u6b21\");\n        }\n\n        \/\/4.\u6263\u51cf\u5e93\u5b58\n        boolean success = seckillVoucherService.update()\n                .eq(\"voucher_id\", voucher.getVoucherId()).eq(\"stock\",voucher.getStock())\/\/cas\u4e50\u89c2\u9501\n                .setSql(\"stock = stock - 1\")\n                .update();\n\n        if (!success){\n            return Result.<em>fail<\/em>(\"\u5e93\u5b58\u4e0d\u8db3\");\n        }\n\n        \/\/5.\u521b\u5efa\u8ba2\u5355\n        idWorker=new RedisIdWorker(stringRedisTemplate);\n\n        VoucherOrder order = new VoucherOrder();\n        Long orderId = idWorker.nextId(\"order\");\n        \/\/\u8ba2\u5355id\n        order.setId(orderId);\n        \/\/\u7528\u6237id\n        order.setUserId(UserHolder.<em>getUser<\/em>().getId());\n        \/\/\u79d2\u6740\u5238id\n        order.setVoucherId(voucher.getVoucherId());\n        save(order);\n        return Result.<em>ok<\/em>(orderId);\n    }\n}<\/code><\/pre>\n\n\n\n<p>\u81f3\u6b64\uff0c\u89e3\u51b3\u4e86\u5355\u4f53\u67b6\u6784\u7684\u201c\u4e00\u4eba\u4e00\u5355\u201d\u95ee\u9898\uff0c\u4f46\u662f\u82e5\u662f\u96c6\u7fa4\u6a21\u5f0f\uff0c\u4ecd\u7136\u56e0\u4e3a\u9501\u7684\u95ee\u9898\u5b58\u5728\u5e76\u53d1\u5b89\u5168\u95ee\u9898\u3002<\/p>\n\n\n\n<p>\u5728\u96c6\u7fa4\u73af\u5883\u4e2d\uff0c\u9700\u8981\u4f7f\u7528 <strong>\u5206\u5e03\u5f0f\u9501\uff08\u5982 Redis Redisson\u3001Zookeeper \u7b49\uff09<\/strong> \u6765\u4fdd\u8bc1\u9501\u7684\u5168\u5c40\u552f\u4e00\u6027\u548c\u8de8\u8282\u70b9\u4e92\u65a5\uff0c\u4ece\u800c\u5f7b\u5e95\u89e3\u51b3\u201c\u4e00\u4eba\u4e00\u5355\u201d\u7684\u5e76\u53d1\u5b89\u5168\u95ee\u9898\u3002<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">4.\u5206\u5e03\u5f0f\u9501<\/h1>\n\n\n\n<p>\u6ee1\u8db3\u5206\u5e03\u5f0f\u7cfb\u7edf\u6216\u96c6\u7fa4\u6a21\u5f0f\u4e0b\u591a\u7ebf\u7a0b\u53ef\u89c1\u5e76\u4e14\u4e92\u65a5\u7684\u9501\u3002<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u591a\u7ebf\u7a0b\u53ef\u89c1<\/li>\n\n\n\n<li>\u4e92\u65a5<\/li>\n\n\n\n<li>\u9ad8\u53ef\u7528<\/li>\n\n\n\n<li>\u9ad8\u6027\u80fd<\/li>\n\n\n\n<li>\u5b89\u5168\u6027<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">\u57fa\u4e8eRedis\u7684\u5206\u5e03\u5f0f\u9501<\/h3>\n\n\n\n<p>\u5206\u5e03\u5f0f\u9501\u7684\u4e24\u4e2a\u57fa\u672c\u65b9\u6cd5\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u83b7\u53d6\u9501\n<ul class=\"wp-block-list\">\n<li>\u4e92\u65a5\uff1aSETNX<\/li>\n\n\n\n<li>\u9700\u8981\u4fdd\u8bc1\u83b7\u53d6\u9501\u4e0e\u8bbe\u5b9a\u8fc7\u671f\u65f6\u95f4\u7684\u539f\u5b50\u6027<\/li>\n\n\n\n<li>\u975e\u963b\u585e\uff1a\u5c1d\u8bd5\u4e00\u6b21\uff0c\u6210\u529ftrue\uff0c\u5931\u8d25false<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u91ca\u653e\u9501\n<ul class=\"wp-block-list\">\n<li>\u624b\u52a8\u91ca\u653e\uff1aDEL<\/li>\n\n\n\n<li>\u8d85\u65f6\u91ca\u653e:EXPIRE\uff0c\u515c\u5e95<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">4.1\u5206\u5e03\u5f0f\u9501\u5b9e\u73b0\u521d\u7ea7\u7248\u672c\uff1a<\/h2>\n\n\n\n<h4 class=\"wp-block-heading\">\u5206\u5e03\u5f0f\u9501\u7c7b\uff1a<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>public class SimpleRedisLock implements ILock{<br><br>    public static final String <em>KEY_PREFIX <\/em>= \"lock:\";<br>    private String name;<br>    private StringRedisTemplate stringRedisTemplate;<br><br>    public SimpleRedisLock(String name, StringRedisTemplate stringRedisTemplate) {<br>        this.name = name;<br>        this.stringRedisTemplate = stringRedisTemplate;<br>    }<br><br>    \/\/\u83b7\u53d6\u9501<br>    @Override<br>    public boolean tryLock(long timeoutSec) {<br>        String key = <em>KEY_PREFIX <\/em>+ name;<br>        long threadId = Thread.<em>currentThread<\/em>().getId();<br>        Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(key, threadId+\"\", timeoutSec, TimeUnit.<em>SECONDS<\/em>);<br><br>        return BooleanUtil.<em>isTrue<\/em>(success);<br>    }<br><br>    \/\/\u91ca\u653e\u9501<br>    @Override<br>    public void unlock() {<br>        stringRedisTemplate.delete(<em>KEY_PREFIX <\/em>+ name);<br>    }<br>}<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">\u4e1a\u52a1\u4fee\u6539\uff1a\u4f7f\u7528\u5206\u5e03\u5f0f\u9501\u800c\u975e\u539f\u6765\u7684synchronized (userId.toString().intern()<\/h4>\n\n\n\n<pre class=\"wp-block-preformatted\">SimpleRedisLock simpleRedisLock = new SimpleRedisLock(\"order:\"+userId, stringRedisTemplate);<br>boolean success = simpleRedisLock.tryLock(1500);<br>if (!success){<br>    return Result.<em>fail<\/em>(\"\u8bf7\u52ff\u91cd\u590d\u4e0b\u5355\");<br>}<br><br>try {<br>    \/\/\u63d0\u4ea4\u4e8b\u52a1<br>    \/\/return createOrder(voucher);,\u5728\u7c7b\u5185\u90e8\u201c\u81ea\u5df1\u8c03\u7528\u81ea\u5df1\u201d\uff0c\u5c31\u7ed5\u8fc7\u4e86\u4ee3\u7406\uff0c\u5bfc\u81f4\u4e8b\u52a1\u4e0d\u751f\u6548<br>    \/\/ \u8c03\u7528\u7684\u662f\u88ab\u4ee3\u7406\u5bf9\u8c61\uff0c\u4e8b\u52a1\u751f\u6548<br>    return proxySelf.createOrder(voucher);<br>} catch (Exception e) {<br>    throw new RuntimeException(e);<br>} finally {<br>    simpleRedisLock.unlock();<br>}<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">4.2Redis\u5206\u5e03\u5f0f\u9501\u8bef\u5220\u95ee\u9898\uff1a<\/h2>\n\n\n\n<figure class=\"wp-block-image size-large\"><div class='fancybox-wrapper lazyload-container-unload' data-fancybox='post-images' href='http:\/\/www.cmd137blog.top\/wp-content\/uploads\/2025\/03\/image-1024x412.png'><img class=\"lazyload lazyload-style-1\" src=\"data:image\/svg+xml;base64,PCEtLUFyZ29uTG9hZGluZy0tPgo8c3ZnIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgc3Ryb2tlPSIjZmZmZmZmMDAiPjxnPjwvZz4KPC9zdmc+\"  loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"412\" data-original=\"http:\/\/www.cmd137blog.top\/wp-content\/uploads\/2025\/03\/image-1024x412.png\" src=\"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB\/AAffA0nNPuCLAAAAAElFTkSuQmCC\" alt=\"\" class=\"wp-image-935\"  sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/div><\/figure>\n\n\n\n<p>\u5f53\u7ebf\u7a0b1\u83b7\u53d6\u9501\u540e\uff0c\u7531\u4e8e\u4e1a\u52a1\u963b\u585e\uff0c\u7ebf\u7a0b1\u7684\u9501\u8d85\u65f6\u91ca\u653e\u4e86\uff0c\u8fd9\u65f6\u5019\u7ebf\u7a0b2\u8d81\u865a\u800c\u5165\u62ff\u5230\u4e86\u9501\uff0c\u7136\u540e\u6b64\u65f6\u7ebf\u7a0b1\u4e1a\u52a1\u5b8c\u6210\u4e86\uff0c\u7136\u540e\u628a\u7ebf\u7a0b2\u521a\u521a\u83b7\u53d6\u7684\u9501\u7ed9\u91ca\u653e\u4e86\uff0c\u8fd9\u65f6\u5019\u7ebf\u7a0b3\u53c8\u8d81\u865a\u800c\u5165\u62ff\u5230\u4e86\u9501\uff0c\u51fa\u73b0\u5e76\u53d1\u5b89\u5168\u95ee\u9898\u3002<\/p>\n\n\n\n<p>\u89e3\u51b3\u601d\u8def\uff1a\u7ed9\u9501\u52a0\u4e0a\u552f\u4e00\u6807\u8bc6\uff0c\u4e3b\u52a8\u91ca\u653e\u65f6\u68c0\u67e5\u6807\u518d\u91ca\u653e\u9501\u3002\u4e0d\u4e00\u81f4\u5219\u4e0d\u91ca\u653e\uff0c<\/p>\n\n\n\n<p>\u6b64\u5904\u8bbe\u8ba1\u6807\u8bc6\uff1aJVM\u5b9e\u4f8b\u7ea7UUID+\u7ebf\u7a0bID\u3002\uff08\u540c\u4e00JVM\u5185\u7684\u7ebf\u7a0b\u5f97\u5230\u7684UUID\u4e00\u6837\uff09\u3002<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public class SimpleRedisLock implements ILock{<br><br>    public static final String <em>KEY_PREFIX <\/em>= \"lock:\";<br>    public static final String <em>ID_PREFIX<\/em>= UUID.<em>randomUUID<\/em>().toString(true)+\"_\";<br>    private String name;<br>    private StringRedisTemplate stringRedisTemplate;<br><br>    public SimpleRedisLock(String name, StringRedisTemplate stringRedisTemplate) {<br>        this.name = name;<br>        this.stringRedisTemplate = stringRedisTemplate;<br>    }<br><br>    \/\/\u83b7\u53d6\u9501<br>    @Override<br>    public boolean tryLock(long timeoutSec) {<br>        String key = <em>KEY_PREFIX <\/em>+ name;<br>        \/\/\u7ebf\u7a0b\u6807\u8bc6:JVM\u5b9e\u4f8b\u7ea7UUID+\u7ebf\u7a0bID<br>        String uniqueLockId = <em>ID_PREFIX<\/em>+Thread.<em>currentThread<\/em>().getId();<br><br>        long threadId = Thread.<em>currentThread<\/em>().getId();<br>        Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(key, uniqueLockId, timeoutSec, TimeUnit.<em>SECONDS<\/em>);<br><br>        return BooleanUtil.<em>isTrue<\/em>(success);<br>    }<br><br>    \/\/\u91ca\u653e\u9501<br>    @Override<br>    public void unlock() {<br>        \/\/\u83b7\u53d6\u6807\u8bc6<br>        String lockId = stringRedisTemplate.opsForValue().get(<em>KEY_PREFIX <\/em>+ name)<br>        String uniqueLockId = <em>ID_PREFIX<\/em>+Thread.<em>currentThread<\/em>().getId();<br><br>        if (lockId.equals(uniqueLockId)) {<br>            stringRedisTemplate.delete(<em>KEY_PREFIX <\/em>+ name);<br>        }<br>    }<br>}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">4.3Redis\u5206\u5e03\u5f0f\u9501\u539f\u5b50\u6027\u95ee\u9898\uff1a<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">\u4ecd\u5b58\u5728\u7684\u95ee\u9898\uff1a<\/h3>\n\n\n\n<p>\u5230\u6b64\u5904\uff0c\u5373\u4f7f\u52a0\u4e0a\u4e86\u552f\u4e00\u6807\u8bc6\u7528\u4e8e\u63a7\u5236\u91ca\u653e\u9501\uff0c\u4f46\u662f\u4ecd\u7136\u6ca1\u6709\u5f7b\u5e95\u89e3\u51b3\u8bef\u5220\u95ee\u9898\uff1a<br>\u7ebf\u7a0bA\u5728\u6267\u884c\u4e1a\u52a1\u540e\uff0c\u62ff\u5230\u4e86\u9501\u7684\u6807\u8bc6\uff0c\u68c0\u67e5\u4e00\u81f4\u540e\uff0c\u963b\u585e\u4e86\uff0c\u6b64\u65f6\u8d85\u65f6\u91ca\u653e\u9501\u3002\u7ebf\u7a0bB\u5c31\u53ef\u4ee5\u62ff\u5230\u9501\uff0c\u6267\u884c\u4e1a\u52a1\uff0c\u5728\u8fd9\u671f\u95f4\uff0cA\u53c8\u88ab\u5524\u9192\uff0c\u56e0\u4e3a\u5df2\u7ecf\u68c0\u67e5\u8fc7\u4e00\u81f4\u6027\uff0c\u5c31\u5728B\u6267\u884c\u4e1a\u52a1\u8fc7\u7a0b\u4e2d\u91ca\u653e\u4e86\u5b9e\u9645\u5c5e\u4e8eB\u7684\u9501\u3002\u6b64\u65f6\u7ebf\u7a0bC\u53c8\u53ef\u4ee5\u62ff\u5230\u9501&#8230;<\/p>\n\n\n\n<p>\u800c\u9020\u6210\u4ee5\u4e0a\u95ee\u9898\u7684\u539f\u56e0\u5c31\u662f\u201c\u5224\u65ad\u6807\u8bc6\u201d\u548c\u201c\u91ca\u653e\u9501\u201d\u4e24\u4e2a\u64cd\u4f5c\u4e4b\u95f4\u6ca1\u6709\u539f\u5b50\u6027\uff0c\u6b64\u5904\u9009\u62e9\u4f7f\u7528Lua\u811a\u672c\uff0c\u5728\u4e00\u4e2a\u811a\u672c\u4e2d\u7f16\u5199\u591a\u6761Redis\u547d\u4ee4\u6765\u5b9e\u73b0\u591a\u6761\u547d\u4ee4\u7684\u539f\u5b50\u6027\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Lua\u811a\u672c\uff1a<\/h3>\n\n\n\n<p><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>-- \u6b63\u786e\u7684\u91ca\u653e\u9501\u811a\u672c\uff1a\u6821\u9a8c\u6301\u6709\u8005\u8eab\u4efd\u540e\u518d\u5220\u9664\n-- KEYS&#91;1]\uff1a\u9501\u7684\u952e\uff08\u5982lock:order\uff09\n-- ARGV&#91;1]\uff1a\u5f53\u524d\u5ba2\u6237\u7aef\u7684\u552f\u4e00\u6807\u8bc6\uff08\u5982clientA:123\uff09\nif redis.call('get', KEYS&#91;1]) == ARGV&#91;1] then\n    redis.call('del', KEYS&#91;1])  -- \u53ea\u6709\u6301\u6709\u8005\u5339\u914d\uff0c\u624d\u5220\u9664\u9501\n    return 1  -- \u91ca\u653e\u6210\u529f\nelse\n    return 0  -- \u9501\u4e0d\u5c5e\u4e8e\u81ea\u5df1\uff0c\u4e0d\u5220\u9664\nend<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">java\u8c03\u7528Lua\u811a\u672c\uff1a<\/h3>\n\n\n\n<p><strong>RedisTemplate \u8c03\u7528 Lua \u811a\u672c\u7684\u5173\u952e API<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong><code>execute<\/code>&nbsp;\u65b9\u6cd5<\/strong>\uff1a<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;T&gt; T execute(RedisScript&lt;T&gt; script, List&lt;K&gt; keys, Object... args)<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u53c2\u6570\u8bf4\u660e<\/strong>\uff1a\n<ul class=\"wp-block-list\">\n<li><code>script<\/code>\uff1a\u5c01\u88c5 Lua \u811a\u672c\u7684<code>RedisScript<\/code>\u5bf9\u8c61<\/li>\n\n\n\n<li><code>keys<\/code>\uff1a\u4f20\u9012\u7ed9 Lua \u811a\u672c\u7684\u952e\u5217\u8868\uff08\u5bf9\u5e94 Lua \u4e2d\u7684<code>KEYS<\/code>\u6570\u7ec4\uff09<\/li>\n\n\n\n<li><code>args<\/code>\uff1a\u4f20\u9012\u7ed9 Lua \u811a\u672c\u7684\u53c2\u6570\u5217\u8868\uff08\u5bf9\u5e94 Lua \u4e2d\u7684<code>ARGV<\/code>\u6570\u7ec4\uff09<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>\u8fd4\u56de\u503c<\/strong>\uff1a\u6267\u884c\u7ed3\u679c\uff0c\u7c7b\u578b\u7531<code>RedisScript<\/code>\u6307\u5b9a<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/\u521d\u59cb\u5316Lua\u811a\u672c\n    private static final DefaultRedisScript&lt;Long&gt; UNLOCK_SCRIPT;\n    static {\n        UNLOCK_SCRIPT=new DefaultRedisScript&lt;&gt;();\n        UNLOCK_SCRIPT.setLocation(new ClassPathResource(\".\/unlock.lua\"));\n        UNLOCK_SCRIPT.setResultType(Long.class);\n    }\n\n...............\n\n\/\/\u91ca\u653e\u9501\n    @Override\n    public void unlock() {\n        stringRedisTemplate.execute(\n                UNLOCK_SCRIPT,\n                Collections.singletonList(KEY_PREFIX + name),\n                ID_PREFIX+Thread.currentThread().getId()\n                );\n    }<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\u4ecd\u5b58\u5728\u7684\u95ee\u9898\uff1a<\/h3>\n\n\n\n<p>\u5230\u6b64\u4e3a\u6b62\uff0c\u5206\u5e03\u5f0f\u9501\u5df2\u7ecf\u5148\u5bf9\u5b8c\u5584\u4e86\uff0c\u4f46\u662f\u4ecd\u7136\u6709\u5e76\u53d1\u5b89\u5168\u95ee\u9898\uff1a<\/p>\n\n\n\n<p>\u7ebf\u7a0bA\u62ff\u5230\u9501\uff0c\u8fdb\u5165\u5230\u4e34\u754c\u533a\uff1acreateOrder\uff0c\u7ed3\u679c\u88ab\u963b\u585e\uff08\u6570\u636e\u5e93\u6162\u3001GC \u6296\u52a8\u3001Redis \u54cd\u5e94\u6162&#8230;\uff09\uff0c\u8d85\u65f6\u91ca\u653e\u4e86\u9501\u3002\u6b64\u65f6B\u62ff\u5230\u4e86\u9501\uff0c\u4e5f\u8fdb\u5165\u4e86\u4e34\u754c\u533a\uff1acreateOrder\u3002\u5c31\u53ef\u80fd\u4ea4\u66ff\u6267\u884ccreateOrder\u5185\u90e8\u4ee3\u7801\uff0c\u4ea7\u751f\u4e86\u5e76\u53d1\u5b89\u5168\u95ee\u9898\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">4.4 Redission<\/h2>\n\n\n\n<p>\u518d\u6765\u8ba8\u8bba\u4e0b\u4e0a\u8ff0\u81ea\u5df1\u5b9e\u73b0\u7684\u5206\u5e03\u5f0f\u9501\u7684\u95ee\u9898\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u4e0d\u53ef\u91cd\u5165\uff1a\u540c\u4e00\u7ebf\u7a0b\u65e0\u6cd5\u591a\u6b21\u83b7\u53d6\u540c\u4e00\u628a\u9501\u3002\n<ul class=\"wp-block-list\">\n<li>\u540c\u4e00\u7ebf\u7a0b\u5185\u65b9\u6cd5A\u62ff\u5230\u4e86\u9501\uff0c\u6267\u884c\u65b9\u6cd5B\uff0c\u4f46\u662f\u65b9\u6cd5B\u4e5f\u8981\u8fd9\u628a\u9501\uff0c\u5c31\u6b7b\u9501\u4e86\u3002<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>\u4e0d\u53ef\u91cd\u8bd5\uff1a\u83b7\u53d6\u9501\u5931\u8d25\u53ea\u5c1d\u8bd5\u4e00\u6b21\u5c31\u8fd4\u56defalse\uff0c\u6ca1\u6709\u91cd\u8bd5\u673a\u5236<\/li>\n\n\n\n<li>\u8d85\u65f6\u91ca\u653e\uff1a\u8d85\u65f6\u91ca\u653e\u867d\u7136\u53ef\u4ee5\u907f\u514d\u6b7b\u9501\uff0c\u4f46\u662f\u4e1a\u52a1\u6267\u884c\u8fc7\u957f\uff0c\u5c31\u4f1a\u5bfc\u81f4\u9501\u91ca\u653e\uff0c\u9020\u6210\u5e76\u53d1\u5b89\u5168\u95ee\u9898\u3002\uff08\u5177\u4f53\u573a\u666f\u89c1\u4e0a\uff09<\/li>\n\n\n\n<li>\u4e3b\u4ece\u4e00\u81f4\u6027\uff1a\uff08\u8bfb\u5199\u5206\u79bb\uff08\u4e3b\u5199\u8bfb\u5b58\uff09\uff09\u3002\u4e3b\u4ece\u540c\u6b65\u51fa\u73b0\u5ef6\u8fdf\u65f6\uff0c\u4e3b\u8282\u70b9\u5b95\u673a\uff0c\u53ef\u80fd\u6709\u591a\u4e2a\u7ebf\u7a0b\u62ff\u5230\u9501\u3002<\/li>\n<\/ul>\n\n\n\n<p>\u4e3a\u4e86\u89e3\u51b3\u8fd9\u4e9b\u95ee\u9898\uff0c\u4f7f\u7528Redisson\uff1a<\/p>\n\n\n\n<p>Redisson \u662f\u57fa\u4e8e Redis \u7684 Java \u9a7b\u5185\u5b58\u6570\u636e\u7f51\u683c\uff0c\u63d0\u4f9b\u5206\u5e03\u5f0f\u548c\u53ef\u6269\u5c55\u7684 Java \u6570\u636e\u7ed3\u6784\uff0c\u7b80\u5316 Redis \u4f7f\u7528\u5e76\u589e\u5f3a\u5206\u5e03\u5f0f\u7cfb\u7edf\u80fd\u529b\u3002\u4ee5\u4e0b\u662f\u5176\u6838\u5fc3\u529f\u80fd\uff1a<\/p>\n\n\n\n<p><strong>1. \u5206\u5e03\u5f0f\u9501\u4e0e\u540c\u6b65\u5668<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u53ef\u91cd\u5165\u9501<\/strong>\uff08RLock\uff09\uff1a\u652f\u6301\u81ea\u52a8\u7eed\u671f\u7684\u5206\u5e03\u5f0f\u9501\uff0c\u907f\u514d\u4e1a\u52a1\u8d85\u65f6\u5bfc\u81f4\u9501\u63d0\u524d\u91ca\u653e\u3002<\/li>\n\n\n\n<li><strong>\u8bfb\u5199\u9501<\/strong>\uff08RReadWriteLock\uff09\uff1a\u5141\u8bb8\u591a\u4e2a\u8bfb\u64cd\u4f5c\u6216\u5355\u4e2a\u5199\u64cd\u4f5c\uff0c\u63d0\u5347\u5e76\u53d1\u6027\u80fd\u3002<\/li>\n\n\n\n<li><strong>\u4fe1\u53f7\u91cf<\/strong>\uff08RSemaphore\uff09\u3001<strong>\u95ed\u9501<\/strong>\uff08RLatch\uff09\uff1a\u63a7\u5236\u5206\u5e03\u5f0f\u73af\u5883\u4e0b\u7684\u8d44\u6e90\u8bbf\u95ee\u548c\u7ebf\u7a0b\u534f\u4f5c\u3002<\/li>\n<\/ul>\n\n\n\n<p><strong>2. \u5206\u5e03\u5f0f\u96c6\u5408\u4e0e\u5bf9\u8c61<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u96c6\u5408\u6846\u67b6<\/strong>\uff1a\u5206\u5e03\u5f0f&nbsp;<code>Map<\/code>\u3001<code>List<\/code>\u3001<code>Set<\/code>\u3001<code>Queue<\/code>&nbsp;\u7b49\uff0c\u652f\u6301\u96c6\u7fa4\u73af\u5883\u4e0b\u7684\u6570\u636e\u5171\u4eab\u3002<\/li>\n\n\n\n<li><strong>\u7279\u6b8a\u7ed3\u6784<\/strong>\uff1a<code>LocalCachedMap<\/code>\uff08\u672c\u5730\u7f13\u5b58 + \u8fdc\u7a0b\u540c\u6b65\uff09\u3001<code>Geo<\/code>\uff08\u5730\u7406\u4f4d\u7f6e\u64cd\u4f5c\uff09\u3001<code>BloomFilter<\/code>\uff08\u5e03\u9686\u8fc7\u6ee4\u5668\uff09\u3002<\/li>\n<\/ul>\n\n\n\n<p><strong>3. \u9ad8\u53ef\u7528\u4e0e\u96c6\u7fa4\u652f\u6301<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u9002\u914d Redis&nbsp;<strong>\u5355\u673a<\/strong>\u3001<strong>\u4e3b\u4ece<\/strong>\u3001<strong>\u54e8\u5175<\/strong>\u3001<strong>\u96c6\u7fa4<\/strong>\u6a21\u5f0f\uff0c\u81ea\u52a8\u6545\u969c\u8f6c\u79fb\u3002<\/li>\n\n\n\n<li>\u8fde\u63a5\u6c60\u7ba1\u7406\u4f18\u5316\u6027\u80fd\uff0c\u51cf\u5c11\u9891\u7e41\u8fde\u63a5\u5f00\u9500\u3002<\/li>\n<\/ul>\n\n\n\n<p><strong>4. \u5f02\u6b65\u4e0e\u54cd\u5e94\u5f0f\u7f16\u7a0b<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u652f\u6301&nbsp;<code>CompletableFuture<\/code>&nbsp;\u548c&nbsp;<code>RxJava<\/code>\uff0c\u975e\u963b\u585e\u8c03\u7528\u63d0\u5347\u541e\u5410\u91cf\u3002<\/li>\n<\/ul>\n\n\n\n<p><strong>5. \u5206\u5e03\u5f0f\u670d\u52a1<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u8fdc\u7a0b\u670d\u52a1<\/strong>\uff08RRemoteService\uff09\uff1a\u8de8\u8282\u70b9\u7684 Java \u65b9\u6cd5\u8c03\u7528\u3002<\/li>\n\n\n\n<li><strong>\u5206\u5e03\u5f0f\u6267\u884c\u5668<\/strong>\uff08RExecutorService\uff09\uff1a\u96c6\u7fa4\u73af\u5883\u4e0b\u7684\u4efb\u52a1\u8c03\u5ea6\u4e0e\u6267\u884c\u3002<\/li>\n<\/ul>\n\n\n\n<p><strong>6. \u5176\u4ed6\u7279\u6027<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u7f13\u5b58\u7b56\u7565<\/strong>\uff1aTTL \u8fc7\u671f\u3001LRU \u6dd8\u6c70\u3001\u672c\u5730\u4e8c\u7ea7\u7f13\u5b58\u3002<\/li>\n\n\n\n<li><strong>\u4e8b\u4ef6\u76d1\u542c<\/strong>\uff1a\u76d1\u542c\u5206\u5e03\u5f0f\u5bf9\u8c61\u7684\u53d8\u66f4\u4e8b\u4ef6\uff08\u5982\u9501\u91ca\u653e\u3001Map \u66f4\u65b0\uff09\u3002<\/li>\n<\/ul>\n\n\n\n<p><strong>\u6838\u5fc3\u4f18\u52bf<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>API \u7b80\u6d01<\/strong>\uff1a\u4e0e Java \u539f\u751f\u63a5\u53e3\u9ad8\u5ea6\u4e00\u81f4\uff0c\u5b66\u4e60\u6210\u672c\u4f4e\u3002<\/li>\n\n\n\n<li><strong>\u53ef\u9760\u6027\u5f3a<\/strong>\uff1a\u89e3\u51b3\u5206\u5e03\u5f0f\u9501\u5e38\u89c1\u95ee\u9898\uff08\u5982\u6b7b\u9501\u3001\u8bef\u91ca\u653e\uff09\u3002<\/li>\n\n\n\n<li><strong>\u6027\u80fd\u4f18\u5316<\/strong>\uff1a\u672c\u5730\u7f13\u5b58\u3001\u6279\u91cf\u64cd\u4f5c\u51cf\u5c11 Redis \u8bbf\u95ee\u3002<\/li>\n<\/ul>\n\n\n\n<p><strong>\u5178\u578b\u573a\u666f<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u5206\u5e03\u5f0f\u9501\uff08\u79d2\u6740\u3001\u5e93\u5b58\u6263\u51cf\uff09<\/li>\n\n\n\n<li>\u5206\u5e03\u5f0f\u7f13\u5b58\uff08\u70ed\u70b9\u6570\u636e\u5171\u4eab\uff09<\/li>\n\n\n\n<li>\u4efb\u52a1\u7f16\u6392\u4e0e\u534f\u4f5c\uff08\u5206\u5e03\u5f0f\u8ba1\u7b97\uff09<\/li>\n\n\n\n<li>\u6570\u636e\u5171\u4eab\uff08\u5206\u5e03\u5f0f\u4f1a\u8bdd\u7ba1\u7406\uff09<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">4.4.1 Redission\u5feb\u901f\u5165\u95e8\uff1a<\/h3>\n\n\n\n<p>Maven\u4f9d\u8d56\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;!--redisson--&gt;<br>&lt;dependency&gt;<br>    &lt;groupId&gt;org.redisson&lt;\/groupId&gt;<br>    &lt;artifactId&gt;redisson&lt;\/artifactId&gt;<br>    &lt;version&gt;3.13.6&lt;\/version&gt;<br>&lt;\/dependency&gt;<\/code><\/pre>\n\n\n\n<p>\u914d\u7f6e\u7c7b\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@Configuration\npublic class RedissonConfig {\n    @Bean\n    public RedissonClient redissonClient(){\n        \/\/\u914d\u7f6e\n        Config config = new Config();\n        config.useSingleServer().setAddress(\"redis:\/\/127.0.0.1:6379\").setPassword(\"123456\");\n        \/\/\u521b\u5efaRedissonClient\u5bf9\u8c61\n        return Redisson.create(config);\n    }\n}<\/code><\/pre>\n\n\n\n<p>\u4fee\u6539\u4e1a\u52a1\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/SimpleRedisLock simpleRedisLock = new SimpleRedisLock(\"order:\"+userId, stringRedisTemplate);<br>RLock lock = redissonClient.getLock(\"lock:order:\" + userId);<br><br>boolean success = lock.tryLock();<br>if (!success){<br>    return Result.<em>fail<\/em>(\"\u8bf7\u52ff\u91cd\u590d\u4e0b\u5355\");<br>}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">4.4.2 Redission\u53ef\u91cd\u5165\u9501\u539f\u7406\uff1a<\/h3>\n\n\n\n<p>\u6dfb\u52a0\u5b57\u6bb5value\u8868\u793a\u8fdb\u5165\u4e34\u754c\u533a\\\u62ff\u5230\u9501\u7684\u65b9\u6cd5\u4e2a\u6570\uff08\u540c\u4e00\u7ebf\u7a0b\u7684\uff09\uff0c\u6807\u8bc6\u4f7f\u7528\u7ebf\u7a0b\u7ea7\u6807\u8bc6\uff0c\u4f7f\u5f97\u540c\u4e00\u7ebf\u7a0b\u5185\u7684\u65b9\u6cd5\u53ef\u4ee5\u91cd\u5165\u9501\u3002\u62ff\u9501\u65f6\u5148\u6839\u636e\u6807\u8bc6\u5224\u65ad\u662f\u5426\u662f\u540c\u4e00\u7ebf\u7a0b\u7684\uff0c\u662f\u5219value+1\uff0c\u8fdb\u5165\u4e34\u754c\u533a\uff1b\u91ca\u653e\u9501\u65f6\u5148\u8ba9value-1\uff0cvalue\u4e3a0\u65f6\u624d\u771f\u6b63\u91ca\u653e\uff0c\u5426\u5219\u53ea\u662f\u5ef6\u957f\u4e0b\u9501\u7684\u6709\u6548\u671f\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">4.4.3 Redission\u7684\u9501\u91cd\u8bd5\u548c\u770b\u95e8\u72d7\uff1a<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u9501\u91cd\u8bd5\u673a\u5236<\/strong>\uff1a<br>\u4f7f\u7528\u770b\u95e8\u72d7\u673a\u5236\u914d\u5408\u516c\u5e73\u9501\u961f\u5217\u3002\u83b7\u53d6\u5931\u8d25\u65f6\u7ebf\u7a0b\u8fdb\u5165\u7b49\u5f85\u961f\u5217\uff0c\u901a\u8fc7\u8ba2\u9605\u673a\u5236\u63a5\u6536\u9501\u91ca\u653e\u901a\u77e5\uff0c\u81ea\u52a8\u91cd\u8bd5\u83b7\u53d6\u9501\uff0c\u907f\u514d\u5fd9\u7b49\u5f85\u3002<\/li>\n\n\n\n<li><strong>\u8d85\u65f6\u7eed\u7ea6<\/strong>\uff1a<br>\u9ed8\u8ba4\u9501\u6301\u6709\u65f6\u95f4 30 \u79d2\uff0c\u540e\u53f0\u770b\u95e8\u72d7\u7ebf\u7a0b\u6bcf 10 \u79d2\uff08\u9501\u8d85\u65f6\u65f6\u95f4\u7684 1\/3\uff09\u81ea\u52a8\u7eed\u7ea6\u3002\u4e1a\u52a1\u672a\u6267\u884c\u5b8c\u65f6\u4f1a\u4e0d\u65ad\u5ef6\u957f\u9501\u7684\u8fc7\u671f\u65f6\u95f4\uff0c\u9632\u6b62\u63d0\u524d\u91ca\u653e\u3002<\/li>\n<\/ol>\n\n\n\n<p>\u6ce8\u610f\uff1a<strong>\u663e\u5f0f\u6307\u5b9a\u9501\u7684\u6709\u6548\u671f<\/strong>\uff08\u5982<code>lock.lock(10, TimeUnit.SECONDS)<\/code>\uff09\u65f6\uff0cRedisson \u4f1a<strong>\u7981\u7528\u770b\u95e8\u72d7\u81ea\u52a8\u7eed\u7ea6\u673a\u5236<\/strong>\u3002\u800c\u770b\u95e8\u72d7\u7684\u9ed8\u8ba4\u65f6\u95f4\u53d8\u91cf\u53ef\u4ee5\u5728\u914d\u7f6e\u7c7b\u91cc\u914d\u7f6e\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">4.4.4 Redission\u89e3\u51b3\u4e2d\u4ece\u4e00\u81f4\u6027\uff1amutiLock\uff1a<\/h3>\n\n\n\n<p><strong>Redisson \u7684 RedissonMultiLock <\/strong>\uff1a\u591a\u4e2a\u72ec\u7acb\u7684Redis\u8282\u70b9\uff0c\u5fc5\u987b\u5728<strong>\u6240\u6709\u8282\u70b9<\/strong>\uff08\u548cRedLock\u7684\u201c<strong>\u8d85\u8fc7\u534a\u6570<\/strong>\u201d\u6bd4\u8f83\uff09\u90fd\u83b7\u53d6\u91cd\u5165\u9501\u3002\u624d\u7b97\u83b7\u53d6\u9501\u6210\u529f\u3002<br><\/p>\n\n\n\n<p>\u521b\u5efa MultiLock \u5b9e\u4f8b\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ \u65b9\u5f0f1\uff1a\u901a\u8fc7\u6784\u9020\u51fd\u6570\u4f20\u5165\u591a\u4e2a RLock \u5b9e\u4f8b\nRedissonMultiLock multiLock = new RedissonMultiLock(lock1, lock2, lock3); <\/code><\/pre>\n\n\n\n<p>\u52a0\u9501\u65b9\u6cd5\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ \u963b\u585e\u5f0f\u52a0\u9501\uff1a\u4e00\u76f4\u7b49\u5f85\u76f4\u5230\u83b7\u53d6\u6240\u6709\u9501\n void lock();\n \n\/\/ \u5e26\u8d85\u65f6\u7684\u963b\u585e\u5f0f\u52a0\u9501\uff1a\u7b49\u5f85\u6307\u5b9a\u65f6\u95f4\u4ecd\u672a\u83b7\u53d6\u6240\u6709\u9501\u5219\u8fd4\u56de\nboolean tryLock(long waitTime, TimeUnit unit);\n\n\/\/ \u5e26\u8d85\u65f6\u548c\u9501\u8fc7\u671f\u65f6\u95f4\u7684\u52a0\u9501\uff1a\u81ea\u52a8\u91ca\u653e\u9501\uff0c\u9632\u6b62\u6b7b\u9501 \nboolean tryLock(long waitTime, long leaseTime, TimeUnit unit);<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">4.5 \u79d2\u6740\u4f18\u5316<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">4.5.1 \u5f02\u6b65\u79d2\u6740<\/h3>\n\n\n\n<p>\u5f53\u524d\u4e1a\u52a1\u903b\u8f91\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u67e5\u8be2\u4f18\u60e0\u5238<\/li>\n\n\n\n<li><strong>\u5224\u65ad\u79d2\u6740\u5e93\u5b58<\/strong><\/li>\n\n\n\n<li>\u67e5\u8be2\u8ba2\u5355<\/li>\n\n\n\n<li><strong>\u6821\u9a8c\u4e00\u4eba\u4e00\u5355<\/strong><\/li>\n\n\n\n<li>\u51cf\u5e93\u5b58<\/li>\n\n\n\n<li>\u521b\u5efa\u8ba2\u5355<\/li>\n<\/ol>\n\n\n\n<p>\u4ee5\u4e0a\u6b65\u9aa4\u4e32\u884c\u6267\u884c\uff0c\u5e76\u4e14\u4f7f\u7528\u4e86\u5206\u5e03\u5f0f\u9501\u7b49\u5de5\u5177\uff0c\u5bfc\u81f4\u6548\u7387\u5f88\u4f4e\u3002\u6240\u4ee5\u5c06\u65e0\u9700\u67e5\u5e93\u76842,4\u62c6\u51fa\u6765\u4ea4\u7ed9redis\uff08Lua\uff09\u901a\u8fc7\u6761\u4ef6\u540e\u5c06{\u4f18\u60e0\u5238id\u3001\u7528\u6237id\u3001\u8ba2\u5355id}\u4ea4\u7ed9\u963b\u585e\u961f\u5217\uff0c\u7136\u540e\u76f4\u63a5\u5411\u524d\u7aef\u8fd4\u56de\u8ba2\u5355id\u3002\u5f02\u6b65\u5c06\u5176\u4ed6\u4efb\u52a1\u7531\u6d88\u606f\u961f\u5217\u4ea4\u7ed9\u53e6\u4e00\u4e2a\u7ebf\u7a0b\u3002<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><div class='fancybox-wrapper lazyload-container-unload' data-fancybox='post-images' href='http:\/\/www.cmd137blog.top\/wp-content\/uploads\/2025\/03\/image-3.png'><img class=\"lazyload lazyload-style-1\" src=\"data:image\/svg+xml;base64,PCEtLUFyZ29uTG9hZGluZy0tPgo8c3ZnIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgc3Ryb2tlPSIjZmZmZmZmMDAiPjxnPjwvZz4KPC9zdmc+\"  loading=\"lazy\" decoding=\"async\" width=\"848\" height=\"324\" data-original=\"http:\/\/www.cmd137blog.top\/wp-content\/uploads\/2025\/03\/image-3.png\" src=\"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB\/AAffA0nNPuCLAAAAAElFTkSuQmCC\" alt=\"\" class=\"wp-image-956\"  sizes=\"auto, (max-width: 848px) 100vw, 848px\" \/><\/div><\/figure>\n\n\n\n<p>lua\u811a\u672c\u5185\u7684\u201c\u51cf\u5e93\u5b58\u201d\u201c\u6dfb\u52a0userId\u201d\u90fd\u662f\u9884\u64cd\u4f5c\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">4.5.2 \u57fa\u4e8eRedis\u548c\u963b\u585e\u961f\u5217\u5b8c\u6210\u79d2\u6740\u8d44\u683c\u5224\u65ad\uff1a<\/h3>\n\n\n\n<p>\u9700\u6c42\uff1a<br>\u2460 \u65b0\u589e\u79d2\u6740\u4f18\u60e0\u5238\u7684\u540c\u65f6\uff0c\u5c06\u4f18\u60e0\u5238\u4fe1\u606f\u4fdd\u5b58\u5230 Redis \u4e2d<br>\u2461 \u57fa\u4e8e Lua \u811a\u672c\uff0c\u5224\u65ad\u79d2\u6740\u5e93\u5b58\u3001\u4e00\u4eba\u4e00\u5355\uff0c\u51b3\u5b9a\u7528\u6237\u662f\u5426\u62a2\u8d2d\u6210\u529f<br>\u2462 \u5982\u679c\u62a2\u8d2d\u6210\u529f\uff0c\u5c06\u4f18\u60e0\u5238 id \u548c\u7528\u6237 id \u5c01\u88c5\u540e\u5b58\u5165\u963b\u585e\u961f\u5217<br>\u2463 \u5f00\u542f\u7ebf\u7a0b\u4efb\u52a1\uff0c\u4e0d\u65ad\u4ece\u963b\u585e\u961f\u5217\u4e2d\u83b7\u53d6\u4fe1\u606f\uff0c\u5b9e\u73b0\u5f02\u6b65\u4e0b\u5355\u529f\u80fd<\/p>\n\n\n\n<p><strong>lua\u811a\u672c\uff1a<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>-- \u53c2\u6570\u5b9a\u4e49<br>local voucherId = ARGV&#91;<em>1<\/em>]<br>local userId = ARGV&#91;<em>2<\/em>]<br>local orderKey = 'seckill:order:' .. voucherId<br>local stockKey = 'seckill:stock:' .. voucherId<br><br>-- \u5e93\u5b58\u68c0\u67e5<br>if (tonumber(redis.call('get', stockKey)) &lt;= <em>0<\/em>) then<br>    return <em>1<br><\/em>end<br><br>-- \u4e00\u4eba\u4e00\u5355\u6821\u9a8c<br>if (redis.call('sismember', orderKey, userId) == <em>1<\/em>) then<br>    return <em>2<br><\/em>end<br><br>-- \u6263\u51cf\u5e93\u5b58\u5e76\u8bb0\u5f55\u8ba2\u5355<br>redis.call('decr', stockKey)<br>redis.call('sadd', orderKey, userId)<br>return <em>0<br><\/em><em>    <\/em><\/code><\/pre>\n\n\n\n<p>\u4e1a\u52a1\u4fee\u6539\uff1a<br><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@Slf4j\n@Service\npublic class VoucherOrderServiceImpl extends ServiceImpl&lt;VoucherOrderMapper, VoucherOrder&gt; implements IVoucherOrderService {\n    @Autowired\n    private ISeckillVoucherService seckillVoucherService;\n\n    @Autowired\n    private StringRedisTemplate stringRedisTemplate;\n\n    @Autowired\n    @Lazy\n    private VoucherOrderServiceImpl proxySelf;\n\n    @Autowired\n    private RedissonClient redissonClient;\n\n    private RedisIdWorker idWorker;\n\n    \/\/\u521d\u59cb\u5316Lua\u811a\u672c\n    private static final DefaultRedisScript&lt;Long&gt; <em>SECKILL_SCRIPTS<\/em>;\n    static {\n        <em>SECKILL_SCRIPTS <\/em>=new DefaultRedisScript&lt;&gt;();\n        <em>SECKILL_SCRIPTS<\/em>.setLocation(new ClassPathResource(\".\/scripts\/seckill.lua\"));\n        <em>SECKILL_SCRIPTS<\/em>.setResultType(Long.class);\n    }\n\n    private BlockingQueue&lt;VoucherOrder&gt; orderTasks = new ArrayBlockingQueue&lt;&gt;(1024*1024);\n    private ExecutorService SECKILL_ORDER_EXECUTOR = Executors.<em>newSingleThreadExecutor<\/em>();\n\n    private class VoucherOrderTask implements Runnable{\n        @Override\n        public void run() {\n            while (true){\n                try {\n                    \/\/1.\u4ece\u963b\u585e\u961f\u5217\u91cc\u83b7\u53d6\u4efb\u52a1\n                    VoucherOrder order = orderTasks.take();\n                    \/\/2.\u6570\u636e\u5e93\u64cd\u4f5c\uff1a\u51cf\u5e93\u5b58+\u6dfb\u52a0\u8ba2\u5355\n                    proxySelf.asyncCreateOrder(order);\/\/\u4f9d\u7136\u4f7f\u7528\u81ea\u5df1\u6ce8\u5165\u81ea\u5df1\u7684\u52a8\u6001\u4ee3\u7406\u5bf9\u8c61\u7684\u65b9\u9632\u6b62\u4e8b\u52a1\u5931\u6548\n                } catch (InterruptedException e) {\n                    <em>log<\/em>.info(\"\u5f02\u6b65\u4efb\u52a1\u5931\u8d25\");\n                }\n\n            }\n        }\n    }\n\n    \/\/\u670d\u52a1\u4e00\u542f\u52a8\u5c31\u8981\u5f00\u542f\u7ebf\u7a0b\u6c60\u4efb\u52a1\uff0c\u6240\u4ee5\n    @PostConstruct\n    private void ExecutorServiceInit(){\n        SECKILL_ORDER_EXECUTOR.submit(new VoucherOrderTask());\n    }\n\n\n\n    @Override\n    public Result seckillVoucher(Long voucherId) {\n        \/\/1.lua\u811a\u672c\u5224\u65ad\u79d2\u6740\u8d44\u683c\uff08\u539f\u5b50\u6027\u672c\u8eab\u4e5f\u4fdd\u8bc1\u7ebf\u7a0b\u5b89\u5168\uff09\n        Long flag = stringRedisTemplate.execute(\n                <em>SECKILL_SCRIPTS<\/em>,\n                Collections.<em>emptyList<\/em>(),\n                voucherId.toString(),\n                UserHolder.<em>getUser<\/em>().getId().toString()\n        );\n        \/\/2.\u5904\u7406\u5224\u65ad\u7ed3\u679c\n        if (flag == 1){\n            return Result.<em>fail<\/em>(\"\u5e93\u5b58\u4e0d\u8db3\");\n        }else if (flag == 2){\n            return Result.<em>fail<\/em>(\"\u4e0d\u80fd\u91cd\u590d\u4e0b\u5355\");\n        }\n        \/\/3.\u82e5\u6709\u8d44\u683c\uff0c\u628a\u4e0b\u5355\u4fe1\u606f\u4fdd\u5b58\u5230\u963b\u585e\u961f\u5217\n        idWorker=new RedisIdWorker(stringRedisTemplate);\n        Long orderId = idWorker.nextId(\"order\");\n        VoucherOrder order = new VoucherOrder();\n        \/\/\u8ba2\u5355id\n        order.setId(orderId);\n        \/\/\u7528\u6237id\n        order.setUserId(UserHolder.<em>getUser<\/em>().getId());\n        \/\/\u79d2\u6740\u5238id\n        order.setVoucherId(voucherId);\n\n        orderTasks.add(order);\n        \/\/4.\u8fd4\u56de\u8ba2\u5355id\n        return Result.<em>ok<\/em>(orderId);\n    }\n\n\n    @Transactional\n    public void asyncCreateOrder(VoucherOrder order){\n        \/\/\u8fd9\u91cc\u7531\u4e8eRedis\u5c31\u89e3\u51b3\u7684\u8d85\u5356\u3001\u4e00\u4eba\u4e00\u5355\u95ee\u9898\uff0c\u6240\u4ee5\u4e0d\u9700\u8981\u518d\u52a0\u9501\u3001\u5224\u65ad\u4ec0\u4e48\u7684\uff08\u53ea\u8981redis\u4e0d\u5d29\uff09\n        \/\/1.\u6263\u51cf\u5e93\u5b58\n        boolean success = seckillVoucherService.update()\n                .eq(\"voucher_id\", order.getVoucherId())\n                .setSql(\"stock = stock - 1\")\n                .update();\n\n        if (!success){\n            <em>log<\/em>.info(\"\u6263\u51cf\u5e93\u5b58\u5931\u8d25\");\n        }\n\n        \/\/2.\u6dfb\u52a0\u8ba2\u5355\n        save(order);\n    }\n........<\/code><\/pre>\n\n\n\n<p><strong>\u57fa\u4e8e\u963b\u585e\u961f\u5217\u7684\u5f02\u6b65\u79d2\u6740\u5b58\u5728\u7684\u95ee\u9898\uff1a<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u5185\u5b58\u9650\u5236\u95ee\u9898\uff1a\u76ee\u524d\u7684\u963b\u585e\u961f\u5217\u662fJDK\u7684\uff0c\u5b58\u50a8\u5728jvm\u4e2d\uff0c\u5bb9\u6613\u7206\u5185\u5b58\u3002<\/li>\n\n\n\n<li>\u6570\u636e\u5b89\u5168\u95ee\u9898\uff1a\u8ba2\u5355\u4fe1\u606f\u53ef\u80fd\u56e0\u670d\u52a1\u5b95\u673a\u4e22\u5931<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">4.6 \u57fa\u4e8eredis\u6d88\u606f\u961f\u5217\u5b9e\u73b0\u5f02\u6b65\u79d2\u6740\uff1a<\/h2>\n\n\n\n<p>Redis \u5b9e\u73b0\u6d88\u606f\u961f\u5217\u7684\u4e09\u79cd\u65b9\u5f0f\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>list \u7ed3\u6784\uff1a\u57fa\u4e8e List \u7ed3\u6784\u6a21\u62df\u6d88\u606f\u961f\u5217<\/li>\n\n\n\n<li>PubSub\uff1a\u57fa\u672c\u7684\u70b9\u5bf9\u70b9\u6d88\u606f\u6a21\u578b<\/li>\n\n\n\n<li>Stream\uff1a\u6bd4\u8f83\u5b8c\u5584\u7684\u6d88\u606f\u961f\u5217\u6a21\u578b<\/li>\n<\/ul>\n\n\n\n<p>\u6ca1\u4ec0\u4e48\u610f\u4e49\uff0c\u540e\u9762\u5b66Rocket\/Rabit MQ\u3002\u8fd9\u91cc\u76f4\u63a5\u8df3\u8fc7\u3002<\/p>\n\n\n\n<p>\u60f3\u53c2\u8003\u53ef\u4ee5\u53bb\uff1a<a href=\"https:\/\/blog.csdn.net\/qq_66345100\/article\/details\/131986713\">\u9ed1\u9a6c\u70b9\u8bc4\u9879\u76ee\u5b66\u4e60\u7b14\u8bb0\uff0815w\u5b57\u8be6\u89e3\uff0c\u582a\u79f0\u53f2\u4e0a\u6700\u8be6\u7ec6\uff0c\u6b22\u8fce\u6536\u85cf\uff09-CSDN\u535a\u5ba2<\/a><\/p>\n\n\n\n<h1 class=\"wp-block-heading\">5 \u8fbe\u4eba\u63a2\u5e97\uff1a<\/h1>\n\n\n\n<p>\u67e5\u770b\u63a2\u5e97\u7b14\u8bb0\u3001\u90fd\u662f\u7b80\u5355\u7684CRUD,\u4e0d\u591a\u8bf4\u3002<br><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">5.1 \u5b8c\u5584\u70b9\u8d5e\u529f\u80fd\uff1a<\/h2>\n\n\n\n<p><strong>\u9700\u6c42:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u540c\u4e00\u4e2a\u7528\u6237\u53ea\u80fd\u70b9\u8d5e\u4e00\u6b21\uff0c\u518d\u6b21\u70b9\u51fb\u5219\u53d6\u6d88\u70b9\u8d5e<\/li>\n\n\n\n<li>\u5982\u679c\u5f53\u524d\u7528\u6237\u5df2\u7ecf\u70b9\u8d5e\uff0c\u5219\u70b9\u8d5e\u6309\u94ae\u9ad8\u4eae\u663e\u793a\uff08\u524d\u7aef\u5df2\u5b9e\u73b0\uff0c\u5224\u65ad\u5b57\u6bb5 Blog \u7c7b\u7684 isLike \u5c5e\u6027\uff09<\/li>\n<\/ul>\n\n\n\n<p><strong>\u5b9e\u73b0\u6b65\u9aa4:<\/strong><br>\u2460 \u7ed9 Blog \u7c7b\u4e2d\u6dfb\u52a0\u4e00\u4e2a isLike \u5b57\u6bb5\uff0c\u6807\u793a\u662f\u5426\u88ab\u5f53\u524d\u7528\u6237\u70b9\u8d5e<br>\u2461 \u4fee\u6539\u70b9\u8d5e\u529f\u80fd\uff0c\u5229\u7528 Redis \u7684 set \u96c6\u5408\u5224\u65ad\u662f\u5426\u70b9\u8d5e\u8fc7\uff0c\u672a\u70b9\u8d5e\u8fc7\u5219\u70b9\u8d5e\u6570 + 1\uff0c\u5df2\u70b9\u8d5e\u8fc7\u5219\u70b9\u8d5e\u6570 &#8211; 1<br>\u2462 \u4fee\u6539\u6839\u636e id \u67e5\u8be2 Blog \u7684\u4e1a\u52a1\uff0c\u5224\u65ad\u5f53\u524d\u767b\u5f55\u7528\u6237\u662f\u5426\u70b9\u8d5e\u8fc7\uff0c\u8d4b\u503c\u7ed9 isLike \u5b57\u6bb5<br>\u2463 \u4fee\u6539\u5206\u9875\u67e5\u8be2 Blog \u4e1a\u52a1\uff0c\u5224\u65ad\u5f53\u524d\u767b\u5f55\u7528\u6237\u662f\u5426\u70b9\u8d5e\u8fc7\uff0c\u8d4b\u503c\u7ed9 isLike \u5b57\u6bb5<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>\u4f7f\u7528<code>Set<\/code>\uff0c\u56e0\u4e3aSet\u7c7b\u578b\u7684\u6570\u636e\u7ed3\u6784\u5177\u6709<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u4e0d\u91cd\u590d<\/strong>\uff0c\u7b26\u5408\u4e1a\u52a1\u7684\u7279\u70b9\uff0c\u4e00\u4e2a\u7528\u6237\u53ea\u80fd\u70b9\u8d5e\u4e00\u6b21<\/li>\n\n\n\n<li><strong>\u9ad8\u6027\u80fd<\/strong>\uff0cSet\u96c6\u5408\u5185\u90e8\u5b9e\u73b0\u4e86\u9ad8\u6548\u7684\u6570\u636e\u7ed3\u6784(Hash\u8868)<\/li>\n\n\n\n<li><strong>\u7075\u6d3b\u6027<\/strong>\uff0cSet\u96c6\u5408\u53ef\u4ee5\u5b9e\u73b0\u4e00\u5bf9\u591a\uff0c\u4e00\u4e2a\u535a\u5ba2\u53ef\u4ee5\u770b\u5230\u6709\u591a\u4e2a\u7528\u6237\u70b9\u8d5e<\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>@Override\npublic Result queryHotBlog(Integer current) {\n    \/\/ \u6839\u636e\u7528\u6237\u67e5\u8be2\n    Page&lt;Blog&gt; page = query()\n            .orderByDesc(\"liked\")\n            .page(new Page&lt;&gt;(current, SystemConstants.<em>MAX_PAGE_SIZE<\/em>));\n    \/\/ \u83b7\u53d6\u5f53\u524d\u9875\u6570\u636e\n    List&lt;Blog&gt; records = page.getRecords();\n    \/\/ \u67e5\u8be2\u7528\u6237\n    records.forEach(blog -&gt;{\n        queryBlogUser(blog);\n        isBlogLiked(blog);\n    });\n    return Result.<em>ok<\/em>(records);\n}\n\n@Override\npublic Result queryBlogById(Long id) {\n    \/\/1.\u67e5\u8be2blog\n    Blog blog = getById(id);\n    if (blog == null){\n        return Result.<em>fail<\/em>(\"\u6570\u636e\u4e0d\u5b58\u5728\");\n    }\n\n    \/\/2.\u67e5\u8be2blog\u6709\u5173\u7528\u6237\n    queryBlogUser(blog);\n    \/\/3.\u67e5\u8be2\u7528\u6237\u70b9\u8d5e\u8be5BLOG\n    isBlogLiked(blog);\n    return Result.<em>ok<\/em>(blog);\n}\n\n@Override\npublic Result likeBlog(Long id) {\n    \/\/1.\u83b7\u53d6\u7528\u6237\n    Long userId = UserHolder.<em>getUser<\/em>().getId();\n    \/\/2.\u5728redis\u5185\u5224\u65ad\u5f53\u524d\u7528\u6237\u662f\u5426\u5df2\u7ecf\u70b9\u8fc7\u8d5e\n    String key = <em>BLOG_LIKED_KEY<\/em>+id;\n    Boolean isMember = stringRedisTemplate.opsForSet().isMember(key, userId.toString());\n\n    if (BooleanUtil.<em>isFalse<\/em>(isMember)){\n        \/\/2.1\u5982\u679c\u6ca1\u6709\u70b9\u8fc7\uff0c\u53ef\u4ee5\u70b9\u8d5e\n        boolean isSuccess = update().setSql(\"liked = liked + 1\").eq(\"id\", id).update();\n        if (isSuccess){\n            \/\/\u66f4\u65b0\u6210\u529f\n            \/\/\u4fdd\u5b58\u7528\u6237\u5230redis\n            stringRedisTemplate.opsForSet().add(key, userId.toString());\n        }\n\n    }else {\n        \/\/2.2\u5982\u679c\u70b9\u8fc7\uff0c\u53d6\u6d88\u70b9\u8d5e\n        boolean isSuccess = update().setSql(\"liked = liked - 1\").eq(\"id\", id).update();\n        if (isSuccess){\n            \/\/\u66f4\u65b0\u6210\u529f\n            \/\/redis\u5185\u7684\u96c6\u5408\u5220\u9664\u8be5\u7528\u6237\n            stringRedisTemplate.opsForSet().remove(key, userId.toString());\n        }\n    }\n\n    return Result.<em>ok<\/em>();\n}\n\nprivate void queryBlogUser(Blog blog) {\n    Long userId = blog.getUserId();\n    User user = userService.getById(userId);\n    blog.setName(user.getNickName());\n    blog.setIcon(user.getIcon());\n}\n\n<em>\/**\n * \u5224\u65ad\u5f53\u524d\u7528\u6237\u662f\u5426\u70b9\u8d5e\u8be5\u535a\u5ba2\n *\/\n<\/em>private void isBlogLiked(Blog blog) {\n        UserDTO user = UserHolder.getUser();\n        if (user==null){\n            \/\/\u7528\u6237\u672a\u767b\u5f55\uff0c\u65e0\u9700\u67e5\u8be2\u662f\u5426\u70b9\u8fc7\u8d5e\n            return;\n        }\n        Long userId = UserHolder.getUser().getId();\n        String key = BLOG_LIKED_KEY + blog.getId();\n        Double score = stringRedisTemplate.opsForZSet().score(key, userId.toString());\n        blog.setIsLike(score!=null);\n    }<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">5.2 \u70b9\u8d5e\u6392\u884c\u699c\uff1a<\/h2>\n\n\n\n<p><strong>\u9700\u6c42\uff1a<\/strong>\u6309\u7167\u70b9\u8d5e\u65f6\u95f4\u5148\u540e\u987a\u5e8f\uff0c\u627e\u5230\u524d5\u540d\u7528\u6237\u3002<\/p>\n\n\n\n<p>\u60f3\u5230<code>zset\/sorted set<\/code>&nbsp;,\u65e2\u4fdd\u8bc1\u552f\u4e00\u6027\uff0c\u4e5f\u4fdd\u8bc1\u6709\u5e8f\u6027\uff08\u6839\u636escore\u503c\u6392\u5e8f\uff09\u3002<br>\u6539\u9020\u4e1a\u52a1\u4ee3\u7801\uff1a<br>\u5728\u5b58\u50a8\u65f6\u4f7f\u7528zset\uff0c\u4f7f\u7528\u65f6\u95f4\u6233\u4f5c\u4e3ascore\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Double score = stringRedisTemplate.opsForZSet().score(key, userId.toString());\n\n        if (score==null){\n            \/\/2.1\u5982\u679c\u6ca1\u6709\u70b9\u8fc7\uff0c\u53ef\u4ee5\u70b9\u8d5e\n            boolean isSuccess = update().setSql(\"liked = liked + 1\").eq(\"id\", id).update();\n            if (isSuccess){\n                \/\/\u66f4\u65b0\u6210\u529f\n                \/\/\u4fdd\u5b58\u7528\u6237\u5230redis\n                stringRedisTemplate.opsForZSet().add(key, userId.toString(),System.currentTimeMillis());\n            }\n\n        }else {\n            \/\/2.2\u5982\u679c\u70b9\u8fc7\uff0c\u53d6\u6d88\u70b9\u8d5e\n            boolean isSuccess = update().setSql(\"liked = liked - 1\").eq(\"id\", id).update();\n            if (isSuccess){\n                \/\/\u66f4\u65b0\u6210\u529f\n                \/\/redis\u5185\u7684\u96c6\u5408\u5220\u9664\u8be5\u7528\u6237\n                stringRedisTemplate.opsForZSet().remove(key, userId.toString());\n            }\n        }<\/code><\/pre>\n\n\n\n<p>\u67e5\u8be2\u70b9\u8d5e\u6392\u540d\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public Result queryBlogLikedRankById(Long id) {<br>    \/\/\u67e5\u8be2top5\u7684\u70b9\u8d5e\u7528\u6237<br>    String key = <em>BLOG_LIKED_KEY <\/em>+ id;<br>    Set&lt;String&gt; top5 = stringRedisTemplate.opsForZSet().range(key, 0, 4);<br><br>    if (top5==null ||top5.isEmpty()){<br>        return Result.<em>ok<\/em>(Collections.<em>emptyList<\/em>());<br>    }<br><br>    \/\/\u89e3\u6790id<br>    Set&lt;Long&gt; ids = top5.stream().map(Long::<em>valueOf<\/em>).collect(Collectors.<em>toSet<\/em>());<br>    \/\/\u6839\u636e\u7528\u6237id\u67e5\u8be2\u7528\u6237\u5e76\u5c01\u88c5\u4e3aDTO\uff08\u53bb\u9664\u654f\u611f\u4fe1\u606f\uff09<br>    List&lt;User&gt; users = userService.listByIds(ids);<br>    List&lt;UserDTO&gt; userDTOList = users.stream()<br>            .map(user -&gt; BeanUtil.<em>copyProperties<\/em>(user, UserDTO.class))<br>            .collect(Collectors.<em>toList<\/em>());<br>    \/\/\u8fd4\u56de<br>    return Result.<em>ok<\/em>(userDTOList);<br>}<\/code><\/pre>\n\n\n\n<p>\u6b64\u65f6\u6709\u4e2a\u5173\u4e8emysql\u7684\u95ee\u9898\uff1a<\/p>\n\n\n\n<p>List&lt;User&gt; users = userService.listByIds(ids);<br>\u8fd9\u4e00\u53e5\u7684SQL\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT id, phone, nick_name, icon, create_time, update_time \nFROM tb_user \nWHERE id IN (5, 1);\nORDER BY FIELD(<strong>id<\/strong>,id1\uff0cid2,id3...)<\/code><\/pre>\n\n\n\n<p>\u8fd9\u91cc\u4f7f\u7528\u4e86in\uff0cSQL\u67e5\u8be2\u65f6\u4e0d\u6309\u53c2\u6570\u7684\u987a\u5e8f\uff0c\u800c\u662f\u4f1a\u81ea\u52a8\u6309\u4ece\u5c0f\u5230\u5927\u7684\u987a\u5e8f\uff0c\u5bfc\u81f4\u6392\u540d\u51fa\u9519\u3002<br>\u89e3\u51b3\uff1a\u4f7f\u7528 ORDER BY FIELD(id1\uff0cid2,id3&#8230;)\u624b\u52a8\u5f3a\u5236\u6307\u5b9a\u67e5\u8be2\u987a\u5e8f\uff1a<\/p>\n\n\n\n<p>\u4e1a\u52a1\u4fee\u6b63\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public Result queryBlogLikedRankById(Long id) {\n        \/\/\u67e5\u8be2top5\u7684\u70b9\u8d5e\u7528\u6237\n        String key = BLOG_LIKED_KEY + id;\n        Set&lt;String&gt; top5 = stringRedisTemplate.opsForZSet().range(key, 0, 4);\n\n        if (top5==null ||top5.isEmpty()){\n            return Result.ok(Collections.emptyList());\n        }\n\n        \/\/\u89e3\u6790id\n        List&lt;Long&gt; ids = top5.stream().map(Long::valueOf).collect(Collectors.toList());\n        \/\/\u6839\u636e\u7528\u6237id\u67e5\u8be2\u7528\u6237\u5e76\u5c01\u88c5\u4e3aDTO\uff08\u53bb\u9664\u654f\u611f\u4fe1\u606f\uff09\n        StringBuilder stringBuilder= new StringBuilder();\n        ids.forEach(tid -&gt; stringBuilder.append(tid).append(\",\"));\n        String idsStr = stringBuilder.toString();\n\n        \/*\n        SELECT id, phone, nick_name, icon, create_time, update_time\n        FROM tb_user\n        WHERE id IN (5, 1);\n        ORDER BY FIELD(id,id1\uff0cid2,id3...)\n         *\/\n        List&lt;User&gt; users = userService\n                .query()\n                .in(\"id\",ids)\n                .last(\"ORDER BY FIELD(id,\"+idsStr+\")\")\n                .list();\n        List&lt;UserDTO&gt; userDTOList = users.stream()\n                .map(user -&gt; BeanUtil.copyProperties(user, UserDTO.class))\n                .collect(Collectors.toList());\n        \/\/\u8fd4\u56de\n        return Result.ok(userDTOList);\n    }<\/code><\/pre>\n\n\n\n<h1 class=\"wp-block-heading\">6 \u597d\u53cb\u5173\u6ce8\uff1a<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">6.1 \u5173\u6ce8\u548c\u53d6\u5173\uff1a<\/h2>\n\n\n\n<p>\u4e24\u4e2a\u63a5\u53e3\uff1a<br>\u5173\u6ce8\u548c\u53d6\u5173\u7684\u63a5\u53e3\uff1b<br>\u5224\u65ad\u662f\u5426\u5173\u6ce8\u7684\u63a5\u53e3\u3002<\/p>\n\n\n\n<p>\u7531\u4e8e\u5173\u6ce8\u8005\u548c\u88ab\u5173\u6ce8\u8005\u662f\u591a\u5bf9\u591a\u7684\u5173\u7cfb\uff0c\u4f7f\u7528\u4e2d\u95f4\u8868\u3002<\/p>\n\n\n\n<p>\u5173\u6ce8\u5c31\u662f\u65b0\u589e\uff0c\u53d6\u5173\u5c31\u662f\u5220\u9664\u3002<\/p>\n\n\n\n<p>\u5728\u6b64\u8282\u987a\u4fbf\u590d\u4e60\u4e0bmybatis<\/p>\n\n\n\n<p>service\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><em>\/**\n * \u8bbe\u7f6e\u5173\u6ce8\uff08True\uff09\/\u53d6\u5173\uff08False\uff09\n * <\/em><strong><em>@param <\/em><\/strong><em>followUserId\n * <\/em><strong><em>@param <\/em><\/strong><em>isFollow\n * <\/em><strong><em>@return\n <\/em><\/strong><em>*\/\n<\/em>@Override\npublic Result follow(Long followUserId, Boolean isFollow) {\n    Long userId = UserHolder.<em>getUser<\/em>().getId();\n\n    \/\/1.\u5224\u65ad\u662f\u5173\u6ce8\u8fd8\u662f\u53d6\u5173\n    if (isFollow){\n        \/\/1.1 \u5173\u6ce8\uff1a\u65b0\u589e\n        Follow follow = new Follow();\n        follow.setFollowUserId(followUserId);\n        follow.setUserId(userId);\n        follow.setCreateTime(LocalDateTime.<em>now<\/em>());\n        \/\/save(follow);\u590d\u4e60\u4e0bmybatis\n        followMapper.myInsert(follow);\n    }else{\n        \/\/1.2 \u53d6\u5173\uff1a\u5220\u9664\n        \/\/remove(new QueryWrapper&lt;Follow&gt;().eq(\"follow_user_id\", followUserId).eq(\"user_id\", id));\n        followMapper.deleteByfollowUserIdAndUserId(followUserId,userId);\n    }\n    return Result.<em>ok<\/em>();\n}\n\n<em>\/**\n * \u67e5\u8be2\u662f\u5426\u5173\u6ce8\n * <\/em><strong><em>@param <\/em><\/strong><em>followUserId\n * <\/em><strong><em>@return\n <\/em><\/strong><em>*\/\n<\/em>@Override\npublic Result isFollow(Long followUserId) {\n    Long userId = UserHolder.<em>getUser<\/em>().getId();\n    int count = followMapper.isFollowed(userId, followUserId);\n    return Result.<em>ok<\/em>(count&gt;0);\n}<\/code><\/pre>\n\n\n\n<p>mapper\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@Insert(\"INSERT INTO tb_follow (user_id, follow_user_id, create_time)\" +<br>        \" VALUES (#{userId},#{followUserId},#{createTime})\")<br>void myInsert(Follow follow);<br><br>@Delete(\"DELETE FROM tb_follow WHERE follow_user_id=#{followUserId} AND user_id = #{userId}\")<br>void deleteByfollowUserIdAndUserId(@Param(\"followUserId\")Long followUserId,<br>                                   @Param(\"userId\")Long userId);<br><br>@Select(\"SELECT COUNT(1) FROM tb_follow WHERE user_id = #{userId} AND follow_user_id = #{followUserId}\")<br>int isFollowed(@Param(\"userId\") Long userId, @Param(\"followUserId\") Long followUserId);<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">6.2 \u5171\u540c\u5173\u6ce8\uff1a<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">\u9700\u6c42<\/h3>\n\n\n\n<p>\u5229\u7528 Redis \u4e2d\u6070\u5f53\u7684\u6570\u636e\u7ed3\u6784\uff0c\u5b9e\u73b0\u5171\u540c\u5173\u6ce8\u529f\u80fd\u3002\u5728\u535a\u4e3b\u4e2a\u4eba\u9875\u9762\u5c55\u793a\u5f53\u524d\u7528\u6237\u4e0e\u535a\u4e3b\u7684\u5171\u540c\u597d\u53cb\u3002<\/p>\n\n\n\n<p>\u7531\u4e8e\u8981\u4f7f\u7528redis\u7684set\uff0c\u6240\u4ee5\u9700\u8981\u6539\u9020\u4e4b\u524d\u5173\u6ce8\u7684\u63a5\u53e3\u3002<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>            followMapper.myInsert(follow);\n            stringRedisTemplate.opsForSet().add(key,followUserId.toString());\n        ...\n            followMapper.deleteByfollowUserIdAndUserId(followUserId,userId);\n            stringRedisTemplate.opsForSet().remove(key,followUserId.toString());<\/code><\/pre>\n\n\n\n<p>\u5171\u540c\u5173\u6ce8\u63a5\u53e3\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public Result followCommons(Long followUserId) {<br>    Long userId = UserHolder.<em>getUser<\/em>().getId();<br>    String myKey = \"follows:\"+userId.toString();<br>    String targetKey = \"follows:\"+followUserId.toString();<br>    \/\/\u6c42\u4ea4\u96c6<br>    Set&lt;String&gt; stringSet = stringRedisTemplate.opsForSet().intersect(myKey, targetKey);<br><br>    if (stringSet==null||stringSet.isEmpty()){<br>        \/\/\u6ca1\u6709\u5171\u540c\u5173\u6ce8<br>        return Result.<em>ok<\/em>(Collections.<em>emptyList<\/em>());<br>    }<br><br>    \/\/\u89e3\u6790ID\u96c6\u5408<br>    List&lt;Long&gt; ids = stringSet.stream()<br>            .map(id -&gt; Long.<em>valueOf<\/em>(id))<br>            .collect(Collectors.<em>toList<\/em>());<br><br>    List&lt;UserDTO&gt; userDTOList = userService.listByIds(ids)<br>            .stream()<br>            .map(user -&gt; BeanUtil.<em>copyProperties<\/em>(user, UserDTO.class))<br>            .collect(Collectors.<em>toList<\/em>());<br><br>    return Result.<em>ok<\/em>(userDTOList);<br>}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">6.3 \u5173\u6ce8\u63a8\u9001<\/h2>\n\n\n\n<p>\u5173\u6ce8\u63a8\u9001\u4e5f\u53ebFeed\u6d41\uff0c\u76f4\u8bd1\u4e3a\u6295\u5582\u3002\u4e3a\u7528\u6237\u6301\u7eed\u7684\u63d0\u4f9b\u201c\u6c89\u6d78\u5f0f\u201d\u7684\u4f53\u9a8c\uff0c\u901a\u8fc7\u65e0\u9650\u4e0b\u62c9\u5237\u65b0\u83b7\u53d6\u65b0\u7684\u4fe1\u606f\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">6.3.1 Feed\u6d41\u57fa\u672c\u6a21\u5f0f<\/h3>\n\n\n\n<h4 class=\"wp-block-heading\">1. Pull\u6a21\u5f0f\uff08\u62c9\u53d6\u5f0f\uff09<\/h4>\n\n\n\n<p>\u7528\u6237\u8bbf\u95ee\u65f6\u4ece\u540e\u53f0\u5b9e\u65f6\u67e5\u8be2\u751f\u6210 Feed\u3002<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u4f18\u70b9<\/strong>\uff1a\n<ul class=\"wp-block-list\">\n<li>\u6570\u636e\u65b0\u9c9c\u3001\u5b9e\u65f6\u6027\u597d\u3002<\/li>\n\n\n\n<li>\u5199\u5165\u8f7b\uff0c\u4e0d\u9700\u63a8\u9001\u6216\u7ef4\u62a4\u7f13\u5b58\u3002<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>\u7f3a\u70b9<\/strong>\uff1a\n<ul class=\"wp-block-list\">\n<li>\u67e5\u8be2\u538b\u529b\u5927\uff0c\u5c24\u5176\u662f\u5927V\u6216\u7c89\u4e1d\u4f17\u591a\u7528\u6237\u3002<\/li>\n\n\n\n<li>\u7528\u6237\u4f53\u9a8c\u5dee\uff0c\u53ef\u80fd\u9047\u5230\u52a0\u8f7d\u6162\u3001\u91cd\u590d\u5185\u5bb9\u7b49\u3002<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">2. Push\u6a21\u5f0f\uff08\u63a8\u9001\u5f0f\uff09<\/h4>\n\n\n\n<p>\u5185\u5bb9\u751f\u4ea7\u8005\u53d1\u5e03\u5185\u5bb9\u65f6\uff0c\u7cfb\u7edf\u5c06\u5176\u63a8\u9001\u5230\u6240\u6709\u5173\u6ce8\u8005\u7684 Feed \u4e2d\u3002<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u4f18\u70b9<\/strong>\uff1a\n<ul class=\"wp-block-list\">\n<li>\u8bfb\u53d6\u5feb\uff0c\u7528\u6237\u8bbf\u95ee\u65f6\u53ef\u76f4\u63a5\u83b7\u53d6\u3002<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>\u7f3a\u70b9<\/strong>\uff1a\n<ul class=\"wp-block-list\">\n<li>\u5199\u5165\u538b\u529b\u5927\u3002<\/li>\n\n\n\n<li>\u5b58\u50a8\u5197\u4f59\uff0c\u5185\u5bb9\u9700\u8981\u590d\u5236\u5230\u591a\u4e2a\u7528\u6237\u3002<\/li>\n\n\n\n<li>\u4e0d\u9002\u5408\u5927V\uff08\u767e\u4e07\u7c89\u4e1d\uff09\u5185\u5bb9\u5206\u53d1\u3002<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">3. Hybrid\u6a21\u5f0f\uff08\u6df7\u5408\u5f0f\uff09<\/h4>\n\n\n\n<p>\u5e38\u89c1\u7684\u65b9\u6848\u662f\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u5bf9\u666e\u901a\u7528\u6237\u91c7\u7528 Push\u3002<\/li>\n\n\n\n<li>\u5bf9\u5927V\uff08\u5982\u7c89\u4e1d &gt; 10w\uff09\u91c7\u7528 Pull\u3002<\/li>\n\n\n\n<li>\u70ed\u95e8\u5185\u5bb9\uff08\u5168\u7f51\u63a8\u8350\uff09\u91c7\u7528\u5b9a\u65f6 Push \u6216\u5e7f\u64ad\u4e0b\u53d1\u3002<\/li>\n<\/ul>\n\n\n\n<p>\u6b64\u8282\u4f7f\u7528\u63a8\u6a21\u5f0f\u6765\u5b9e\u73b0\uff1a<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">6.3.2 \u57fa\u4e8e\u63a8\u6a21\u5f0f\u5b9e\u73b0\u5173\u6ce8\u63a8\u9001\u529f\u80fd\uff1a<\/h3>\n\n\n\n<p><strong>\u529f\u80fd\u9700\u6c42\uff1a<\/strong><\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u65b0\u589e\u63a2\u5e97\u7b14\u8bb0\u4e1a\u52a1\u6d41\u7a0b<\/strong>\uff1a\u5728\u4fdd\u5b58 blog \u6570\u636e\u5230\u6570\u636e\u5e93\u7684\u64cd\u4f5c\u540c\u6b65\u6267\u884c\u65f6\uff0c\u9700\u5c06\u8be5\u7b14\u8bb0\u63a8\u9001\u81f3\u7c89\u4e1d\u7684\u6536\u4ef6\u7bb1 \u3002<\/li>\n\n\n\n<li><strong>\u6536\u4ef6\u7bb1\u6570\u636e\u7ed3\u6784<\/strong>\uff1a\u57fa\u4e8e Redis \u5b9e\u73b0\uff0c\u4e14\u8981\u652f\u6301\u6309\u65f6\u95f4\u6233\u5bf9\u6536\u4ef6\u7bb1\u5185\u5bb9\u8fdb\u884c\u6392\u5e8f \u3002<\/li>\n\n\n\n<li><strong>\u6570\u636e\u67e5\u8be2\u80fd\u529b<\/strong>\uff1a\u652f\u6301\u5bf9\u6536\u4ef6\u7bb1\u6570\u636e\u6267\u884c\u5206\u9875\u67e5\u8be2\u64cd\u4f5c \u3002<\/li>\n<\/ol>\n\n\n\n<p>Timeline \u63a8\u8350\u7528 ZSet\u5b9e\u73b0feed\u7684\u6eda\u52a8\u5206\u9875\u3002<\/p>\n\n\n\n<p>\u5728\u4fdd\u5b58blog\u65f6\u63a8\u9001\u5230\u6536\u4ef6\u7bb1\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public Result saveBlog(Blog blog) {<br>    \/\/ \u83b7\u53d6\u767b\u5f55\u7528\u6237<br>    UserDTO user = UserHolder.<em>getUser<\/em>();<br>    blog.setUserId(user.getId());<br>    \/\/ \u4fdd\u5b58\u63a2\u5e97\u535a\u6587<br>    save(blog);<br>    \/\/\u83b7\u53d6\u6240\u6709\u7c89\u4e1d\u5217\u8868<br>    List&lt;Follow&gt; followers = followService.query().eq(\"follow_user_id\", user.getId()).list();<br>    \/\/\u53d1\u9001\u5230\u7c89\u4e1d\u7684\u6536\u4ef6\u7bb1<br>    followers.forEach(follow -&gt; {<br>        Long followId = follow.getUserId();<br>        String key = \"feed:\" + followId;<br>        stringRedisTemplate.opsForZSet().add(key, blog.getId().toString(), System.<em>currentTimeMillis<\/em>());<br>    });<br>    \/\/ \u8fd4\u56deid<br>    return Result.<em>ok<\/em>(blog.getId());<br>}<\/code><\/pre>\n\n\n\n<p>\u6eda\u52a8\u5206\u9875\u57fa\u4e8e\u4ee5\u65f6\u95f4\u6233\u4e3ascore\u7684zset\u5b9e\u73b0\u3002\u6709\u56db\u4e2a\u67e5\u8be2\u53c2\u6570\uff0c\u4e24\u79cd\u60c5\u51b5\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u7b2c\u4e00\u6b21\u67e5\u8be2\uff1a\n<ul class=\"wp-block-list\">\n<li>max:<strong>\u5f53\u524d\u65f6\u95f4\u6233<\/strong><\/li>\n\n\n\n<li>min:0<\/li>\n\n\n\n<li>offset:<strong>0<\/strong><\/li>\n\n\n\n<li>count:CONST LIMIT<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>\u4e4b\u540e\u7684\u67e5\u8be2\uff1a\n<ul class=\"wp-block-list\">\n<li>max:<strong>\u4e0a\u4e00\u6b21\u67e5\u8be2\u7684\u6700\u5c0f\u65f6\u95f4\u6233<\/strong><\/li>\n\n\n\n<li>min:0<\/li>\n\n\n\n<li>offset:<strong>\u5df2\u67e5\u67e5\u8be2\u7ed3\u679c\u65f6\u95f4\u6233\u4e0e\u6700\u5c0f\u65f6\u95f4\u6233\u76f8\u7b49\u7684\u6570\u91cf<\/strong>\uff08\u6ce8\u610f\u662f\u5df2\u7ecf\u88ab\u67e5\u7684\u6240\u6709\u7ed3\u679c\uff08\u4e0d\u53ea\u662f\u4e0a\u4e00\u6b21\uff09\u91cc\u7684\u76f8\u540c\u7684\uff09<\/li>\n\n\n\n<li>count:CONST LIMIT<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<p>\u6b64\u5904\u89c6\u9891\u5185\u8bb2\u7684\u6709bug\uff0c<a href=\"https:\/\/blog.csdn.net\/axinru\/article\/details\/140781692\">\u5173\u4e8e\u9ed1\u9a6c\u70b9\u8bc4P87\u7684\u6eda\u52a8\u67e5\u8be2\uff0c\u4e3a\u4ec0\u4e48\u8981\u52a0\uff1aos = minTime == max ? os + offset : os\uff1b_os = mintime == max ? os : os + offset;-CSDN\u535a\u5ba2<\/a>\u3002<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public Result queryBlogOfFollow(Long max, Integer offset) {<br>    \/\/1.\u83b7\u53d6\u5f53\u524d\u7528\u6237<br>    Long userId = UserHolder.<em>getUser<\/em>().getId();<br>    \/\/2.\u67e5\u8be2\u6536\u4ef6\u7bb1<br>    String key = <em>FEED_KEY <\/em>+ userId;<br>    Set&lt;ZSetOperations.TypedTuple&lt;String&gt;&gt; typedTuples =<br>            stringRedisTemplate.opsForZSet().reverseRangeByScoreWithScores(key, 0, max, offset, 2);<br><br>    if(typedTuples==null || typedTuples.isEmpty()){<br>        return Result.<em>ok<\/em>();<br>    }<br>    \/\/3.\u89e3\u6790\u6536\u4ef6\u7bb1<br>    long minTime=max;<br>    int os=1;<br>    List&lt;Long&gt; ids = new ArrayList&lt;&gt;(typedTuples.size());<br>    for (ZSetOperations.TypedTuple&lt;String&gt; typedTuple : typedTuples){<br>        \/\/\u83b7\u53d6blogId<br>        ids.add(Long.<em>valueOf<\/em>(typedTuple.getValue()));<br>        \/\/\u83b7\u53d6score<br>        long time = typedTuple.getScore().longValue();<br>        if (time==minTime){<br>            os++;<br>        }else {<br>            minTime=time;<br>            os = 1;<br>        }<br>    }<br><br>    os = minTime == max ? os + offset : os;<br>    \/\/4.\u6839\u636eid\u67e5\u8be2blog<br>    String idsStr = ids.stream()<br>            .map(Object::toString)<br>            .collect(Collectors.<em>joining<\/em>(\",\"));<br>    List&lt;Blog&gt; blogs = query().in(\"id\", ids).last(\"ORDER BY FIELD(id,\"+idsStr+\")\").list();<br><br>    \/\/5.\u586b\u5145\u975eblog\u6570\u636e\u5e93\u5b57\u6bb5<br>    for (Blog blog : blogs) {<br>        queryBlogUser(blog);<br>        isBlogLiked(blog);<br>    }<br>    \/\/6.\u8fd4\u56de<br>    ScrollResult r = new ScrollResult();<br>    r.setList(blogs);<br>    r.setOffset(os);<br>    r.setMinTime(minTime);<br>    return Result.<em>ok<\/em>(r);<br>}<\/code><\/pre>\n\n\n\n<h1 class=\"wp-block-heading\">7 \u7528\u6237\u7b7e\u5230 \uff1a BitMap<\/h1>\n\n\n\n<p>\u9644\u8fd1\u5546\u94fa\u6709\u5173\u7684Redis GEO\u6ca1\u4ec0\u4e48\u7528\uff0c\u76f4\u63a5\u8df3\u8fc7\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">7.1 BitMap\u7528\u6cd5\uff1a<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Bitmap \u7b80\u8981\u4ecb\u7ecd<\/h3>\n\n\n\n<p><strong>\u5b9a\u4e49<\/strong>\uff1a\u7528\u6bd4\u7279\u4f4d\uff08bit\uff09\u8868\u793a\u5143\u7d20\u72b6\u6001\uff080\/1\uff09\u7684\u6570\u636e\u7ed3\u6784\uff0c\u6bcf\u4e2a\u6bd4\u7279\u5bf9\u5e94\u4e00\u4e2a\u6574\u6570\u5143\u7d20\u3002<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">\u6838\u5fc3\u7279\u70b9<\/h4>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u7a7a\u95f4\u6548\u7387\u9ad8<\/strong><br>\u5b58\u50a8&nbsp;<code>n<\/code>&nbsp;\u4e2a\u5143\u7d20\u4ec5\u9700&nbsp;<code>n\/8<\/code>&nbsp;\u5b57\u8282\uff08\u5982\u5b58\u50a8 1 \u4ebf\u4e2a ID \u4ec5\u9700 12MB\uff09\u3002<\/li>\n\n\n\n<li><strong>\u64cd\u4f5c\u901f\u5ea6\u5feb<\/strong><br>\u4f4d\u8fd0\u7b97\u7531 CPU \u76f4\u63a5\u652f\u6301\uff0c\u65f6\u95f4\u590d\u6742\u5ea6\u901a\u5e38\u4e3a&nbsp;<code>O(1)<\/code>&nbsp;\u6216&nbsp;<code>O(n\/64)<\/code>\u3002<\/li>\n\n\n\n<li><strong>\u652f\u6301\u96c6\u5408\u8fd0\u7b97<\/strong><br>\u901a\u8fc7\u4f4d\u8fd0\u7b97\u5b9e\u73b0\u9ad8\u6548\u4ea4\u96c6\uff08<code>&amp;<\/code>\uff09\u3001\u5e76\u96c6\uff08<code>|<\/code>\uff09\u3001\u5dee\u96c6\uff08<code>&amp; ~<\/code>\uff09\u7b49\u64cd\u4f5c\u3002<\/li>\n<\/ol>\n\n\n\n<h4 class=\"wp-block-heading\">\u5178\u578b\u5e94\u7528\u573a\u666f<\/h4>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u53bb\u91cd\u4e0e\u5224\u91cd<\/strong>\n<ul class=\"wp-block-list\">\n<li>\u7edf\u8ba1\u72ec\u7acb\u7528\u6237\u6570\uff08\u5982 UV\uff09<\/li>\n\n\n\n<li>\u68c0\u6d4b\u91cd\u590d\u6570\u636e\uff08\u5982\u65e5\u5fd7 ID \u53bb\u91cd\uff09<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>\u8303\u56f4\u67e5\u8be2\u4e0e\u8fc7\u6ee4<\/strong>\n<ul class=\"wp-block-list\">\n<li>\u7b5b\u9009\u5e74\u9f84\u5728 18-30 \u5c81\u7684\u7528\u6237<\/li>\n\n\n\n<li>\u6743\u9650\u5feb\u901f\u5339\u914d\uff08\u5982\u5224\u65ad\u7528\u6237\u662f\u5426\u6709\u8bfb\u5199\u6743\u9650\uff09<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>\u5927\u6570\u636e\u6392\u5e8f<\/strong>\n<ul class=\"wp-block-list\">\n<li>\u5bf9 0-100 \u4e07\u7684\u6574\u6570\u6392\u5e8f\uff08\u65f6\u95f4\u590d\u6742\u5ea6&nbsp;<code>O(n)<\/code>\uff09<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>\u5e03\u9686\u8fc7\u6ee4\u5668\u5e95\u5c42\u5b9e\u73b0<\/strong>\n<ul class=\"wp-block-list\">\n<li>\u5feb\u901f\u5224\u65ad\u5143\u7d20\u662f\u5426 \u201c\u53ef\u80fd\u5b58\u5728\u201d\uff08\u6709\u4e00\u5b9a\u8bef\u5224\u7387\uff09<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<h4 class=\"wp-block-heading\">\u5c40\u9650\u6027<\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u4ec5\u652f\u6301\u6574\u6570\u6620\u5c04\uff0c\u65e0\u6cd5\u76f4\u63a5\u5904\u7406\u5b57\u7b26\u4e32\u7b49\u7c7b\u578b<\/li>\n\n\n\n<li>\u82e5\u5143\u7d20\u8303\u56f4\u5927\u4f46\u5b9e\u9645\u6570\u91cf\u5c11\uff08\u5982\u6700\u5927\u503c 1 \u4ebf\u4f46\u4ec5\u5b58 10 \u4e2a\u5143\u7d20\uff09\uff0c\u4f1a\u6d6a\u8d39\u7a7a\u95f4<\/li>\n\n\n\n<li>\u65e0\u6cd5\u8bb0\u5f55\u5143\u7d20\u91cd\u590d\u6b21\u6570\uff08\u9700\u53d8\u79cd\u5982 Counting Bitmap\uff09<\/li>\n<\/ul>\n\n\n\n<p>\u5728Redis\u4e2d\u5e95\u5c42\u662fString\u3002<br>Redis \u5b57\u7b26\u4e32\u7684\u6700\u5927\u5b57\u8282\u957f\u5ea6\u4e3a 512MB\uff0c\u56e0\u6b64 Bitmap \u7684\u6700\u5927 bit \u6570\u4e3a&nbsp;<strong>512MB \u00d7 8 = 4,294,967,296 bit\uff08\u7ea6 42 \u4ebf bit\uff09<\/strong><\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th><strong>Redis \u547d\u4ee4<\/strong><\/th><th><strong>RedisTemplate \u65b9\u6cd5<\/strong><\/th><th><strong>\u63cf\u8ff0<\/strong><\/th><th><strong>\u793a\u4f8b<\/strong><\/th><\/tr><\/thead><tbody><tr><td><code>SETBIT<\/code><\/td><td><code>opsForValue().setBit(key, offset, value)<\/code><\/td><td>\u8bbe\u7f6e\u6307\u5b9a\u4f4d\u7f6e\u7684\u4f4d\u503c\uff08true\/false\uff09<\/td><td><code>redisTemplate.opsForValue().setBit(\"user:sign\", 0, true);<\/code><\/td><\/tr><tr><td><code>GETBIT<\/code><\/td><td><code>opsForValue().getBit(key, offset)<\/code><\/td><td>\u83b7\u53d6\u6307\u5b9a\u4f4d\u7f6e\u7684\u4f4d\u503c<\/td><td><code>Boolean isSigned = redisTemplate.opsForValue().getBit(\"user:sign\", 0);<\/code><\/td><\/tr><tr><td><code>BITCOUNT<\/code><\/td><td><code>redisTemplate.execute( (RedisCallback&lt;Long&gt;) conn -&gt; conn.bitCount(key.getBytes()) )<\/code><\/td><td>\u7edf\u8ba1\u4f4d\u56fe\u4e2d\u503c\u4e3a 1 \u7684\u4f4d\u6570\u91cf<\/td><td><code>Long count = redisTemplate.execute( (RedisCallback&lt;Long&gt;) conn -&gt; conn.bitCount(\"user:sign\".getBytes()) );<\/code><\/td><\/tr><tr><td><code>BITOP<\/code><\/td><td><code>redisTemplate.execute( (RedisCallback&lt;Long&gt;) conn -&gt; conn.bitOp(operation, destKey.getBytes(), srcKeys.getBytes()) )<\/code><\/td><td>\u5bf9\u591a\u4e2a\u4f4d\u56fe\u6267\u884c\u4f4d\u8fd0\u7b97\uff08AND\/OR\/XOR\/NOT\uff09<\/td><td><code>redisTemplate.execute( (RedisCallback&lt;Long&gt;) conn -&gt; conn.bitOp(RedisStringCommands.BitOperation.AND, \"result\".getBytes(), \"key1\".getBytes(), \"key2\".getBytes()) );<\/code><\/td><\/tr><tr><td><code>BITPOS<\/code><\/td><td><code>redisTemplate.execute( (RedisCallback&lt;Long&gt;) conn -&gt; conn.bitPos(key.getBytes(), bit, start, end) )<\/code><\/td><td>\u67e5\u627e\u7b2c\u4e00\u4e2a\u503c\u4e3a\u6307\u5b9a\u4f4d\u7684\u4f4d\u7f6e<\/td><td><code>Long firstOne = redisTemplate.execute( (RedisCallback&lt;Long&gt;) conn -&gt; conn.bitPos(\"user:sign\".getBytes(), true, 0, -1) );<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">7.2 \u5b9e\u73b0\u7b7e\u5230\u529f\u80fd\uff1a<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>public Result sign() {<br>    \/\/1.\u83b7\u53d6\u7528\u6237<br>    Long id = UserHolder.<em>getUser<\/em>().getId();<br>    \/\/2.\u83b7\u53d6\u65e5\u671f<br>    LocalDateTime now = LocalDateTime.<em>now<\/em>();<br>    int dayOfMonth = now.getDayOfMonth();<br>    \/\/3.setBitMap<br>    String key = <em>USER_SIGN_KEY<\/em>+id+\":\"+now.format(DateTimeFormatter.<em>ofPattern<\/em>(\"yyyyMM\"));<br>    redisTemplate.opsForValue().setBit(key,dayOfMonth-1,true);<br>    \/\/4.\u8fd4\u56de<br>    return Result.<em>ok<\/em>();<br>}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">7.3 \u7edf\u8ba1\u8fde\u7eed\u7b7e\u5230\uff1a<\/h2>\n\n\n\n<p>\u8fde\u7eed\u7b7e\u5230\u6b21\u6570\uff1a\u4ece\u6700\u540e\u4e00\u6b21\u7b7e\u5230\u5f00\u59cb\u5411\u524d\u7edf\u8ba1\u5230\u7b2c\u4e00\u6b21\u672a\u7b7e\u5230\u4e3a\u6b62\u7684\u8fde\u7eed\u6b21\u6570\u3002<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public Result countContinuedSignedDay() {<br>    \/\/1.\u83b7\u53d6\u7528\u6237<br>    Long id = UserHolder.<em>getUser<\/em>().getId();<br>    \/\/2.\u83b7\u53d6\u65e5\u671f<br>    LocalDateTime now = LocalDateTime.<em>now<\/em>();<br>    int dayOfMonth = now.getDayOfMonth();<br>    \/\/3.\u83b7\u53d6key<br>    String key = <em>USER_SIGN_KEY<\/em>+id+\":\"+now.format(DateTimeFormatter.<em>ofPattern<\/em>(\"yyyyMM\"));<br>    \/\/4.\u83b7\u53d6\u672c\u6708\u622a\u6b62\u4eca\u5929\u4e3a\u6b62\u7684\u6240\u6709\u7684\u7b7e\u5230\u8bb0\u5f55\uff0c\uff0c\u8fd4\u56de\u7684\u662f\u4e00\u4e2a\u5341\u8fdb\u5236\u7684\u6570\u5b57<br>    List&lt;Long&gt; bit = redisTemplate.opsForValue().bitField(<br>            key,<br>            BitFieldSubCommands.<em>create<\/em>()<br>                    .get(BitFieldSubCommands.BitFieldType.<em>unsigned<\/em>(dayOfMonth))<br>                    .valueAt(0)<br>    );<br>    \/\/\u6ca1\u7b7e\u5230<br>    if (bit==null||bit.size()==0)<br>        return Result.<em>ok<\/em>(0);<br><br>    Long num = bit.get(0);<br>    \/\/\u6ca1\u7b7e\u5230<br>    if (num==0)<br>        return Result.<em>ok<\/em>(0);<br><br>    \/\/5.\u53f3\u79fb\u904d\u5386\u5f97\u5230\u8fde\u7eed\u7b7e\u5230\u5929\u6570<br>    \/\/5.1\u4eca\u5929\u6ca1\u7b7e\u5230\u76f4\u63a5\u8fd4\u56de0<br>    if ((num &amp; 1)==0)<br>        return Result.<em>ok<\/em>(0);<br>    \/\/5.2\u904d\u5386\u5f97\u5230\u8fde\u7eed\u5929\u6570<br>    int count=0;<br>    while ((num&amp;1)==1){<br>        count++;<br>        num=num&gt;&gt;1;<br>    }<br><br>    return Result.<em>ok<\/em>(count);<br>}<\/code><\/pre>\n\n\n\n<h1 class=\"wp-block-heading\">8.UV\u7edf\u8ba1<\/h1>\n\n\n\n<ul class=\"wp-block-list\">\n<li>UV\uff1a\u5168\u79f0Unique Visitor\uff0c\u4e5f\u53eb\u72ec\u7acb\u8bbf\u5ba2\u91cf\uff0c\u662f\u6307\u901a\u8fc7\u4e92\u8054\u7f51\u8bbf\u95ee\u3001\u6d4f\u89c8\u8fd9\u4e2a\u7f51\u9875\u7684\u81ea\u7136\u4eba\u30021\u5929\u5185\u540c\u4e00\u4e2a\u7528\u6237\u591a\u6b21\u8bbf\u95ee\u8be5\u7f51\u7ad9\uff0c\u53ea\u8bb0\u5f551\u6b21\u3002<\/li>\n<\/ul>\n\n\n\n<ul class=\"wp-block-list\">\n<li>PV\uff1a\u5168\u79f0Page View\uff0c\u4e5f\u53eb\u9875\u9762\u8bbf\u95ee\u91cf\u6216\u70b9\u51fb\u91cf\uff0c\u7528\u6237\u6bcf\u8bbf\u95ee\u7f51\u7ad9\u7684\u4e00\u4e2a\u9875\u9762\uff0c\u8bb0\u5f551\u6b21PV\uff0c\u7528\u6237\u591a\u6b21\u6253\u5f00\u9875\u9762\uff0c\u5219\u8bb0\u5f55\u591a\u6b21PV\u3002\u5f80\u5f80\u7528\u6765\u8861\u91cf\u7f51\u7ad9\u7684\u6d41\u91cf\u3002<br><\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">8.1HyperLogLog\u7528\u6cd5<\/h2>\n\n\n\n<p>Hyperloglog(HLL)\u662f\u4eceLoglog\u7b97\u6cd5\u6d3e\u751f\u7684\u6982\u7387\u7b97\u6cd5\uff0c\u7528\u4e8e\u786e\u5b9a\u975e\u5e38\u5927\u7684\u96c6\u5408\u7684\u57fa\u6570\uff0c\u800c\u4e0d\u9700\u8981\u5b58\u50a8\u5176\u6240\u6709\u503c\u3002<\/p>\n\n\n\n<p>Redis\u4e2d\u7684HLL\u662f\u57fa\u4e8estring\u7ed3\u6784\u5b9e\u73b0\u7684\uff0c\u5355\u4e2aHLL\u7684\u5185\u5b58\u6c38\u8fdc\u5c0f\u4e8e16kb\u3002\u4f46\u5176\u6d4b\u91cf\u7ed3\u679c\u662f\u6982\u7387\u6027\u7684\uff0c\u6709\u5c0f\u4e8e<strong>0.81\uff05<\/strong>\u7684\u8bef\u5dee\u3002\u4e0d\u8fc7\u5bf9\u4e8eUV\u7edf\u8ba1\u6765\u8bf4\uff0c\u8fd9\u5b8c\u5168\u53ef\u4ee5\u5ffd\u7565\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">8.2 \u5b9e\u73b0UV\u7edf\u8ba1<\/h2>\n\n\n\n<h4 class=\"wp-block-heading\">\u5b9e\u73b0\u65b9\u6848\uff1a<\/h4>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u4f7f\u7528<strong>\u540e\u7aef\u62e6\u622a\u5668<\/strong>\u7edf\u8ba1\uff08\u5982Spring\u7684 <code>HandlerInterceptor<\/code>\uff09\u3002<\/li>\n\n\n\n<li>\u63d0\u53d6 <code>userId<\/code> \/ IP \/ Token \/ UA\uff0c <code>HyperLogLog<\/code> \u5b9e\u73b0\u53bb\u91cd\u7edf\u8ba1\u3002<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code><code>\npublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {\n    String uid = getUserIdOrIp(request);\n    stringRedisTemplate.opsForHyperLogLog().add(\"uv:\" + LocalDate.now(), uid);\n    return true;\n}\n<\/code><\/code><\/pre>\n\n\n\n<h1 class=\"wp-block-heading\">9 \u4f7f\u7528Rocket MQ\u4ee3\u66ff\u539f\u751f\u963b\u585e\u961f\u5217\uff1a<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">9.0 \u51c6\u5907\u5de5\u4f5c\uff1a<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">maven\u914d\u7f6e\uff1a<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;!--RocketMQ-->\n        &lt;dependency>\n            &lt;groupId>org.apache.rocketmq&lt;\/groupId>\n            &lt;artifactId>rocketmq-spring-boot-starter&lt;\/artifactId>\n            &lt;version>2.2.3&lt;\/version>\n        &lt;\/dependency><\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">yaml\uff1a\uff08\u6ce8\u610f\u4e0d\u5728spring\u4e0b\uff0c\uff08GPT\u6d6a\u8d39\u4e862\u5c0f\u65f6\u65f6\u95f4\uff0cwatch out\uff01\uff09\uff09<\/h3>\n\n\n\n<pre class=\"wp-block-preformatted\">rocketmq:<br>  name-server: 127.0.0.1:9876<br>  consumer:<br>      group: VOUCHER_ORDER_GROUP<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\u672c\u5730\u542f\u52a8\u670d\u52a1\uff1a<\/h3>\n\n\n\n<p>\u6b64\u5904\u4f7f\u7528docker\uff08\u6709\u4e2a\u5927\u5751\uff0c\u89c1\u4e0bconf\u6587\u4ef6\u4e2d\u89e3\u91ca\uff09\uff1a<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">docker-compose.yml\uff1a<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>version: '3.8'\nservices:\n  namesrv:\n    image: apache\/rocketmq:4.9.6\n    container_name: rocketmq-namesrv\n    ports:\n      - \"9876:9876\"\n    command: sh mqnamesrv\n\n  broker:\n    image: apache\/rocketmq:4.9.6\n    container_name: rocketmq-broker\n    depends_on:\n      - namesrv\n    ports:\n      - \"10909:10909\"\n      - \"10911:10911\"\n    volumes:\n      - .\/conf\/broker.conf:\/home\/rocketmq\/rocketmq-4.9.6\/conf\/broker.conf\n    command: sh mqbroker -n namesrv:9876 -c \/home\/rocketmq\/rocketmq-4.9.6\/conf\/broker.conf\n\n  dashboard:\n    image: styletang\/rocketmq-dashboard\n    container_name: rocketmq-dashboard\n    ports:\n      - \"8079:8080\"  # \u5bbf\u4e3b\u673a 8079 \u2192 \u5bb9\u5668 8080\n    environment:\n      - ROCKETMQ_NAMESRV_ADDR=namesrv:9876\n<\/code><\/pre>\n\n\n\n<h4 class=\"wp-block-heading\">conf\/broker.conf\uff1a<\/h4>\n\n\n\n<pre class=\"wp-block-code\"><code>brokerClusterName=DefaultCluster\nbrokerName=broker-a\nbrokerId=0\ndeleteWhen=04\nfileReservedTime=48\nbrokerRole=ASYNC_MASTER\nflushDiskType=ASYNC_FLUSH\n\n# \u26a0\ufe0f \u5173\u952e\uff1a\u6307\u5b9a Broker \u7684 IP \u5730\u5740\uff0c\u5fc5\u987b\u4f7f\u7528\u5bbf\u4e3b\u673a\u53ef\u8bbf\u95ee\u7684 IP\n# \u5982\u679c\u4e0d\u8bbe\u7f6e\uff0cSpring Boot Consumer \u5c31\u4f1a\u8fde\u63a5docker\u5185\u90e8\u7684ip\u5730\u5740\uff08\u6ce8\u518c\u5230nameserver\u7684\uff09\uff0c\u65e0\u6cd5\u8fde\u63a5\u5230 Broker\u3002\nbrokerIP1=127.0.0.1\n<\/code><\/pre>\n\n\n\n<p>docker-compose up -d \u542f\u52a8<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">9.1 \u4ee3\u7801\u5b9e\u73b0\uff1a<\/h2>\n\n\n\n<p>\u53ef\u4ee5\u5148\u5220\u53bb\u4e0d\u7528\u7684\u963b\u585e\u961f\u5217\u76f8\u5173\u4ee5\u53ca\u7528\u4e8e\u6d88\u8d39\u4efb\u52a1\u7684\u7ebf\u7a0b\u6c60\u53ca\u4efb\u52a1\u7c7b\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">9.1.0\uff1a\u5411redis\u521d\u59cb\u5316\u5e93\u5b58\uff1a<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>@Component\npublic class StockInitializer implements ApplicationRunner {\n\n    \/\/ \u6ce8\u5165\u79d2\u6740\u5238\u670d\u52a1\uff0c\u7528\u6765\u67e5\u8be2\u6570\u636e\u5e93\u4e2d\u7684\u79d2\u6740\u5238\u4fe1\u606f\n    @Autowired\n    private ISeckillVoucherService seckillVoucherService;\n\n    \/\/ \u6ce8\u5165 Redis \u6a21\u677f\uff0c\u7528\u4e8e\u64cd\u4f5c Redis\n    @Autowired\n    private StringRedisTemplate stringRedisTemplate;\n\n    \/**\n     * run \u65b9\u6cd5\u4f1a\u5728 Spring Boot \u542f\u52a8\u5b8c\u6210\u540e\u81ea\u52a8\u6267\u884c\n     * @param args \u542f\u52a8\u53c2\u6570\uff0c\u4e00\u822c\u4e0d\u9700\u8981\u4f7f\u7528\n     *\/\n    @Override\n    public void run(ApplicationArguments args) throws Exception {\n        \/\/ \u67e5\u8be2\u6570\u636e\u5e93\u4e2d\u6240\u6709\u7684\u79d2\u6740\u5238\n        List&lt;SeckillVoucher> vouchers = seckillVoucherService.list();\n\n        \/\/ \u904d\u5386\u6bcf\u4e00\u4e2a\u79d2\u6740\u5238\uff0c\u521d\u59cb\u5316\u5e93\u5b58\u5230 Redis\n        for (SeckillVoucher voucher : vouchers) {\n            \/\/ \u6784\u5efa Redis key\uff0c\u6bd4\u5982 \"seckill:stock:1\"\n            String stockKey = \"seckill:stock:\" + voucher.getVoucherId();\n\n            \/\/ \u5982\u679c Redis \u4e2d\u4e0d\u5b58\u5728\u8fd9\u4e2a key\uff0c\u5219\u8bbe\u7f6e\u5e93\u5b58\u503c\n            \/\/ setIfAbsent \u76f8\u5f53\u4e8e \"SETNX\"\uff0c\u4fdd\u8bc1\u4e0d\u4f1a\u8986\u76d6\u5df2\u7ecf\u5b58\u5728\u7684\u5e93\u5b58\n            stringRedisTemplate.opsForValue().setIfAbsent(stockKey, voucher.getStock().toString());\n        }\n    }\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">9.1.1 \u751f\u4ea7\u8005\uff1a<\/h3>\n\n\n\n<p>\u4f7f\u7528<code>RocketMQTemplate<\/code>\u8fd9\u4e2a\u6838\u5fc3\u5de5\u5177\u7c7b\u4f5c\u4e3a\u751f\u4ea7\u8005\u5bf9\u8c61\uff1a<\/p>\n\n\n\n<p>VoucherOrderServiceImpl\u4e2d\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@Autowired\n    private RocketMQTemplate rocketMQTemplate;\n\n.........\n\n@Override\n    public Result seckillVoucher(Long voucherId) {\n        \/\/1.lua\u811a\u672c\u5224\u65ad\u79d2\u6740\u8d44\u683c\uff08\u539f\u5b50\u6027\u672c\u8eab\u4e5f\u4fdd\u8bc1\u7ebf\u7a0b\u5b89\u5168\uff09\n        Long flag = stringRedisTemplate.execute(\n                SECKILL_SCRIPTS,\n                Collections.emptyList(),\n                voucherId.toString(),\n                UserHolder.getUser().getId().toString()\n        );\n        \/\/2.\u5904\u7406\u5224\u65ad\u7ed3\u679c\n        if (flag == 1){\n            return Result.fail(\"\u5e93\u5b58\u4e0d\u8db3\");\n        }else if (flag == 2){\n            return Result.fail(\"\u4e0d\u80fd\u91cd\u590d\u4e0b\u5355\");\n        }\n        \/\/3.\u82e5\u6709\u8d44\u683c\uff0c\u628a\u4e0b\u5355\u4fe1\u606f\u4fdd\u5b58\u5230\u963b\u585e\u961f\u5217\n        idWorker=new RedisIdWorker(stringRedisTemplate);\n        Long orderId = idWorker.nextId(\"order\");\n        VoucherOrder order = new VoucherOrder();\n        \/\/\u8ba2\u5355id\n        order.setId(orderId);\n        \/\/\u7528\u6237id\n        order.setUserId(UserHolder.getUser().getId());\n        \/\/\u79d2\u6740\u5238id\n        order.setVoucherId(voucherId);\n\n        \/\/4. \u53d1\u9001\u8ba2\u5355\u6d88\u606f\u5230 RocketMQ\n        rocketMQTemplate.convertAndSend(\"seckill-order-topic\", order);\n\n        \/\/5.\u8fd4\u56de\u8ba2\u5355id\n        return Result.ok(orderId);\n    }<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">9.1.2 \u6d88\u8d39\u8005\uff1a<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>@Slf4j\n@Component\n@RocketMQMessageListener(\n        topic = \"seckill-order-topic\",   \/\/ \u8ba2\u9605\u7684 Topic\uff1a\u751f\u4ea7\u8005\u53d1\u5230\u8fd9\u91cc\u7684\u6d88\u606f\u4f1a\u88ab\u6536\u5230\n        consumerGroup = \"VOUCHER_ORDER_GROUP\" \/\/ \u6d88\u8d39\u7ec4\uff1a\u4fdd\u8bc1\u6d88\u606f\u53ea\u88ab\u4e00\u53f0\u6d88\u8d39\u8005\u5904\u7406\n)\npublic class VoucherOrderConsumer implements RocketMQListener&lt;VoucherOrder> {\n    @Autowired\n    private VoucherOrderServiceImpl voucherOrderService;\n\n    \/\/ \u5f53 MQ \u6709\u6d88\u606f\u5230\u8fbe\u65f6\uff0c\u81ea\u52a8\u56de\u8c03\u8fd9\u4e2a\u65b9\u6cd5\n    @Override\n    public void onMessage(VoucherOrder order) {\n        log.info(\"\u63a5\u6536\u5230\u8ba2\u5355\u6d88\u606f, orderId={}\", order.getId());\n        voucherOrderService.asyncCreateOrder(order);\n    }\n}<\/code><\/pre>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>0 \u51c6\u5907\u5de5\u4f5c\u53ca\u524d\u7f6e\u77e5\u8bc6 \u5bfc\u51fa\u521d\u59cb\u5316\u6587\u4ef6\u5e76\u4e0a\u4f20\u5230gitee CMD137\/hm-dianping 1 \u77ed\u4fe1\u767b\u5f55  [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[37,66],"tags":[],"class_list":["post-807","post","type-post","status-publish","format-standard","hentry","category-javaweblist","category-66"],"_links":{"self":[{"href":"http:\/\/www.cmd137blog.top\/index.php?rest_route=\/wp\/v2\/posts\/807","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.cmd137blog.top\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.cmd137blog.top\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.cmd137blog.top\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.cmd137blog.top\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=807"}],"version-history":[{"count":85,"href":"http:\/\/www.cmd137blog.top\/index.php?rest_route=\/wp\/v2\/posts\/807\/revisions"}],"predecessor-version":[{"id":1352,"href":"http:\/\/www.cmd137blog.top\/index.php?rest_route=\/wp\/v2\/posts\/807\/revisions\/1352"}],"wp:attachment":[{"href":"http:\/\/www.cmd137blog.top\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=807"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.cmd137blog.top\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=807"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.cmd137blog.top\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=807"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}