<?xml version="1.0" encoding="utf-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0"><channel><title>ZENDER</title><link>https://blog.zender.top/</link><description>愿美好如初</description><item><title>MyBatisCodeHelper-Pro3.3.6+2321破解</title><link>https://blog.zender.top/post/MyBatisCodeHelperPro3.3.6.html</link><description>&lt;p style=&quot;text-align: left;&quot;&gt;Idea2024版本的MyBatisCodeHelper-Pro3.3.6+2321最新破解版本，直接激活到2099年，如下图：&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;img class=&quot;ue-image&quot; src=&quot;https://blog.zender.top/zb_users/upload/2024/10/202410171729131252825037.png&quot; title=&quot;image-20241016102030777.png&quot; alt=&quot;image-20241016102030777.png&quot;/&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;color: #888888; font-family: monospace; text-wrap: wrap; font-size: 18px;&quot;&gt;下载地址：&lt;/span&gt;&lt;/p&gt;&lt;p&gt;[ReplyVisible]&lt;span style=&quot;text-wrap: wrap;&quot;&gt;链接：&lt;/span&gt;&lt;a href=&quot;https://pan.baidu.com/s/1f_BHHnMZ9I6ohnNKh92iOQ?pwd=ytab&quot; target=&quot;_blank&quot; style=&quot;text-wrap: wrap;&quot;&gt;MyBatisCodeHelper-Pro3.3.6+2321&lt;/a&gt;[/ReplyVisible]&lt;/p&gt;</description><pubDate>Thu, 17 Oct 2024 10:02:22 +0800</pubDate></item><item><title>网站迁移公告</title><link>https://blog.zender.top/post/qianyi.html</link><description>&lt;pre style=&quot;-webkit-tap-highlight-color: transparent; box-sizing: border-box; margin-top: 0px; margin-bottom: 0px; outline: none; padding: 0px; background-color: rgb(255, 255, 255); position: absolute; left: -99999px; user-select: auto !important;&quot;&gt;由于旧网站使用z-blog搭建，迁移文章评论数据非常麻烦，以前的文章格式也无法转换为md文档，无法兼容新版Blog。旧网站也保留！

请访问：旧BLOG&lt;/pre&gt;&lt;p style=&quot;text-align: center;&quot;&gt;&lt;img class=&quot;ue-image&quot; src=&quot;https://blog.zender.top/zb_users/upload/%E8%BF%81%E7%A7%BB%E5%85%AC%E5%91%8A.png&quot;/&gt;&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;由于本网站使用z-blog搭建，迁移文章评论数据非常麻烦，文章格式也无法转换为md文档，无法兼容新版Blog。旧网站也保留！&lt;/p&gt;&lt;p style=&quot;text-align: center;&quot;&gt;新BLOG请访问：&lt;a href=&quot;https://www.zender.top&quot; target=&quot;_blank&quot;&gt;新BLOG&lt;/a&gt;&lt;/p&gt;</description><pubDate>Thu, 10 Oct 2024 10:56:53 +0800</pubDate></item><item><title>Java项目防止SQL注入4总方式</title><link>https://blog.zender.top/post/no_sql_inject.html</link><description>&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;一、什么是SQL注入？&lt;/h1&gt;&lt;p&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严，攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句，在管理员不知情的情况下实现非法操作，以此来实现欺骗数据库服务器执行非授权的任意查询，从而进一步得到相应的数据信息。&lt;/p&gt;&lt;pre class=&quot;brush:sql;toolbar:false&quot;&gt;String&amp;nbsp;sql&amp;nbsp;=&amp;nbsp;&amp;quot;delete&amp;nbsp;from&amp;nbsp;table1&amp;nbsp;where&amp;nbsp;id&amp;nbsp;=&amp;nbsp;&amp;quot;&amp;nbsp;+&amp;nbsp;&amp;quot;id&amp;quot;;&lt;/pre&gt;&lt;p&gt;&lt;span style=&quot;font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; font-size: 16px; background-color: #FFFFFF;&quot;&gt;这个id从请求参数中获取，若参数被拼接为：1001 or&amp;nbsp; 1 = 1，最执行语句变为：&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;brush:sql;toolbar:false&quot;&gt;String&amp;nbsp;sql&amp;nbsp;=&amp;nbsp;&amp;quot;delete&amp;nbsp;from&amp;nbsp;table1&amp;nbsp;where&amp;nbsp;id&amp;nbsp;=&amp;nbsp;1001&amp;nbsp;or&amp;nbsp;1&amp;nbsp;=&amp;nbsp;1&amp;quot;;&lt;/pre&gt;&lt;p&gt;此时，数据库的数据都会被清空掉，后果非常严重。&lt;/p&gt;&lt;h2 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.5rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;二、Java项目防止SQL注入方式&lt;/h2&gt;&lt;p&gt;这里总结4种：&lt;/p&gt;&lt;h2 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.5rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;2.1、PreparedStatement防止SQL注入&lt;/h2&gt;&lt;p&gt;PreparedStatement具有预编译功能，以上述SQL为例，使用PreparedStatement预编译后的SQL为：&lt;/p&gt;&lt;pre class=&quot;brush:sql;toolbar:false&quot;&gt;delete&amp;nbsp;from&amp;nbsp;table1&amp;nbsp;where&amp;nbsp;id=&amp;nbsp;?&lt;/pre&gt;&lt;p&gt;此时SQL语句结构已固定，无论&amp;quot;?&amp;quot;被替换为任何参数，SQL语句只认为where后面只有一个条件，当再传入 1001 or&amp;nbsp; 1 = 1时，语句会报错，从而达到防止SQL注入效果。&lt;/p&gt;&lt;h2 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.5rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;2.2、mybatis中#{}防止SQL注入&lt;/h2&gt;&lt;p&gt;mybatis中#{}表达式防止SQL注入与PreparedStatement类似，都是对SQL语句进行预编译处理&lt;/p&gt;&lt;p&gt;注意：&lt;/p&gt;&lt;blockquote style=&quot;margin-top: 8px; margin-bottom: 8px; padding-right: 12px; padding-left: 12px; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; font-size: 16px; white-space: normal;&quot;&gt;&lt;p&gt;#{} ：参数占位符&lt;/p&gt;&lt;p&gt;${} ：拼接替换符，不能防止SQL注入，一般用于&amp;nbsp;传入数据库对象（如：数据库名称、表名）&amp;nbsp;order by&amp;nbsp; 后的条件&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.5rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;2.3、对请求参数的敏感词汇进行过滤&lt;/h2&gt;&lt;p&gt;这里是springboot的写法，如下：&lt;/p&gt;&lt;pre class=&quot;brush:java;toolbar:false&quot;&gt;import&amp;nbsp;org.springframework.context.annotation.Configuration;
import&amp;nbsp;javax.servlet.*;
import&amp;nbsp;javax.servlet.annotation.WebFilter;
import&amp;nbsp;java.io.IOException;
import&amp;nbsp;java.util.Enumeration;
&amp;nbsp;
/**
&amp;nbsp;*&amp;nbsp;@Description:&amp;nbsp;sql防注入过滤器
&amp;nbsp;*/
@WebFilter(urlPatterns&amp;nbsp;=&amp;nbsp;&amp;quot;/*&amp;quot;,filterName&amp;nbsp;=&amp;nbsp;&amp;quot;sqlFilter&amp;quot;)
@Configuration
public&amp;nbsp;class&amp;nbsp;SqlFilter&amp;nbsp;implements&amp;nbsp;Filter&amp;nbsp;{
&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@Override
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;init(FilterConfig&amp;nbsp;filterConfig)&amp;nbsp;throws&amp;nbsp;ServletException&amp;nbsp;{}
&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/**
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;@description&amp;nbsp;sql注入过滤
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*/
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@Override
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;doFilter(ServletRequest&amp;nbsp;servletRequest,&amp;nbsp;ServletResponse&amp;nbsp;servletResponse,&amp;nbsp;FilterChain&amp;nbsp;filterChain)&amp;nbsp;throws&amp;nbsp;IOException,&amp;nbsp;ServletException&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ServletRequest&amp;nbsp;request&amp;nbsp;=&amp;nbsp;servletRequest;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ServletResponse&amp;nbsp;response&amp;nbsp;=&amp;nbsp;servletResponse;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;获得所有请求参数名
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Enumeration&amp;lt;String&amp;gt;&amp;nbsp;names&amp;nbsp;=&amp;nbsp;request.getParameterNames();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String&amp;nbsp;sql&amp;nbsp;=&amp;nbsp;&amp;quot;&amp;quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;while&amp;nbsp;(names.hasMoreElements()){
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;得到参数名
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String&amp;nbsp;name&amp;nbsp;=&amp;nbsp;names.nextElement().toString();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;得到参数对应值
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String[]&amp;nbsp;values&amp;nbsp;=&amp;nbsp;request.getParameterValues(name);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;(int&amp;nbsp;i&amp;nbsp;=&amp;nbsp;0;&amp;nbsp;i&amp;nbsp;&amp;lt;&amp;nbsp;values.length;&amp;nbsp;i++)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sql&amp;nbsp;+=&amp;nbsp;values[i];
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(sqlValidate(sql))&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//TODO&amp;nbsp;这里直接抛异常处理，前后端交互项目中，请把错误信息按前后端&amp;quot;数据返回的VO&amp;quot;对象进行封装
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;throw&amp;nbsp;new&amp;nbsp;IOException(&amp;quot;您发送请求中的参数中含有非法字符&amp;quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;else&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;filterChain.doFilter(request,response);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/**
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;@description&amp;nbsp;匹配效验
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*/
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;protected&amp;nbsp;static&amp;nbsp;boolean&amp;nbsp;sqlValidate(String&amp;nbsp;str){
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;统一转为小写
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String&amp;nbsp;s&amp;nbsp;=&amp;nbsp;str.toLowerCase();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;过滤掉的sql关键字，特殊字符前面需要加\\进行转义
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String&amp;nbsp;badStr&amp;nbsp;=
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;select|update|and|or|delete|insert|truncate|char|into|substr|ascii|declare|exec|count|master|into|drop|execute|table|&amp;quot;+
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;char|declare|sitename|xp_cmdshell|like|from|grant|use|group_concat|column_name|&amp;quot;&amp;nbsp;+
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;information_schema.columns|table_schema|union|where|order|by|&amp;quot;&amp;nbsp;+
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;#39;\\*|\\;|\\-|\\--|\\+|\\,|\\//|\\/|\\%|\\#&amp;quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//使用正则表达式进行匹配
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;boolean&amp;nbsp;matches&amp;nbsp;=&amp;nbsp;s.matches(badStr);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;matches;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@Override
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;destroy()&amp;nbsp;{}
}&lt;/pre&gt;&lt;h2 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.5rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;4、nginx反向代理防止SQL注入&lt;/h2&gt;&lt;p&gt;越来越多网站使用nginx进行反向代理，该层我们也可以进行防止SQL注入配置。&lt;/p&gt;&lt;p&gt;将下面的Nginx配置文件代码放入到server块中，然后重启Nginx即可&lt;/p&gt;&lt;pre class=&quot;brush:bash;toolbar:false&quot;&gt;if&amp;nbsp;($request_method&amp;nbsp;!~*&amp;nbsp;GET|POST)&amp;nbsp;{&amp;nbsp;return&amp;nbsp;444;&amp;nbsp;}
&amp;nbsp;#使用444错误代码可以更加减轻服务器负载压力。
&amp;nbsp;#防止SQL注入
&amp;nbsp;if&amp;nbsp;($query_string&amp;nbsp;~*&amp;nbsp;(\$|&amp;#39;|--|[+|(%20)]union[+|(%20)]|[+|(%20)]insert[+|(%20)]|[+|(%20)]drop[+|(%20)]|[+|(%20)]truncate[+|(%20)]|[+|(%20)]update[+|(%20)]|[+|(%20)]from[+|(%20)]|[+|(%20)]grant[+|(%20)]|[+|(%20)]exec[+|(%20)]|[+|(%20)]where[+|(%20)]|[+|(%20)]select[+|(%20)]|[+|(%20)]and[+|(%20)]|[+|(%20)]or[+|(%20)]|[+|(%20)]count[+|(%20)]|[+|(%20)]exec[+|(%20)]|[+|(%20)]chr[+|(%20)]|[+|(%20)]mid[+|(%20)]|[+|(%20)]like[+|(%20)]|[+|(%20)]iframe[+|(%20)]|[\&amp;lt;|%3c]script[\&amp;gt;|%3e]|javascript|alert|webscan|dbappsecurity|style|confirm\(|innerhtml|innertext)(.*)$)&amp;nbsp;{&amp;nbsp;return&amp;nbsp;555;&amp;nbsp;}
&amp;nbsp;if&amp;nbsp;($uri&amp;nbsp;~*&amp;nbsp;(/~).*)&amp;nbsp;{&amp;nbsp;return&amp;nbsp;501;&amp;nbsp;}
&amp;nbsp;if&amp;nbsp;($uri&amp;nbsp;~*&amp;nbsp;(\\x.))&amp;nbsp;{&amp;nbsp;return&amp;nbsp;501;&amp;nbsp;}
&amp;nbsp;#防止SQL注入&amp;nbsp;
&amp;nbsp;if&amp;nbsp;($query_string&amp;nbsp;~*&amp;nbsp;&amp;quot;[;&amp;#39;&amp;lt;&amp;gt;].*&amp;quot;)&amp;nbsp;{&amp;nbsp;return&amp;nbsp;509;&amp;nbsp;}
&amp;nbsp;if&amp;nbsp;($request_uri&amp;nbsp;~&amp;nbsp;&amp;quot;&amp;nbsp;&amp;quot;)&amp;nbsp;{&amp;nbsp;return&amp;nbsp;509;&amp;nbsp;}
&amp;nbsp;if&amp;nbsp;($request_uri&amp;nbsp;~&amp;nbsp;(\/\.+))&amp;nbsp;{&amp;nbsp;return&amp;nbsp;509;&amp;nbsp;}
&amp;nbsp;if&amp;nbsp;($request_uri&amp;nbsp;~&amp;nbsp;(\.+\/))&amp;nbsp;{&amp;nbsp;return&amp;nbsp;509;&amp;nbsp;}
&amp;nbsp;#if&amp;nbsp;($uri&amp;nbsp;~*&amp;nbsp;(insert|select|delete|update|count|master|truncate|declare|exec|\*|\&amp;#39;)(.*)$&amp;nbsp;)&amp;nbsp;{&amp;nbsp;return&amp;nbsp;503;&amp;nbsp;}
&amp;nbsp;#防止SQL注入
&amp;nbsp;if&amp;nbsp;($request_uri&amp;nbsp;~*&amp;nbsp;&amp;quot;(cost\()|(concat\()&amp;quot;)&amp;nbsp;{&amp;nbsp;return&amp;nbsp;504;&amp;nbsp;}
&amp;nbsp;if&amp;nbsp;($request_uri&amp;nbsp;~*&amp;nbsp;&amp;quot;[+|(%20)]union[+|(%20)]&amp;quot;)&amp;nbsp;{&amp;nbsp;return&amp;nbsp;504;&amp;nbsp;}
&amp;nbsp;if&amp;nbsp;($request_uri&amp;nbsp;~*&amp;nbsp;&amp;quot;[+|(%20)]and[+|(%20)]&amp;quot;)&amp;nbsp;{&amp;nbsp;return&amp;nbsp;504;&amp;nbsp;}
&amp;nbsp;if&amp;nbsp;($request_uri&amp;nbsp;~*&amp;nbsp;&amp;quot;[+|(%20)]select[+|(%20)]&amp;quot;)&amp;nbsp;{&amp;nbsp;return&amp;nbsp;504;&amp;nbsp;}
&amp;nbsp;if&amp;nbsp;($request_uri&amp;nbsp;~*&amp;nbsp;&amp;quot;[+|(%20)]or[+|(%20)]&amp;quot;)&amp;nbsp;{&amp;nbsp;return&amp;nbsp;504;&amp;nbsp;}
&amp;nbsp;if&amp;nbsp;($request_uri&amp;nbsp;~*&amp;nbsp;&amp;quot;[+|(%20)]delete[+|(%20)]&amp;quot;)&amp;nbsp;{&amp;nbsp;return&amp;nbsp;504;&amp;nbsp;}
&amp;nbsp;if&amp;nbsp;($request_uri&amp;nbsp;~*&amp;nbsp;&amp;quot;[+|(%20)]update[+|(%20)]&amp;quot;)&amp;nbsp;{&amp;nbsp;return&amp;nbsp;504;&amp;nbsp;}
&amp;nbsp;if&amp;nbsp;($request_uri&amp;nbsp;~*&amp;nbsp;&amp;quot;[+|(%20)]insert[+|(%20)]&amp;quot;)&amp;nbsp;{&amp;nbsp;return&amp;nbsp;504;&amp;nbsp;}
&amp;nbsp;if&amp;nbsp;($query_string&amp;nbsp;~&amp;nbsp;&amp;quot;(&amp;lt;|%3C).*script.*(&amp;gt;|%3E)&amp;quot;)&amp;nbsp;{&amp;nbsp;return&amp;nbsp;505;&amp;nbsp;}
&amp;nbsp;if&amp;nbsp;($query_string&amp;nbsp;~&amp;nbsp;&amp;quot;GLOBALS(=|\[|\%[0-9A-Z]{0,2})&amp;quot;)&amp;nbsp;{&amp;nbsp;return&amp;nbsp;505;&amp;nbsp;}
&amp;nbsp;if&amp;nbsp;($query_string&amp;nbsp;~&amp;nbsp;&amp;quot;_REQUEST(=|\[|\%[0-9A-Z]{0,2})&amp;quot;)&amp;nbsp;{&amp;nbsp;return&amp;nbsp;505;&amp;nbsp;}
&amp;nbsp;if&amp;nbsp;($query_string&amp;nbsp;~&amp;nbsp;&amp;quot;proc/self/environ&amp;quot;)&amp;nbsp;{&amp;nbsp;return&amp;nbsp;505;&amp;nbsp;}
&amp;nbsp;if&amp;nbsp;($query_string&amp;nbsp;~&amp;nbsp;&amp;quot;mosConfig_[a-zA-Z_]{1,21}(=|\%3D)&amp;quot;)&amp;nbsp;{&amp;nbsp;return&amp;nbsp;505;&amp;nbsp;}
&amp;nbsp;if&amp;nbsp;($query_string&amp;nbsp;~&amp;nbsp;&amp;quot;base64_(en|de)code\(.*\)&amp;quot;)&amp;nbsp;{&amp;nbsp;return&amp;nbsp;505;&amp;nbsp;}
&amp;nbsp;if&amp;nbsp;($query_string&amp;nbsp;~&amp;nbsp;&amp;quot;[a-zA-Z0-9_]=http://&amp;quot;)&amp;nbsp;{&amp;nbsp;return&amp;nbsp;506;&amp;nbsp;}
&amp;nbsp;if&amp;nbsp;($query_string&amp;nbsp;~&amp;nbsp;&amp;quot;[a-zA-Z0-9_]=(\.\.//?)+&amp;quot;)&amp;nbsp;{&amp;nbsp;return&amp;nbsp;506;&amp;nbsp;}
&amp;nbsp;if&amp;nbsp;($query_string&amp;nbsp;~&amp;nbsp;&amp;quot;[a-zA-Z0-9_]=/([a-z0-9_.]//?)+&amp;quot;)&amp;nbsp;{&amp;nbsp;return&amp;nbsp;506;&amp;nbsp;}
&amp;nbsp;if&amp;nbsp;($query_string&amp;nbsp;~&amp;nbsp;&amp;quot;b(ultram|unicauca|valium|viagra|vicodin|xanax|ypxaieo)b&amp;quot;)&amp;nbsp;{&amp;nbsp;return&amp;nbsp;507;&amp;nbsp;}
&amp;nbsp;if&amp;nbsp;($query_string&amp;nbsp;~&amp;nbsp;&amp;quot;b(erections|hoodia|huronriveracres|impotence|levitra|libido)b&amp;quot;)&amp;nbsp;{return&amp;nbsp;507;&amp;nbsp;}
&amp;nbsp;if&amp;nbsp;($query_string&amp;nbsp;~&amp;nbsp;&amp;quot;b(ambien|bluespill|cialis|cocaine|ejaculation|erectile)b&amp;quot;)&amp;nbsp;{&amp;nbsp;return&amp;nbsp;507;&amp;nbsp;}
&amp;nbsp;if&amp;nbsp;($query_string&amp;nbsp;~&amp;nbsp;&amp;quot;b(lipitor|phentermin|pro[sz]ac|sandyauer|tramadol|troyhamby)b&amp;quot;)&amp;nbsp;{&amp;nbsp;return&amp;nbsp;507;&amp;nbsp;}
&amp;nbsp;#这里大家根据自己情况添加删减上述判断参数，cURL、wget这类的屏蔽有点儿极端了，但要“宁可错杀一千，不可放过一个”。
&amp;nbsp;if&amp;nbsp;($http_user_agent&amp;nbsp;~*&amp;nbsp;YisouSpider|ApacheBench|WebBench|Jmeter|JoeDog|Havij|GetRight|TurnitinBot|GrabNet|masscan|mail2000|github|wget|curl|Java|python)&amp;nbsp;{&amp;nbsp;return&amp;nbsp;508;&amp;nbsp;}
&amp;nbsp;#同上，大家根据自己站点实际情况来添加删减下面的屏蔽拦截参数。
&amp;nbsp;if&amp;nbsp;($http_user_agent&amp;nbsp;~*&amp;nbsp;&amp;quot;Go-Ahead-Got-It&amp;quot;)&amp;nbsp;{&amp;nbsp;return&amp;nbsp;508;&amp;nbsp;}
&amp;nbsp;if&amp;nbsp;($http_user_agent&amp;nbsp;~*&amp;nbsp;&amp;quot;GetWeb!&amp;quot;)&amp;nbsp;{&amp;nbsp;return&amp;nbsp;508;&amp;nbsp;}
&amp;nbsp;if&amp;nbsp;($http_user_agent&amp;nbsp;~*&amp;nbsp;&amp;quot;Go!Zilla&amp;quot;)&amp;nbsp;{&amp;nbsp;return&amp;nbsp;508;&amp;nbsp;}
&amp;nbsp;if&amp;nbsp;($http_user_agent&amp;nbsp;~*&amp;nbsp;&amp;quot;Download&amp;nbsp;Demon&amp;quot;)&amp;nbsp;{&amp;nbsp;return&amp;nbsp;508;&amp;nbsp;}
&amp;nbsp;if&amp;nbsp;($http_user_agent&amp;nbsp;~*&amp;nbsp;&amp;quot;Indy&amp;nbsp;Library&amp;quot;)&amp;nbsp;{&amp;nbsp;return&amp;nbsp;508;&amp;nbsp;}
&amp;nbsp;if&amp;nbsp;($http_user_agent&amp;nbsp;~*&amp;nbsp;&amp;quot;libwww-perl&amp;quot;)&amp;nbsp;{&amp;nbsp;return&amp;nbsp;508;&amp;nbsp;}
&amp;nbsp;if&amp;nbsp;($http_user_agent&amp;nbsp;~*&amp;nbsp;&amp;quot;Nmap&amp;nbsp;Scripting&amp;nbsp;Engine&amp;quot;)&amp;nbsp;{&amp;nbsp;return&amp;nbsp;508;&amp;nbsp;}
&amp;nbsp;if&amp;nbsp;($http_user_agent&amp;nbsp;~*&amp;nbsp;&amp;quot;~17ce.com&amp;quot;)&amp;nbsp;{&amp;nbsp;return&amp;nbsp;508;&amp;nbsp;}
&amp;nbsp;if&amp;nbsp;($http_user_agent&amp;nbsp;~*&amp;nbsp;&amp;quot;WebBench*&amp;quot;)&amp;nbsp;{&amp;nbsp;return&amp;nbsp;508;&amp;nbsp;}
&amp;nbsp;if&amp;nbsp;($http_user_agent&amp;nbsp;~*&amp;nbsp;&amp;quot;spider&amp;quot;)&amp;nbsp;{&amp;nbsp;return&amp;nbsp;508;&amp;nbsp;}&amp;nbsp;#这个会影响国内某些搜索引擎爬虫，比如：搜狗
&amp;nbsp;#拦截各恶意请求的UA，可以通过分析站点日志文件或者waf日志作为参考配置。
&amp;nbsp;if&amp;nbsp;($http_referer&amp;nbsp;~*&amp;nbsp;17ce.com)&amp;nbsp;{&amp;nbsp;return&amp;nbsp;509;&amp;nbsp;}
&amp;nbsp;#拦截17ce.com站点测速节点的请求，所以明月一直都说这些测速网站的数据仅供参考不能当真的。
&amp;nbsp;if&amp;nbsp;($http_referer&amp;nbsp;~*&amp;nbsp;WebBench*&amp;quot;)&amp;nbsp;{&amp;nbsp;return&amp;nbsp;509;&amp;nbsp;}
&amp;nbsp;#拦截WebBench或者类似压力测试工具，其他工具只需要更换名称即可。&lt;/pre&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;</description><pubDate>Wed, 06 Sep 2023 11:29:03 +0800</pubDate></item><item><title>JAVA开发小技巧--读取文件魔数来识别文件类型</title><link>https://blog.zender.top/post/MagicNumber.html</link><description>&lt;p&gt;我们在开发应用软件的时候一定会涉及到文件上传的功能，并且我们还要对用户上传文件的合法性进行校验。在文件校验的时候，我们通常会通过文件的后缀名去校验该文件是否合法，但是这样校验就会有一个弊端：如果用户上传的文件是改过文件名后缀的文件该怎么办呢 ？（比如某个上传接口只允许上传图片，那么如果我把一个 .txt 文件的后缀改成 .jpg ，那么就可以绕过文件的后缀名校验方法 ）俗话说“繁琐问题必有猥琐解法”，那么今天就给各位小伙伴介绍另外一种文件校验方式 —— 通过文件魔数值进行校验。&lt;br/&gt;&lt;/p&gt;&lt;p&gt;魔数这个词在不同领域代表不同的含义。在计算机领域，魔数有两个含义，一指用来判断文件类型的魔数；二指程序代码中的魔数，也称魔法值。&lt;/p&gt;&lt;p&gt;我们今天所说的魔数就是表示不同文件类型的魔术数字，它指定是文件的最开头的几个用于唯一区别其它文件类型的字节，有了这些魔术数字，我们就可以很方便的区别不同的文件，这也使得编程变得更加容易，减少了我们用于区别一个文件的文件类型所要花费的时间 。关于文件校验也没有什么需要过多阐述的东西，下面就直接上代码&lt;/p&gt;&lt;pre class=&quot;brush:java;toolbar:false&quot;&gt;/**
&amp;nbsp;*&amp;nbsp;利用文件魔数值判断文件类型
&amp;nbsp;*/
public&amp;nbsp;class&amp;nbsp;FileMagicUtils&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/**
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;缓存文件魔数值
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*/
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;static&amp;nbsp;final&amp;nbsp;HashMap&amp;lt;String,&amp;nbsp;String&amp;gt;&amp;nbsp;mFileTypes&amp;nbsp;=&amp;nbsp;new&amp;nbsp;HashMap&amp;lt;&amp;gt;();

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;static&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mFileTypes.put(&amp;quot;jpg&amp;quot;,&amp;nbsp;&amp;quot;FFD8FFE0&amp;quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mFileTypes.put(&amp;quot;png&amp;quot;,&amp;nbsp;&amp;quot;89504E47&amp;quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mFileTypes.put(&amp;quot;gif&amp;quot;,&amp;nbsp;&amp;quot;47494638&amp;quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mFileTypes.put(&amp;quot;tif&amp;quot;,&amp;nbsp;&amp;quot;49492A00&amp;quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mFileTypes.put(&amp;quot;bmp&amp;quot;,&amp;nbsp;&amp;quot;424D&amp;quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mFileTypes.put(&amp;quot;psd&amp;quot;,&amp;nbsp;&amp;quot;38425053&amp;quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mFileTypes.put(&amp;quot;xml&amp;quot;,&amp;nbsp;&amp;quot;3C3F786D6C&amp;quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mFileTypes.put(&amp;quot;html&amp;quot;,&amp;nbsp;&amp;quot;68746D6C3E&amp;quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mFileTypes.put(&amp;quot;doc&amp;quot;,&amp;nbsp;&amp;quot;D0CF11E0&amp;quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mFileTypes.put(&amp;quot;mdb&amp;quot;,&amp;nbsp;&amp;quot;5374616E64617264204A&amp;quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mFileTypes.put(&amp;quot;pdf&amp;quot;,&amp;nbsp;&amp;quot;255044462D312E&amp;quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mFileTypes.put(&amp;quot;docx&amp;quot;,&amp;nbsp;&amp;quot;504B0304&amp;quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mFileTypes.put(&amp;quot;rar&amp;quot;,&amp;nbsp;&amp;quot;52617221&amp;quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mFileTypes.put(&amp;quot;avi&amp;quot;,&amp;nbsp;&amp;quot;41564920&amp;quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/**
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;获取文件魔数值
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;@param&amp;nbsp;file&amp;nbsp;对应文件
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;@return&amp;nbsp;16进制魔数值
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*/
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;static&amp;nbsp;String&amp;nbsp;getMagicNumber(File&amp;nbsp;file)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;byte[]&amp;nbsp;bytes&amp;nbsp;=&amp;nbsp;new&amp;nbsp;byte[20];
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try&amp;nbsp;(FileInputStream&amp;nbsp;inputStream&amp;nbsp;=&amp;nbsp;new&amp;nbsp;FileInputStream(file))&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;inputStream.read(bytes,&amp;nbsp;0,&amp;nbsp;20);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;StringBuilder&amp;nbsp;sb&amp;nbsp;=&amp;nbsp;new&amp;nbsp;StringBuilder();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;(byte&amp;nbsp;b&amp;nbsp;:&amp;nbsp;bytes)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;b&amp;nbsp;&amp;amp;&amp;nbsp;0xFF转16禁止。
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sb.append(Integer.toHexString(b&amp;nbsp;&amp;amp;&amp;nbsp;0xFF));
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;sb.toString().toUpperCase();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;catch&amp;nbsp;(IOException&amp;nbsp;e)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;throw&amp;nbsp;new&amp;nbsp;RuntimeException(e);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/**
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;判断单个文件的后缀名
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;@param&amp;nbsp;file&amp;nbsp;文件
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;@param&amp;nbsp;fileSuffix&amp;nbsp;需要判断的文件后缀名
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*/
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;static&amp;nbsp;boolean&amp;nbsp;isAllowedExtension(File&amp;nbsp;file,&amp;nbsp;String&amp;nbsp;fileSuffix){
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String&amp;nbsp;magicNumber&amp;nbsp;=&amp;nbsp;getMagicNumber(file);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;System.out.println(magicNumber);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;magicNumber.startsWith(mFileTypes.get(fileSuffix));
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/**
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;判断单个文件的后缀名是否在一个范围
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;@param&amp;nbsp;file&amp;nbsp;文件
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;@param&amp;nbsp;fileSuffixs&amp;nbsp;需要判断的文件后缀名
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*/
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;static&amp;nbsp;boolean&amp;nbsp;isAllowedExtension(File&amp;nbsp;file,&amp;nbsp;String...&amp;nbsp;fileSuffixs){
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String&amp;nbsp;magicNumber&amp;nbsp;=&amp;nbsp;getMagicNumber(file);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;System.out.println(magicNumber);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;boolean&amp;nbsp;temp&amp;nbsp;=&amp;nbsp;false;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;(String&amp;nbsp;fileSuffix&amp;nbsp;:&amp;nbsp;fileSuffixs)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if(magicNumber.startsWith(mFileTypes.get(fileSuffix))){
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;temp&amp;nbsp;=&amp;nbsp;true;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;temp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;static&amp;nbsp;void&amp;nbsp;main(String[]&amp;nbsp;args)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;System.out.println(isAllowedExtension(new&amp;nbsp;File(&amp;quot;D:\\图片1.png&amp;quot;),&amp;nbsp;&amp;quot;png&amp;quot;,&amp;nbsp;&amp;quot;jpg&amp;quot;));
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
}&lt;/pre&gt;&lt;p&gt;&lt;img src=&quot;https://blog.zender.top/zb_users/upload/2023/202308241692840568241229.png&quot; alt=&quot;image.png&quot;/&gt;&lt;/p&gt;&lt;p&gt;运行结果&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://blog.zender.top/zb_users/upload/2023/202308241692840587778167.png&quot; alt=&quot;image.png&quot;/&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; font-size: 16px; background-color: #FFFFFF;&quot;&gt;方法很简单，没有什么复杂的逻辑，唯一需要注意的就是：代码中罗列出的文件魔数值都是一些常用的文件所对应的魔数值，如果有特殊的文件需要校验的话，可以先跑一次校验工具，获取到对应的魔数值以后，将魔数值加到 HashMap 中就可以正常使用。&lt;/span&gt;&lt;/p&gt;</description><pubDate>Thu, 24 Aug 2023 09:28:16 +0800</pubDate></item><item><title>分类树菜单优化</title><link>https://blog.zender.top/post/classification_tree.html</link><description>&lt;p&gt;&lt;span style=&quot;font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; font-size: 16px; background-color: #FFFFFF;&quot;&gt;分类树查询功能，在各个业务系统中可以说随处可见，特别是在电商系统中。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; font-size: 16px; background-color: #FFFFFF;&quot;&gt;&lt;img src=&quot;https://blog.zender.top/zb_users/upload/2023/202308221692666110282131.png&quot; alt=&quot;image.png&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; font-size: 16px; background-color: #FFFFFF;&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;但就是这样一个简单的分类树查询功能，我们却优化了5次。&lt;/p&gt;&lt;p&gt;到底是怎么回事呢？&lt;/p&gt;&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;背景&lt;/h1&gt;&lt;p&gt;我们的网站使用了SpringBoot推荐的模板引擎：Thymeleaf，进行动态渲染。&lt;/p&gt;&lt;p&gt;它是一个XML/XHTML/HTML5模板引擎，可用于Web与非Web环境中的应用开发。&lt;/p&gt;&lt;p&gt;它提供了一个用于整合SpringMVC的可选模块，在应用开发中，我们可以使用Thymeleaf来完全代替JSP或其他模板引擎，如Velocity\FreeMarker等。&lt;/p&gt;&lt;p&gt;前端开发写好Thymeleaf的模板文件，调用后端接口获取数据，进行动态绑定，就能把想要的内容展示给用户。&lt;/p&gt;&lt;p&gt;由于当时这个是从0-1的新项目，为了开快速开发功能，我们第一版接口，直接从数据库中查询分类数据，组装成分类树，然后返回给前端。&lt;/p&gt;&lt;p&gt;通过这种方式，简化了数据流程，快速把整个页面功能调通了。&lt;/p&gt;&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;第1次优化&lt;/h1&gt;&lt;p&gt;我们将该接口部署到dev环境，刚开始没啥问题。&lt;/p&gt;&lt;p&gt;随着开发人员添加的分类越来越多，很快就暴露出性能瓶颈。&lt;/p&gt;&lt;p&gt;我们不得不做优化了。&lt;/p&gt;&lt;p&gt;我们第一个想到的是：加Redis缓存。&lt;/p&gt;&lt;p&gt;流程图如下：&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://blog.zender.top/zb_users/upload/2023/202308221692666123765791.png&quot; alt=&quot;image.png&quot;/&gt;&lt;/p&gt;&lt;p&gt;于是暂时这样优化了一下：&lt;/p&gt;&lt;blockquote style=&quot;margin-top: 8px; margin-bottom: 8px; padding-right: 12px; padding-left: 12px; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; font-size: 16px; white-space: normal;&quot;&gt;&lt;ol class=&quot;wiz-list-level1 list-paddingleft-2&quot; style=&quot;margin-top: 8px; padding-left: 2rem;&quot;&gt;&lt;li&gt;&lt;p&gt;用户访问接口获取分类树时，先从Redis中查询数据。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;如果Redis中有数据，则直接数据。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;如果Redis中没有数据，则再从数据库中查询数据，拼接成分类树返回。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;将从数据库中查到的分类树的数据，保存到Redis中，设置过期时间5分钟。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;将分类树返回给用户。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;我们在Redis中定义一个了key，value是一个分类树的json格式转换成了字符串，使用简单的key/value形式保存数据。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/blockquote&gt;&lt;p&gt;经过这样优化之后，dev环境的联调和自测顺利完成了。&lt;/p&gt;&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;第2次优化&lt;/h1&gt;&lt;p&gt;我们将这个功能部署到st环境了。&lt;/p&gt;&lt;p&gt;刚开始测试同学没有发现什么问题，但随着后面不断地深入测试，隔一段时间就出现一次首页访问很慢的情况。&lt;/p&gt;&lt;p&gt;于是，我们马上进行了第2次优化。&lt;/p&gt;&lt;p&gt;我们决定使用Job定期异步更新分类树到Redis中，在系统上线之前，会先生成一份数据。&lt;/p&gt;&lt;p&gt;当然为了保险起见，防止Redis在哪条突然挂了，之前分类树同步写入Redis的逻辑还是保留。&lt;/p&gt;&lt;p&gt;于是，流程图改成了这样：&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://blog.zender.top/zb_users/upload/2023/202308221692666136659091.png&quot; alt=&quot;image.png&quot;/&gt;&lt;/p&gt;&lt;p&gt;增加了一个job每隔5分钟执行一次，从数据库中查询分类数据，封装成分类树，更新到Redis缓存中。&lt;/p&gt;&lt;p&gt;其他的流程保持不变。&lt;/p&gt;&lt;p&gt;此外，Redis的过期时间之前设置的5分钟，现在要改成永久。&lt;/p&gt;&lt;p&gt;通过这次优化之后，st环境就没有再出现过分类树查询的性能问题了。&lt;/p&gt;&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;第3次优化&lt;/h1&gt;&lt;p&gt;测试了一段时间之后，整个网站的功能快要上线了。&lt;/p&gt;&lt;p&gt;为了保险起见，我们需要对网站首页做一次压力测试。&lt;/p&gt;&lt;p&gt;果然测出问题了，网站首页最大的qps是100多，最后发现是每次都从Redis获取分类树导致的网站首页的性能瓶颈。&lt;/p&gt;&lt;p&gt;我们需要做第3次优化。&lt;/p&gt;&lt;p&gt;该怎么优化呢？&lt;/p&gt;&lt;p&gt;答：加内存缓存。&lt;/p&gt;&lt;p&gt;如果加了内存缓存，就需要考虑数据一致性问题。&lt;/p&gt;&lt;p&gt;内存缓存是保存在服务器节点上的，不同的服务器节点更新的频率可能有点差异，这样可能会导致数据的不一致性。&lt;/p&gt;&lt;p&gt;但分类本身是更新频率比较低的数据，对于用户来说不太敏感，即使在短时间内，用户看到的分类树有些差异，也不会对用户造成太大的影响。&lt;/p&gt;&lt;p&gt;因此，分类树这种业务场景，是可以使用内存缓存的。&lt;/p&gt;&lt;p&gt;于是，我们使用了Spring推荐的caffine作为内存缓存。&lt;/p&gt;&lt;p&gt;改造后的流程图如下：&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://blog.zender.top/zb_users/upload/2023/202308221692666152920961.png&quot; alt=&quot;image.png&quot;/&gt;&lt;/p&gt;&lt;p&gt;用户访问接口时改成先从本地缓存分类数查询数据。&lt;/p&gt;&lt;p&gt;如果本地缓存有，则直接返回。&lt;/p&gt;&lt;p&gt;如果本地缓存没有，则从Redis中查询数据。&lt;/p&gt;&lt;p&gt;如果Redis中有数据，则将数据更新到本地缓存中，然后返回数据。&lt;/p&gt;&lt;p&gt;如果Redis中也没有数据（说明Redis挂了），则从数据库中查询数据，更新到Redis中（万一Redis恢复了呢），然后更新到本地缓存中，返回返回数据。&lt;/p&gt;&lt;p&gt;需要注意的是，需要改本地缓存设置一个过期时间，这里设置的5分钟，不然的话，没办法获取新的数据。&lt;/p&gt;&lt;p&gt;这样优化之后，再次做网站首页的压力测试，qps提升到了500多，满足上线要求。&lt;/p&gt;&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;第4次优化&lt;/h1&gt;&lt;p&gt;之后，这个功能顺利上线了。&lt;/p&gt;&lt;p&gt;使用了很长一段时间没有出现问题。&lt;/p&gt;&lt;p&gt;两年后的某一天，有用户反馈说，网站首页有点慢。&lt;/p&gt;&lt;p&gt;我们排查了一下原因发现，分类树的数据太多了，一次性返回了上万个分类。&lt;/p&gt;&lt;p&gt;原来在系统上线的这两年多的时间内，运营同学在系统后台增加了很多分类。&lt;/p&gt;&lt;p&gt;我们需要做第4次优化。&lt;/p&gt;&lt;p&gt;这时要如何优化呢？&lt;/p&gt;&lt;p&gt;限制分类树的数量？&lt;/p&gt;&lt;p&gt;答：也不太现实，目前这个业务场景就是有这么多分类，不能让用户选择不到他想要的分类吧？&lt;/p&gt;&lt;p&gt;这时我们想到最快的办法是开启nginx的GZip功能。&lt;/p&gt;&lt;p&gt;让数据在传输之前，先压缩一下，然后进行传输，在用户浏览器中，自动解压，将真实的分类树数据展示给用户。&lt;/p&gt;&lt;p&gt;之前调用接口返回的分类树有1MB的大小，优化之后，接口返回的分类树的大小是100Kb，一下子缩小了10倍。&lt;/p&gt;&lt;p&gt;这样简单的优化之后，性能提升了一些。&lt;/p&gt;&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;第5次优化&lt;/h1&gt;&lt;p&gt;经过上面优化之后，用户很长一段时间都没有反馈性能问题。&lt;/p&gt;&lt;p&gt;但有一天公司同事在排查Redis中大key的时候，揪出了分类树。之前的分类树使用key/value的结构保存数据的。&lt;/p&gt;&lt;p&gt;我们不得不做第5次优化。&lt;/p&gt;&lt;p&gt;为了优化在Redis中存储数据的大小，我们首先需要对数据进行瘦身。&lt;/p&gt;&lt;p&gt;只保存需要用到的字段。&lt;/p&gt;&lt;p&gt;例如：&lt;/p&gt;&lt;pre class=&quot;brush:java;toolbar:false&quot;&gt;@AllArgsConstructor
@Data
public&amp;nbsp;class&amp;nbsp;Category&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;Long&amp;nbsp;id;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;String&amp;nbsp;name;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;Long&amp;nbsp;parentId;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;Date&amp;nbsp;inDate;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;Long&amp;nbsp;inUserId;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;String&amp;nbsp;inUserName;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;List&amp;lt;Category&amp;gt;&amp;nbsp;children;
}&lt;/pre&gt;&lt;p&gt;像这个分类对象中inDate、inUserId和inUserName字段是可以不用保存的。&lt;/p&gt;&lt;p&gt;修改自动名称。&lt;/p&gt;&lt;p&gt;例如：&lt;/p&gt;&lt;pre class=&quot;brush:java;toolbar:false&quot;&gt;@AllArgsConstructor
@Data
public&amp;nbsp;class&amp;nbsp;Category&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/**
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;分类编号
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*/
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@JsonProperty(&amp;quot;i&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;Long&amp;nbsp;id;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/**
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;分类层级
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*/
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@JsonProperty(&amp;quot;l&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;Integer&amp;nbsp;level;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/**
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;分类名称
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*/
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@JsonProperty(&amp;quot;n&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;String&amp;nbsp;name;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/**
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;父分类编号
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*/
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@JsonProperty(&amp;quot;p&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;Long&amp;nbsp;parentId;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/**
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;子分类列表
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*/
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@JsonProperty(&amp;quot;c&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;List&amp;lt;Category&amp;gt;&amp;nbsp;children;
}&lt;/pre&gt;&lt;p&gt;由于在一万多条数据中，每条数据的字段名称是固定的，他们的重复率太高了。&lt;/p&gt;&lt;p&gt;由此，可以在json序列化时，改成一个简短的名称，以便于返回更少的数据大小。&lt;/p&gt;&lt;p&gt;这还不够，需要对存储的数据做压缩。&lt;/p&gt;&lt;p&gt;之前在Redis中保存的key/value，其中的value是json格式的字符串。&lt;/p&gt;&lt;p&gt;其实RedisTemplate支持，value保存byte数组。&lt;/p&gt;&lt;p&gt;先将json字符串数据用GZip工具类压缩成byte数组，然后保存到Redis中。&lt;/p&gt;&lt;p&gt;再获取数据时，将byte数组转换成json字符串，然后再转换成分类树。&lt;/p&gt;&lt;p&gt;这样优化之后，保存到Redis中的分类树的数据大小，一下子减少了10倍，Redis的大key问题被解决了。&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;原文URL:https://mp.weixin.qq.com/s/W7dvcaUMmz_hp5X9xDhS6w&lt;/p&gt;</description><pubDate>Tue, 22 Aug 2023 09:01:13 +0800</pubDate></item><item><title>JDK1.0到JDK20各版本特性及趣闻</title><link>https://blog.zender.top/post/JDK_1_20.html</link><description>&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;JDK 1.0 （1996）&lt;/h1&gt;&lt;p&gt;包含Java语言、Java类库和Java虚拟机。支持图形用户界面（GUI）、数据库连接（JDBC）等功能。&lt;/p&gt;&lt;p&gt;&amp;quot;Oak&amp;quot; 是 Java 语言的前身，由于“Oak”这个名称已经被其他公司使用了，因此在1995年，Java 的创造者——Sun 公司决定将其改名为“Java”。 据说，这个名称是来自于一家咖啡店。当时，在设计 Java 语言的时候，创始团队成员到一家咖啡店买咖啡时，发现这家咖啡店的名字叫做“Java Coffee”，于是决定以“Java”作为新的名称。&lt;/p&gt;&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;JDK 1.1 （1997-2）&lt;/h1&gt;&lt;p&gt;增加了内部类、JavaBeans、远程方法调用（RMI）等功能。&lt;br/&gt;&lt;/p&gt;&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;JDK 1.2 / Java 2 （1998-12）&lt;/h1&gt;&lt;p&gt;引入了Swing组件、反射机制和动态代理、集合框架、Java 2D和Java 3D图形等特性。&lt;br/&gt;&lt;/p&gt;&lt;p&gt;这是Java SE第一个真正具有商业价值的版本。该版本引入了Java命名空间（namespace）等特性，使得Java应用程序能够更好地组织和管理。在此阶段并存过三个虚拟机,Classic VM、HotSpot VM和Exact VM，其中HotSpot在1999年4月诞生。&lt;br/&gt;&lt;/p&gt;&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;JDK 1.3 （2000-5）&lt;/h1&gt;&lt;p&gt;增加了Java平台的功能、底层类库、增强了AWT和Swing用户界面等。&lt;/p&gt;&lt;p&gt;当时Sun公司正在面对来自Microsoft的严峻挑战，后者推出了.NET框架，试图颠覆Java的地位。因此，Sun公司决定加快Java的开发速度，增强其竞争力。&lt;/p&gt;&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;JDK 1.4LST （2002-2）&lt;/h1&gt;&lt;p&gt;引入Java管理扩展（JMX）、新的I/O API，称为NIO（New I/O）、断言、内置的XML解析器（SAX和DOM）等特性。&lt;/p&gt;&lt;p&gt;这是Java SE中功能最全面的版本之一。该版本引入了NIO和XML解析器等新特性，提高了Java程序的性能和扩展性。&lt;/p&gt;&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;JDK 5.0 / Java 1.5 （2004-9）&lt;/h1&gt;&lt;p&gt;引入了泛型、枚举类型、自动装箱和拆箱、注解、并发编程API等特性。&lt;/p&gt;&lt;p&gt;这是Java SE中功能最丰富的版本之一。该版本引入了泛型、类型安全的枚举、自动装箱/拆箱和注解等新特性，使得Java程序的编写更加简洁和精简。&lt;/p&gt;&lt;p&gt;Sun公司从这个版本开始放弃了谦逊的“JDK 1.x”的命名方式,将产品版本号修改成了“JDK x”。Sun 公司这样做的原因是，从 JDK 5.0 开始，Java 平台的发布计划每年发布一个版本，为了避免版本号混淆，同时更好地传达 Java 平台的快速迭代和更新，采用了新的命名方式。&lt;/p&gt;&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;JDK 6 （2006-12）&lt;/h1&gt;&lt;p&gt;引入了JDBC 4.0 API、Java监视与管理控制台（JConsole）、增加了对动态语言的支持、改进的GUI工具包（Swing）、改进的Java Web Start、改进的性能与安全性等特性。&lt;/p&gt;&lt;p&gt;JDK的更新没有能够继续维持两年发布一个主版本的研发速度,这导致了JDK 6的生命周期异常的长。 Java 6 在银行等体系使用比较多可能有以下几个原因：&lt;/p&gt;&lt;p&gt;稳定性高：Java 6 是一个相对成熟和稳定的版本，由于经过多年的实践和测试，已被广泛认可并得到了验证，符合金融行业对稳定性的要求。虽然 Java 7 和 Java 8 等新版本带来了更多的功能和优化，但这些功能并不是每个银行都需要，而且新版本的稳定性也需要一定时间的验证和检验。&lt;/p&gt;&lt;p&gt;可控性强：银行等体系对软件的可控性要求非常高，需要确保软件能够稳定运行，并具备良好的可维护性。在这个方面，Java 6 较新的特性和更新也相对较少，使其相对容易维护和掌控。&lt;/p&gt;&lt;p&gt;成本考虑：对于一些大型机构，升级到最新的 Java 版本可能需要付出巨大的成本。例如，需要修改现有的代码和应用程序、重新测试和审计等。另外，许多旧系统都还在运行 Java 6，升级到新版本需要重新评估风险和效益，这也需要额外的成本和投入。&lt;/p&gt;&lt;p&gt;总之，Java 6 在银行等体系中使用较多是由于其稳定性、可控性和成本方面的考虑。然而，随着时间的推移和新版本的推出，银行等体系也需要逐步升级到更先进的 Java 版本，以满足业务的需求和提高运行效率。&lt;/p&gt;&lt;p&gt;此外，虽然 Java 7 和 Java 8 带来了更多的新特性和优化，但是在银行等体系中升级到新版本需要付出巨大的成本，例如重新编写和测试代码、重新评估安全性等，这也是银行等企业将 Java 6 作为开发平台的一大原因。&lt;/p&gt;&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;JDK 7 （2011-7）&lt;/h1&gt;&lt;p&gt;引入了switch语句的字符串表达式形式、二进制字面量和下划线数字表示法、Try with Resources语句等特性。&lt;/p&gt;&lt;p&gt;在JDK 7开发期间，Sun公司相继在技术竞争和商业竞争中陷入泥潭，公司的股票 市值跌至仅有高峰时期的3%，已无力推动JDK 7的研发工作按计划继续进行。为了尽快结束JDK 7长期跳票的问题，Oracle收购Sun公司后随即宣布马上实行“B计划”，大幅裁剪了JDK 7预定目标，以保证 JDK 7的正式版能够于2011年7月28日准时发布。&lt;/p&gt;&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;JDK 8LST （2014-3）&lt;/h1&gt;&lt;p&gt;引入了Lambda表达式、Stream API、Date/Time API、接口默认方法、方法引用、重复注解、Optional 类、Nashorn JavaScript 引擎等特性。&lt;/p&gt;&lt;p&gt;这是Java SE中最重要的版本之一，引入了Lambda表达式、Stream API、Default方法、DateTime API等新功能，极大地提高了Java程序的编写效率和开发效率。 原定于2013年9月发布,最终还是跳票到了2014年3月18日，从JDK 8开始，Oracle启用JEP（JDK Enhancement Proposals）来定义和管理纳入新版JDK发布范围的功能特性。&lt;/p&gt;&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;JDK 9 （2017-9）&lt;/h1&gt;&lt;p&gt;引入了模块化系统、JShell、HTTP/2客户端和服务器端API等特性。&lt;/p&gt;&lt;p&gt;发版节奏变更：以后JDK将会在每年的3月和9月各发布一个大版本、每6个JDK大版本中才会被划出一个长期支持(Long Term Support,LTS)版,只有LTS版的JDK能够获得为期3年的支持和更新，JDK 8和JDK 11会是LTS版。 再下一个就到2021年发布的JDK 17 了。&lt;/p&gt;&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;JDK 10 （2018-3）&lt;/h1&gt;&lt;p&gt;引入了局部变量类型推断、改进了Javadoc等特性。&lt;/p&gt;&lt;p&gt;2018年3月27日，Android的Java侵权案有了最终判决，法庭裁定Google赔偿Oracle合计88亿美元，要知道2009年Oracle收购Sun也就只花了74亿，收购完成后随即就用Sun的专利把Google告上了法庭，经过Oracle法务部的几轮神操作，一场官司的赔偿让收购Sun公司等同免费。&lt;/p&gt;&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;JDK 11LST （2018-9）&lt;/h1&gt;&lt;p&gt;引入了HTTP Client API（HTTP 客户端API）、Nest-Based Access Control（基于嵌套的访问控制）、Flight Recorder（飞行记录器）、Epsilon GC（Epsilon垃圾回收器）改进的ZGC（Z Garbage Collector）等特性。&lt;/p&gt;&lt;p&gt;这是Java SE中长期支持版本中最新的一个版本，引入了HTTP客户端、Local-Variable Syntax for Lambda Parameters、ZGC等新功能，具有重要的实用价值。 2018年10月，最后一届JavaOne2018在旧金山举行，这个1996年伴随Java一同诞生、成长的开发者年度盛会落下帷幕。此外Java Mission Control（Java Mission Control （JMC）是一个由 Oracle 公司开发的用于监视、管理和分析 Java 应用程序的工具。）开发团队也于6月被Oracle解散。&lt;/p&gt;&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;JDK 12（2019年3月）&lt;/h1&gt;&lt;p&gt;引入了Switch表达式预览功能、微基准测试套件等特性。&lt;/p&gt;&lt;p&gt;2019年2月，在JDK 12发布前夕，Oracle果然如之前宣布那样在六个月之后就放弃了对上一个版本OpenJDK的维护，RedHat同时从Oracle手上接过OpenJDK 8和OpenJDK 11的管理权利和维护职责。&lt;/p&gt;&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;JDK 13（2019年9月）&lt;/h1&gt;&lt;p&gt;引入了文本块预览功能、动态CDS归档等特性。&lt;/p&gt;&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;JDK 14LST（2020年3月）&lt;/h1&gt;&lt;p&gt;引入了实例模式匹配预览功能、非易失性内存支持等特性。&lt;/p&gt;&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;JDK 15（2020年9月）&lt;/h1&gt;&lt;p&gt;引入了密封类预览功能、Edwards-Curve数字签名算法等特性。&lt;/p&gt;&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;JDK 16（2021年3月）&lt;/h1&gt;&lt;p&gt;引入了记录类预览功能、向量API等特性。&lt;/p&gt;&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;JDK 17LST（2021年9月）&lt;/h1&gt;&lt;p&gt;长期支持版本，引入了模式匹配、密封类等正式特性。&lt;/p&gt;&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;JDK 18（2022年3月）&lt;/h1&gt;&lt;p&gt;引入简单的web服务器、支持在Java API文档中加入代码片段、制定UTF-8作为Java API的默认字符集。&lt;/p&gt;&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;JDK 19 （2022年9月）&lt;/h1&gt;&lt;p&gt;引入结构化并发的API来简化多线程的编程、支持虚拟线程等。&lt;/p&gt;&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;JDK 20LST （2023年3月）：&lt;/h1&gt;&lt;p&gt;引入记录模式、Switch模式匹配等。&lt;/p&gt;&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;当前市面JDK版本使用情况&lt;/h1&gt;&lt;p&gt;New Relic近日发布了一份《2023 年 Java 生态系统状况报告》，这份报告收集了上百万份线上的应用程序的数据，统计了目前生产环境中使用最多的JDK版本，最受欢迎的JDK供应商以及容器等的相关数据。&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://blog.zender.top/zb_users/upload/2023/202308161692176507665155.png&quot; alt=&quot;image.png&quot;/&gt;&lt;/p&gt;&lt;p&gt;目前市面上有超过56%的应用程序使用了JDK 11，而相比之下，Java 8 的使用从2020年的84%降低到了现在的32%左右，大部分公司在这三年之间都升级到了JDK 11 或者 JDK 17这两个LTS版本上面。&lt;/p&gt;&lt;p&gt;在JDK 的厂商上面，Amazon这两年的增长量是比较大的，从22%上升到了31%，是目前市面上的各个JDK厂商中占比最高的。&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;原文：https://www.cnblogs.com/Jcloud/p/17631822.html&lt;/p&gt;</description><pubDate>Wed, 16 Aug 2023 17:01:13 +0800</pubDate></item><item><title>使用 Redis 对用户 IP 进行接口限流</title><link>https://blog.zender.top/post/spring_current_limiting.html</link><description>&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; white-space: normal; font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; color: #0E88EB;&quot;&gt;一、思路&lt;/span&gt;&lt;/h1&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;margin-top: 8px; margin-bottom: 0px; white-space: normal; font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; font-size: 0.938rem;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;使用接口限流的主要目的在于提高系统的稳定性，防止接口被恶意打击（短时间内大量请求）。&lt;/span&gt;&lt;/p&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;margin-top: 8px; margin-bottom: 0px; white-space: normal; font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; font-size: 0.938rem;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;比如要求某接口在1分钟内请求次数不超过1000次，那么应该如何设计代码呢？&lt;/span&gt;&lt;/p&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;margin-top: 8px; margin-bottom: 0px; white-space: normal; font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; font-size: 0.938rem;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;下面讲两种思路，如果想看代码可直接翻到后面的代码部分。&lt;/span&gt;&lt;/p&gt;&lt;h2 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.5rem; white-space: normal; font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; color: rgb(14, 136, 235);&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;1.1 固定时间段（旧思路）&lt;/span&gt;&lt;/h2&gt;&lt;h3 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.25rem; white-space: normal; font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;1.1.1 思路描述&lt;/span&gt;&lt;/h3&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;margin-top: 8px; margin-bottom: 0px; white-space: normal; font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; font-size: 0.938rem;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;该方案的思路是：使用Redis记录固定时间段内某用户IP访问某接口的次数，其中：&lt;/span&gt;&lt;/p&gt;&lt;ul data-tool=&quot;mdnice编辑器&quot; class=&quot; list-paddingleft-2&quot; style=&quot;margin-top: 8px; padding-left: 2rem; font-size: 16px; white-space: normal; font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif;&quot;&gt;&lt;li&gt;&lt;section style=&quot;color: rgb(1, 1, 1); font-size: 0.938rem;&quot;&gt;&lt;strong style=&quot;color: rgb(14, 136, 235);&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;Redis的key&lt;/span&gt;&lt;/strong&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;：用户IP + 接口方法名&lt;/span&gt;&lt;/section&gt;&lt;/li&gt;&lt;li&gt;&lt;section style=&quot;color: rgb(1, 1, 1); font-size: 0.938rem;&quot;&gt;&lt;strong style=&quot;color: rgb(14, 136, 235);&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;Redis的value&lt;/span&gt;&lt;/strong&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;：当前接口访问次数。&lt;/span&gt;&lt;/section&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;margin-top: 8px; margin-bottom: 0px; white-space: normal; font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; font-size: 0.938rem;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;当用户在近期内第一次访问该接口时，向Redis中设置一个包含了用户IP和接口方法名的key，value的值初始化为1（表示第一次访问当前接口）。同时，设置该key的过期时间（比如为60秒）。&lt;/span&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;&lt;br/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;margin-top: 8px; margin-bottom: 0px; color: rgb(0, 0, 0); font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; font-size: 0.938rem;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;之后，只要这个key还未过期，用户每次访问该接口都会导致value自增1次。&lt;/span&gt;&lt;/p&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;margin-top: 8px; margin-bottom: 0px; color: rgb(0, 0, 0); font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; font-size: 0.938rem;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;用户每次访问接口前，先从Redis中拿到当前接口访问次数，如果发现访问次数大于规定的次数（如超过1000次），则向用户返回接口访问失败的标识。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://blog.zender.top/zb_users/upload/2023/202307281690535291379257.png&quot; alt=&quot;image.png&quot;/&gt;&lt;/p&gt;&lt;h5 data-tool=&quot;mdnice编辑器&quot; style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1rem; white-space: normal; background-color: rgb(255, 255, 255); font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;1.1.2 思路缺陷&lt;/span&gt;&lt;/h5&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;margin-top: 8px; margin-bottom: 0px; white-space: normal; background-color: rgb(255, 255, 255); font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; font-size: 0.938rem;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;该方案的缺点在于，限流时间段是固定的。&lt;/span&gt;&lt;/p&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;margin-top: 8px; margin-bottom: 0px; white-space: normal; background-color: rgb(255, 255, 255); font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; font-size: 0.938rem;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;比如要求某接口在1分钟内请求次数不超过1000次，观察以下流程：&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://blog.zender.top/zb_users/upload/2023/202307281690535299557008.png&quot; alt=&quot;image.png&quot;/&gt;&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://blog.zender.top/zb_users/upload/2023/202307281690535315343981.png&quot; alt=&quot;image.png&quot;/&gt;&lt;/p&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;margin-top: 8px; margin-bottom: 0px; white-space: normal; background-color: rgb(255, 255, 255); font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; font-size: 0.938rem;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;可以发现，00:59和01:01之间仅仅间隔了2秒，但接口却被访问了1000+999=1999次，是限流次数（1000次）的2倍！&lt;/span&gt;&lt;/p&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;margin-top: 8px; margin-bottom: 0px; white-space: normal; background-color: rgb(255, 255, 255); font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; font-size: 0.938rem;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;所以在该方案中，限流次数的设置可能不起作用，仍然可能在短时间内造成大量访问。&lt;/span&gt;&lt;/p&gt;&lt;h2 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.5rem; white-space: normal; background-color: rgb(255, 255, 255); font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; color: rgb(14, 136, 235);&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;1.2 滑动窗口（新思路）&lt;/span&gt;&lt;/h2&gt;&lt;h3 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.25rem; white-space: normal; background-color: rgb(255, 255, 255); font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;1.2.1 思路描述&lt;/span&gt;&lt;/h3&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;margin-top: 8px; margin-bottom: 0px; white-space: normal; background-color: rgb(255, 255, 255); font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; font-size: 0.938rem;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;为了避免出现方案1中由于键过期导致的短期访问量增大的情况，我们可以改变一下思路，也就是把固定的时间段改成动态的：&lt;/span&gt;&lt;/p&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;margin-top: 8px; margin-bottom: 0px; white-space: normal; background-color: rgb(255, 255, 255); font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; font-size: 0.938rem;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;假设某个接口在10秒内只允许访问5次。用户每次访问接口时，记录当前用户访问的时间点（时间戳），并计算前10秒内用户访问该接口的总次数。如果总次数大于限流次数，则不允许用户访问该接口。这样就能保证在任意时刻用户的访问次数不会超过1000次。&lt;/span&gt;&lt;/p&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;margin-top: 8px; margin-bottom: 0px; white-space: normal; background-color: rgb(255, 255, 255); font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; font-size: 0.938rem;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;如下图，假设用户在0:19时间点访问接口，经检查其前10秒内访问次数为5次，则允许本次访问。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://blog.zender.top/zb_users/upload/2023/202307281690535330485598.png&quot; alt=&quot;image.png&quot;/&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-family: 微软雅黑; font-size: 18.672px; background-color: #FFFFFF;&quot;&gt;假设用户0:20时间点访问接口，经检查其前10秒内访问次数为6次（超出限流次数5次），则不允许本次访问。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://blog.zender.top/zb_users/upload/2023/202307281690535345434891.png&quot; alt=&quot;image.png&quot;/&gt;&lt;/p&gt;&lt;h2 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.5rem; font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;1.2.2 Redis部分的实现&lt;/span&gt;&lt;/h2&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;margin-top: 8px; margin-bottom: 0px; font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; font-size: 15.008px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;&lt;strong style=&quot;color: rgb(14, 136, 235);&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;1）选用何种 Redis 数据结构&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;margin-top: 8px; margin-bottom: 0px; font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; font-size: 15.008px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;首先是需要确定使用哪个Redis数据结构。用户每次访问时，需要用一个key记录用户访问的时间点，而且还需要利用这些时间点进行范围检查。&lt;/span&gt;&lt;/p&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;margin-top: 8px; margin-bottom: 0px; font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; font-size: 15.008px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;&lt;strong style=&quot;color: rgb(14, 136, 235);&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;2）为何选择 zSet 数据结构&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;margin-top: 8px; margin-bottom: 0px; font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; font-size: 15.008px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;为了能够实现范围检查，可以考虑使用Redis中的zSet有序集合。&lt;/span&gt;&lt;/p&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;margin-top: 8px; margin-bottom: 0px; font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; font-size: 15.008px; white-space: normal; background-color: rgb(255, 255, 255);&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;添加一个zSet元素的命令如下：&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;brush:bash;toolbar:false&quot;&gt;ZADD&amp;nbsp;[key]&amp;nbsp;[score]&amp;nbsp;[member]&lt;/pre&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;margin-top: 8px; margin-bottom: 0px; white-space: normal; background-color: rgb(255, 255, 255); font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; font-size: 0.938rem;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;它有一个关键的属性score，通过它可以记录当前member的优先级。&lt;/span&gt;&lt;/p&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;margin-top: 8px; margin-bottom: 0px; white-space: normal; background-color: rgb(255, 255, 255); font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; font-size: 0.938rem;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;于是我们可以把score设置成用户访问接口的时间戳，以便于通过score进行范围检查。key则记录用户IP和接口方法名，至于member设置成什么没有影响，一个member记录了用户访问接口的时间点。因此member也可以设置成时间戳。&lt;/span&gt;&lt;/p&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;margin-top: 8px; margin-bottom: 0px; white-space: normal; background-color: rgb(255, 255, 255); font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; font-size: 0.938rem;&quot;&gt;&lt;strong style=&quot;color: rgb(14, 136, 235);&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;3）zSet 如何进行范围检查（检查前几秒的访问次数）&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;margin-top: 8px; margin-bottom: 0px; white-space: normal; background-color: rgb(255, 255, 255); font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; font-size: 0.938rem;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;思路是，把特定时间间隔之前的member都删掉，留下的member就是时间间隔之内的总访问次数。然后统计当前key中的member有多少个即可。&lt;/span&gt;&lt;/p&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;margin-top: 8px; margin-bottom: 0px; white-space: normal; background-color: rgb(255, 255, 255); font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; font-size: 0.938rem;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;① 把特定时间间隔之前的member都删掉。&lt;/span&gt;&lt;/p&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;margin-top: 8px; margin-bottom: 0px; white-space: normal; background-color: rgb(255, 255, 255); font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; font-size: 0.938rem;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;zSet有如下命令，用于删除score范围在&lt;/span&gt;&lt;code style=&quot;margin: 8px 0px; font-size: 0.875rem; color: rgb(30, 107, 184); background-color: rgba(27, 31, 35, 0.05); font-family: &amp;quot;Operator Mono&amp;quot;, Consolas, Monaco, Menlo, monospace;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;[min~max]&lt;/span&gt;&lt;/code&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;之间的member：&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;brush:bash;toolbar:false&quot;&gt;Zremrangebyscore&amp;nbsp;[key]&amp;nbsp;[min]&amp;nbsp;[max]&lt;/pre&gt;&lt;p&gt;&lt;span style=&quot;font-family: 微软雅黑; font-size: 18.672px; background-color: #FFFFFF;&quot;&gt;假设限流时间设置为5秒，当前用户访问接口时，获取当前系统时间戳为&lt;/span&gt;&lt;code style=&quot;margin: 8px 0px; white-space: normal; font-size: 0.875rem; color: rgb(30, 107, 184); background-color: rgba(27, 31, 35, 0.05); font-family: &amp;quot;Operator Mono&amp;quot;, Consolas, Monaco, Menlo, monospace;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;currentTimeMill&lt;/span&gt;&lt;/code&gt;&lt;span style=&quot;font-family: 微软雅黑; font-size: 18.672px; background-color: #FFFFFF;&quot;&gt;，那么删除的score范围可以设置为：&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;brush:bash;toolbar:false&quot;&gt;min&amp;nbsp;=&amp;nbsp;0
max&amp;nbsp;=&amp;nbsp;currentTimeMill&amp;nbsp;-&amp;nbsp;5&amp;nbsp;*&amp;nbsp;1000&lt;/pre&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;margin-top: 8px; margin-bottom: 0px; white-space: normal; background-color: rgb(255, 255, 255); font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; font-size: 0.938rem;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;相当于把5秒之前的所有member都删除了，只留下前5秒内的key。&lt;/span&gt;&lt;/p&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;margin-top: 8px; margin-bottom: 0px; white-space: normal; background-color: rgb(255, 255, 255); font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; font-size: 0.938rem;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;② 统计特定key中已存在的member有多少个。&lt;/span&gt;&lt;/p&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;margin-top: 8px; margin-bottom: 0px; white-space: normal; background-color: rgb(255, 255, 255); font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; font-size: 0.938rem;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;zSet有如下命令，用于统计某个key的member总数：&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;brush:bash;toolbar:false&quot;&gt;&amp;nbsp;ZCARD&amp;nbsp;[key]&lt;/pre&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;margin-top: 8px; margin-bottom: 0px; white-space: normal; background-color: rgb(255, 255, 255); font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; font-size: 0.938rem;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;margin-top: 8px; margin-bottom: 0px; font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; font-size: 0.938rem;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;统计的key的member总数，就是当前接口已经访问的次数。如果该数目大于限流次数，则说明当前的访问应被限流。&lt;/span&gt;&lt;/p&gt;&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; color: #0E88EB;&quot;&gt;二、代码实现&lt;/span&gt;&lt;/h1&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;margin-top: 8px; margin-bottom: 0px; font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; font-size: 0.938rem;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;主要是使用注解 + AOP的形式实现。&lt;/span&gt;&lt;/p&gt;&lt;h2 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.5rem; white-space: normal; background-color: rgb(255, 255, 255); font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;2.1.1 限流注解&lt;/span&gt;&lt;/h2&gt;&lt;pre class=&quot;brush:java;toolbar:false&quot;&gt;Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public&amp;nbsp;@interface&amp;nbsp;FixedTimeRateLimiter&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/**
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;定义redis中限流key前缀&amp;nbsp;例如：rate_limit:com.xxx.controller.HelloController-hello&amp;nbsp;//HelloController中的hello方法
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*/
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String&amp;nbsp;key()&amp;nbsp;default&amp;nbsp;&amp;quot;ft_rate_limit:&amp;quot;;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/**
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;限流时间，单位秒
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;@return
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*/
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int&amp;nbsp;time()&amp;nbsp;default&amp;nbsp;60;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/**
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;限流时间内限流次数
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;@return
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*/
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int&amp;nbsp;count()&amp;nbsp;default&amp;nbsp;100;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/**
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;限流类型：1.限制接口访问次数&amp;nbsp;2.限制ip访问次数
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;@return
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*/
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;LimitType&amp;nbsp;limitType()&amp;nbsp;default&amp;nbsp;LimitType.DEFAULT;
}&lt;/pre&gt;&lt;h2 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.5rem; white-space: normal; background-color: rgb(255, 255, 255); font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;2.1.2 定义lua脚本&lt;/span&gt;&lt;/h2&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;margin-top: 8px; margin-bottom: 0px; white-space: normal; background-color: rgb(255, 255, 255); font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; font-size: 0.938rem;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;在&lt;code style=&quot;margin: 8px 0px; font-size: 0.875rem; color: rgb(30, 107, 184); background-color: rgba(27, 31, 35, 0.05); font-family: &amp;quot;Operator Mono&amp;quot;, Consolas, Monaco, Menlo, monospace;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;resources/lua&lt;/span&gt;&lt;/code&gt;下新建&lt;code style=&quot;margin: 8px 0px; font-size: 0.875rem; color: rgb(30, 107, 184); background-color: rgba(27, 31, 35, 0.05); font-family: &amp;quot;Operator Mono&amp;quot;, Consolas, Monaco, Menlo, monospace;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;limit.lua&lt;/span&gt;&lt;/code&gt;：&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;brush:bash;toolbar:false&quot;&gt;-&amp;nbsp;获取redis键
local&amp;nbsp;key&amp;nbsp;=&amp;nbsp;KEYS[1]
--&amp;nbsp;获取第一个参数（次数）
local&amp;nbsp;count&amp;nbsp;=&amp;nbsp;tonumber(ARGV[1])
--&amp;nbsp;获取第二个参数（时间）
local&amp;nbsp;time&amp;nbsp;=&amp;nbsp;tonumber(ARGV[2])
--&amp;nbsp;获取当前流量
local&amp;nbsp;current&amp;nbsp;=&amp;nbsp;redis.call(&amp;#39;get&amp;#39;,&amp;nbsp;key);
--&amp;nbsp;如果current值存在，且值大于规定的次数，则拒绝放行（直接返回当前流量）
if&amp;nbsp;current&amp;nbsp;and&amp;nbsp;tonumber(current)&amp;nbsp;&amp;gt;&amp;nbsp;count&amp;nbsp;then
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;tonumber(current)
end
--&amp;nbsp;如果值小于规定次数，或值不存在，则允许放行，当前流量数+1&amp;nbsp;&amp;nbsp;(值不存在情况下，可以自增变为1)
current&amp;nbsp;=&amp;nbsp;redis.call(&amp;#39;incr&amp;#39;,&amp;nbsp;key);
--&amp;nbsp;如果是第一次进来，那么开始设置键的过期时间。
if&amp;nbsp;tonumber(current)&amp;nbsp;==&amp;nbsp;1&amp;nbsp;then&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;redis.call(&amp;#39;expire&amp;#39;,&amp;nbsp;key,&amp;nbsp;time);
end
--&amp;nbsp;返回当前流量
return&amp;nbsp;tonumber(current)&lt;/pre&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;margin-top: 8px; margin-bottom: 0px; white-space: normal; background-color: rgb(255, 255, 255); font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; font-size: 0.938rem;&quot;&gt;&lt;span style=&quot;font-family: 微软雅黑; font-size: 1.5rem; font-weight: bold;&quot;&gt;2.1.3 注入Lua执行脚本&lt;/span&gt;&lt;br/&gt;&lt;/p&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;margin-top: 8px; margin-bottom: 0px; white-space: normal; background-color: rgb(255, 255, 255); font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; font-size: 0.938rem;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;关键代码是&lt;/span&gt;&lt;code style=&quot;margin: 8px 0px; font-size: 0.875rem; color: rgb(30, 107, 184); background-color: rgba(27, 31, 35, 0.05); font-family: &amp;quot;Operator Mono&amp;quot;, Consolas, Monaco, Menlo, monospace;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;limitScript()&lt;/span&gt;&lt;/code&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑; font-size: 1.167rem;&quot;&gt;方法&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;brush:java;toolbar:false&quot;&gt;@Configuration
public&amp;nbsp;class&amp;nbsp;RedisScriptConfig&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/**
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;定义lua脚本
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*/
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@Bean
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;DefaultRedisScript&amp;lt;Long&amp;gt;&amp;nbsp;limitScript(){
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;DefaultRedisScript&amp;lt;Long&amp;gt;&amp;nbsp;script&amp;nbsp;=&amp;nbsp;new&amp;nbsp;DefaultRedisScript&amp;lt;&amp;gt;();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;script.setResultType(Long.class);//设置lua脚本返回值类型&amp;nbsp;需要同lua脚本中返回值一致
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;script.setScriptSource(new&amp;nbsp;ResourceScriptSource(new&amp;nbsp;ClassPathResource(&amp;quot;lua\\rateLimit.lua&amp;quot;)));//读取lua文件
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;script;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
}&lt;/pre&gt;&lt;p&gt;&lt;span style=&quot;font-family: 微软雅黑; font-size: 18.672px; background-color: #FFFFFF;&quot;&gt;Redis相关配置：&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;brush:java;toolbar:false&quot;&gt;/**
*&amp;nbsp;开启缓存支持
*/
@Slf4j
@EnableCaching
@Configuration
public&amp;nbsp;class&amp;nbsp;RedisConfig{
	/**
	&amp;nbsp;*&amp;nbsp;RedisTemplate配置
	&amp;nbsp;*&amp;nbsp;@param&amp;nbsp;lettuceConnectionFactory
	&amp;nbsp;*&amp;nbsp;@return
	&amp;nbsp;*/
	@Bean
	public&amp;nbsp;RedisTemplate&amp;lt;String,&amp;nbsp;Object&amp;gt;&amp;nbsp;redisTemplate(LettuceConnectionFactory&amp;nbsp;lettuceConnectionFactory)&amp;nbsp;{
		log.info(&amp;quot;&amp;nbsp;---&amp;nbsp;redis&amp;nbsp;config&amp;nbsp;init&amp;nbsp;---&amp;nbsp;&amp;quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Jackson2JsonRedisSerializer&amp;lt;Object&amp;gt;&amp;nbsp;jackson2JsonRedisSerializer&amp;nbsp;=&amp;nbsp;jacksonSerializer();
		RedisTemplate&amp;lt;String,&amp;nbsp;Object&amp;gt;&amp;nbsp;redisTemplate&amp;nbsp;=&amp;nbsp;new&amp;nbsp;RedisTemplate&amp;lt;String,&amp;nbsp;Object&amp;gt;();
		redisTemplate.setConnectionFactory(lettuceConnectionFactory);
		RedisSerializer&amp;lt;String&amp;gt;&amp;nbsp;stringSerializer&amp;nbsp;=&amp;nbsp;new&amp;nbsp;StringRedisSerializer();

		//&amp;nbsp;key序列化
		redisTemplate.setKeySerializer(stringSerializer);
		//&amp;nbsp;value序列化
		redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
		//&amp;nbsp;Hash&amp;nbsp;key序列化
		redisTemplate.setHashKeySerializer(stringSerializer);
		//&amp;nbsp;Hash&amp;nbsp;value序列化
		redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
		redisTemplate.afterPropertiesSet();
		return&amp;nbsp;redisTemplate;
	}

	/**
	&amp;nbsp;*&amp;nbsp;StringRedisTemplate配置
	&amp;nbsp;*&amp;nbsp;@param&amp;nbsp;lettuceConnectionFactory
	&amp;nbsp;*&amp;nbsp;@return
	&amp;nbsp;*/
	@Bean
	public&amp;nbsp;StringRedisTemplate&amp;nbsp;setStringredisTemplate(LettuceConnectionFactory&amp;nbsp;lettuceConnectionFactory)&amp;nbsp;{
		log.info(&amp;quot;&amp;nbsp;---&amp;nbsp;StringRedisTemplate&amp;nbsp;config&amp;nbsp;init&amp;nbsp;---&amp;nbsp;&amp;quot;);
		Jackson2JsonRedisSerializer&amp;lt;Object&amp;gt;&amp;nbsp;jackson2JsonRedisSerializer&amp;nbsp;=&amp;nbsp;jacksonSerializer();
		StringRedisTemplate&amp;nbsp;stringRedisTemplate&amp;nbsp;=&amp;nbsp;new&amp;nbsp;StringRedisTemplate();
		stringRedisTemplate.setConnectionFactory(lettuceConnectionFactory);
		RedisSerializer&amp;lt;String&amp;gt;&amp;nbsp;stringSerializer&amp;nbsp;=&amp;nbsp;new&amp;nbsp;StringRedisSerializer();
		//&amp;nbsp;key序列化
		stringRedisTemplate.setKeySerializer(stringSerializer);
		//&amp;nbsp;value序列化
		stringRedisTemplate.setValueSerializer(stringSerializer);
		//&amp;nbsp;Hash&amp;nbsp;key序列化
		stringRedisTemplate.setHashKeySerializer(stringSerializer);
		//&amp;nbsp;Hash&amp;nbsp;value序列化
		stringRedisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
		stringRedisTemplate.afterPropertiesSet();
		return&amp;nbsp;stringRedisTemplate;
	}

	private&amp;nbsp;Jackson2JsonRedisSerializer&amp;lt;Object&amp;gt;&amp;nbsp;jacksonSerializer()&amp;nbsp;{
		Jackson2JsonRedisSerializer&amp;lt;Object&amp;gt;&amp;nbsp;jackson2JsonRedisSerializer&amp;nbsp;=&amp;nbsp;new&amp;nbsp;Jackson2JsonRedisSerializer&amp;lt;&amp;gt;(Object.class);
		ObjectMapper&amp;nbsp;objectMapper&amp;nbsp;=&amp;nbsp;new&amp;nbsp;ObjectMapper();
		objectMapper.setVisibility(PropertyAccessor.ALL,&amp;nbsp;JsonAutoDetect.Visibility.ANY);
		//objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
		objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
				ObjectMapper.DefaultTyping.NON_FINAL,
				JsonTypeInfo.As.WRAPPER_ARRAY);

		jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
		return&amp;nbsp;jackson2JsonRedisSerializer;
	}
}&lt;/pre&gt;&lt;h2 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.5rem; white-space: normal; background-color: rgb(255, 255, 255); font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;2.1.3 定义Aop切面类&lt;/span&gt;&lt;/h2&gt;&lt;pre class=&quot;brush:java;toolbar:false&quot;&gt;/**
&amp;nbsp;*&amp;nbsp;固定时间段内限流
&amp;nbsp;*/
@Slf4j
@Aspect
@Component
public&amp;nbsp;class&amp;nbsp;FixedTimeRateLimitAspect&amp;nbsp;{

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@Autowired
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RedisTemplate&amp;lt;String,&amp;nbsp;Object&amp;gt;&amp;nbsp;redisTemplate;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@Autowired
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RedisScript&amp;lt;Long&amp;gt;&amp;nbsp;redisScript;&amp;nbsp;//实现类为DefaultRedisScript&amp;lt;Long&amp;gt;&amp;nbsp;limitScript()

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@Pointcut(&amp;quot;@annotation(com.zender.limiting.annotation.FixedTimeRateLimiter)&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;pointCut()&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@Before(&amp;quot;pointCut()&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;beforeRateLimit(JoinPoint&amp;nbsp;jp)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;获取RateLimiter注解上的值
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MethodSignature&amp;nbsp;methodSignature&amp;nbsp;=&amp;nbsp;(MethodSignature)&amp;nbsp;jp.getSignature();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;FixedTimeRateLimiter&amp;nbsp;rateLimiter&amp;nbsp;=&amp;nbsp;AnnotationUtils.findAnnotation(methodSignature.getMethod(),&amp;nbsp;FixedTimeRateLimiter.class);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int&amp;nbsp;time&amp;nbsp;=&amp;nbsp;rateLimiter.time();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int&amp;nbsp;count&amp;nbsp;=&amp;nbsp;rateLimiter.count();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;构建redis中的key值
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String&amp;nbsp;rateKey&amp;nbsp;=&amp;nbsp;getRateLimitKey(rateLimiter,&amp;nbsp;methodSignature);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;log.info(&amp;quot;redis中key值：&amp;quot;&amp;nbsp;+&amp;nbsp;rateKey);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Long&amp;nbsp;current&amp;nbsp;=&amp;nbsp;redisTemplate.execute(redisScript,&amp;nbsp;Collections.singletonList(rateKey),&amp;nbsp;time,&amp;nbsp;count);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(current&amp;nbsp;==&amp;nbsp;null&amp;nbsp;||&amp;nbsp;current.intValue()&amp;nbsp;&amp;gt;&amp;nbsp;count)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;log.error(&amp;quot;固定时间段限流：限制请求数&amp;#39;{}&amp;#39;,当前请求数&amp;#39;{}&amp;#39;,缓存key&amp;#39;{}&amp;#39;&amp;quot;,&amp;nbsp;count,&amp;nbsp;current,&amp;nbsp;rateKey);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;throw&amp;nbsp;new&amp;nbsp;BizException(BusinessCodes.InterFaceFlow.IFF0001);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&amp;nbsp;catch&amp;nbsp;(Exception&amp;nbsp;e)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;throw&amp;nbsp;e;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}


&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/**
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;redis中key两种类型格式为：
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;1.&amp;nbsp;&amp;nbsp;ft_rate_limit:com.xxx.controller.HelloController-hello
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;2.&amp;nbsp;&amp;nbsp;ft_rate_limit:127.0.0.1-com.xxx.controller.HelloController-hello
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;@return
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*/
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;String&amp;nbsp;getRateLimitKey(FixedTimeRateLimiter&amp;nbsp;rateLimiter,&amp;nbsp;MethodSignature&amp;nbsp;methodSignature)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;StringBuilder&amp;nbsp;key&amp;nbsp;=&amp;nbsp;new&amp;nbsp;StringBuilder(rateLimiter.key());
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(rateLimiter.limitType()&amp;nbsp;==&amp;nbsp;LimitType.IP)&amp;nbsp;{//如果参数类型为IP
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;获取客户端ip
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String&amp;nbsp;clientIP&amp;nbsp;=&amp;nbsp;ServletUtil.getClientIP(((ServletRequestAttributes)&amp;nbsp;RequestContextHolder.getRequestAttributes()).getRequest());
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;key.append(clientIP).append(&amp;quot;-&amp;quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Method&amp;nbsp;method&amp;nbsp;=&amp;nbsp;methodSignature.getMethod();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;获取全类名
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String&amp;nbsp;className&amp;nbsp;=&amp;nbsp;method.getDeclaringClass().getName();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;获取方法名
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String&amp;nbsp;methodName&amp;nbsp;=&amp;nbsp;method.getName();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;key.append(className).append(&amp;quot;-&amp;quot;).append(methodName);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;log.info(&amp;quot;全类名+方法名&amp;nbsp;&amp;quot;&amp;nbsp;+&amp;nbsp;className&amp;nbsp;+&amp;nbsp;&amp;quot;-&amp;quot;&amp;nbsp;+&amp;nbsp;methodName);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;key.toString();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
}&lt;/pre&gt;&lt;h2 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.5rem; white-space: normal; background-color: rgb(255, 255, 255); font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; color: rgb(14, 136, 235);&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;2.2 滑动窗口思路&lt;/span&gt;&lt;/h2&gt;&lt;h3 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.25rem; white-space: normal; background-color: rgb(255, 255, 255); font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;2.2.1 限流注解&lt;/span&gt;&lt;/h3&gt;&lt;pre class=&quot;brush:java;toolbar:false&quot;&gt;@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public&amp;nbsp;@interface&amp;nbsp;RateLimiter&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/**
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;定义redis中限流key前缀&amp;nbsp;例如：rate_limit:com.xxx.controller.HelloController-hello&amp;nbsp;//HelloController中的hello方法
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*/
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String&amp;nbsp;key()&amp;nbsp;default&amp;nbsp;&amp;quot;rate_limit:&amp;quot;;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/**
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;限流时间，单位秒
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;@return
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*/
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int&amp;nbsp;time()&amp;nbsp;default&amp;nbsp;60;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/**
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;限流时间内限流次数
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;@return
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*/
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int&amp;nbsp;count()&amp;nbsp;default&amp;nbsp;100;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/**
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;限流类型：1.限制接口访问次数&amp;nbsp;2.限制ip访问次数
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;@return
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*/
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;LimitType&amp;nbsp;limitType()&amp;nbsp;default&amp;nbsp;LimitType.DEFAULT;
}&lt;/pre&gt;&lt;h2 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.5rem; white-space: normal; background-color: rgb(255, 255, 255); font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;2.2.2 定义Aop切面类&lt;/span&gt;&lt;/h2&gt;&lt;pre class=&quot;brush:java;toolbar:false&quot;&gt;/**
&amp;nbsp;*&amp;nbsp;滑动时间段内限流
&amp;nbsp;*/
@Slf4j
@Aspect
@Component
public&amp;nbsp;class&amp;nbsp;RateLimiterAspect&amp;nbsp;{

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@Autowired
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;RedisTemplate&amp;lt;String,Object&amp;gt;&amp;nbsp;redisTemplate;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@Pointcut(&amp;quot;@annotation(com.zender.limiting.annotation.RateLimiter)&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;pointCut()&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}


&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/**
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;实现限流（新思路）
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;@param&amp;nbsp;jp
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*/
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@SuppressWarnings(&amp;quot;unchecked&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@Before(&amp;quot;pointCut()&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;void&amp;nbsp;doBefore(JoinPoint&amp;nbsp;jp)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;获取RateLimiter注解上的值
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MethodSignature&amp;nbsp;methodSignature&amp;nbsp;=&amp;nbsp;(MethodSignature)&amp;nbsp;jp.getSignature();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RateLimiter&amp;nbsp;rateLimiter&amp;nbsp;=&amp;nbsp;AnnotationUtils.findAnnotation(methodSignature.getMethod(),&amp;nbsp;RateLimiter.class);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;在&amp;nbsp;{time}&amp;nbsp;秒内仅允许访问&amp;nbsp;{count}&amp;nbsp;次。
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int&amp;nbsp;time&amp;nbsp;=&amp;nbsp;rateLimiter.time();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int&amp;nbsp;count&amp;nbsp;=&amp;nbsp;rateLimiter.count();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;根据用户IP（可选）和接口方法，构造key
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String&amp;nbsp;rateKey&amp;nbsp;=&amp;nbsp;getCombineKey(rateLimiter,&amp;nbsp;jp);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;限流逻辑实现
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ZSetOperations&amp;nbsp;zSetOperations&amp;nbsp;=&amp;nbsp;redisTemplate.opsForZSet();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;记录本次访问的时间结点
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;long&amp;nbsp;currentMs&amp;nbsp;=&amp;nbsp;System.currentTimeMillis();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;zSetOperations.add(rateKey,&amp;nbsp;currentMs,&amp;nbsp;currentMs);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;这一步是为了防止member一直存在于内存中
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;redisTemplate.expire(rateKey,&amp;nbsp;time,&amp;nbsp;TimeUnit.SECONDS);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;移除{time}秒之前的访问记录（滑动窗口思想）
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;zSetOperations.removeRangeByScore(rateKey,&amp;nbsp;0,&amp;nbsp;currentMs&amp;nbsp;-&amp;nbsp;time&amp;nbsp;*&amp;nbsp;1000L);

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;获得当前窗口内的访问记录数
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Long&amp;nbsp;current&amp;nbsp;=&amp;nbsp;zSetOperations.zCard(rateKey);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;限流判断
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(current&amp;nbsp;!=&amp;nbsp;null&amp;nbsp;&amp;amp;&amp;amp;&amp;nbsp;current&amp;nbsp;&amp;gt;&amp;nbsp;count)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;log.error(&amp;quot;滑动窗口限流：限制请求数&amp;#39;{}&amp;#39;,当前请求数&amp;#39;{}&amp;#39;,缓存key&amp;#39;{}&amp;#39;&amp;quot;,&amp;nbsp;count,&amp;nbsp;current,&amp;nbsp;rateKey);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;throw&amp;nbsp;new&amp;nbsp;BizException(BusinessCodes.InterFaceFlow.IFF0001);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;/**
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;把用户IP和接口方法名拼接成&amp;nbsp;redis&amp;nbsp;的&amp;nbsp;key
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;@param&amp;nbsp;point&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;切入点
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*&amp;nbsp;@return&amp;nbsp;组合key
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;*/
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;private&amp;nbsp;String&amp;nbsp;getCombineKey(RateLimiter&amp;nbsp;rateLimiter,&amp;nbsp;JoinPoint&amp;nbsp;point)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;StringBuilder&amp;nbsp;key&amp;nbsp;=&amp;nbsp;new&amp;nbsp;StringBuilder(rateLimiter.key());
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;(rateLimiter.limitType()&amp;nbsp;==&amp;nbsp;LimitType.IP)&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;如果参数类型为IP
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;获取客户端ip
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String&amp;nbsp;clientIP&amp;nbsp;=&amp;nbsp;ServletUtil.getClientIP(((ServletRequestAttributes)&amp;nbsp;RequestContextHolder.getRequestAttributes()).getRequest());
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;key.append(clientIP).append(&amp;quot;-&amp;quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MethodSignature&amp;nbsp;signature&amp;nbsp;=&amp;nbsp;(MethodSignature)&amp;nbsp;point.getSignature();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Method&amp;nbsp;method&amp;nbsp;=&amp;nbsp;signature.getMethod();

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;获取全类名
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String&amp;nbsp;className&amp;nbsp;=&amp;nbsp;method.getDeclaringClass().getName();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;获取方法名
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String&amp;nbsp;methodName&amp;nbsp;=&amp;nbsp;method.getName();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;key.append(&amp;quot;-&amp;quot;).append(className).append(&amp;quot;-&amp;quot;).append(methodName);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//&amp;nbsp;log.info(&amp;quot;全类名&amp;nbsp;+&amp;nbsp;方法名&amp;nbsp;&amp;quot;&amp;nbsp;+&amp;nbsp;className&amp;nbsp;+&amp;nbsp;&amp;quot;-&amp;quot;&amp;nbsp;+&amp;nbsp;methodName);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;key.toString();
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
}&lt;/pre&gt;&lt;p&gt;&lt;span style=&quot;font-family: 微软雅黑; font-size: 18.672px; background-color: #FFFFFF;&quot;&gt;分别测试即可：&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;brush:java;toolbar:false&quot;&gt;@RestController
public&amp;nbsp;class&amp;nbsp;TestController&amp;nbsp;{
	@GetMapping(&amp;quot;/hello&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@RateLimiter(time&amp;nbsp;=&amp;nbsp;10,count&amp;nbsp;=&amp;nbsp;3,limitType&amp;nbsp;=&amp;nbsp;LimitType.DEFAULT)&amp;nbsp;//10秒内允许访问三次
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;String&amp;nbsp;hello(){
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;&amp;quot;hello&amp;quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@GetMapping(&amp;quot;/hello2&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@FixedTimeRateLimiter(time&amp;nbsp;=&amp;nbsp;10,count&amp;nbsp;=&amp;nbsp;3,limitType&amp;nbsp;=&amp;nbsp;LimitType.DEFAULT)&amp;nbsp;//10秒内允许访问三次
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public&amp;nbsp;String&amp;nbsp;hello2(){
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;&amp;quot;hello2&amp;quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
}&lt;/pre&gt;&lt;p&gt;&lt;img src=&quot;https://blog.zender.top/zb_users/upload/2023/202307281690535552209539.png&quot; alt=&quot;image.png&quot;/&gt;&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://blog.zender.top/zb_users/upload/2023/202307281690535574195985.png&quot; alt=&quot;image.png&quot;/&gt;&lt;span style=&quot;font-family: 微软雅黑; font-size: 18.672px; background-color: #FFFFFF;&quot;&gt;&lt;/span&gt;&lt;br/&gt;&lt;/p&gt;</description><pubDate>Fri, 28 Jul 2023 17:07:38 +0800</pubDate></item><item><title>MySQL大数据表处理方案总结</title><link>https://blog.zender.top/post/MySQL_BigDataSummary.html</link><description>&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;一、场景&lt;/h1&gt;&lt;p&gt;当我们业务数据库表中的数据越来越多，如果你也和我遇到了以下类似场景，那让我们一起来解决这个问题&lt;/p&gt;&lt;ul style=&quot;margin-top: 8px; padding-left: 2rem;&quot; class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;数据的插入，查询时长较长。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;后续业务需求的扩展在表中新增字段影响较大。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;表中的数据并不是所有的都为有效数据 需求只查询时间区间内的。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.25rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;评估表数据体量&lt;/h3&gt;&lt;p&gt;我们可以从表容量/磁盘空间/实例容量三方面评估数据体量，接下来让我们分别展开来看看。&lt;/p&gt;&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;二、表容量&lt;/h1&gt;&lt;p&gt;表容量主要从表的记录数、平均长度、增长量、读写量、总大小量进行评估。一般对于OLTP的表，建议单表不要超过2000W行数据量，总大小15G以内。访问量：单表读写量在1600/s以内&lt;/p&gt;&lt;p&gt;查询行数据的方式：&lt;/p&gt;&lt;p&gt;我们一般查询表数据有多少数据时用到的经典sql语句如下：&lt;/p&gt;&lt;pre class=&quot;brush:sql;toolbar:false&quot;&gt;select&amp;nbsp;count(*)&amp;nbsp;from&amp;nbsp;table;
select&amp;nbsp;count(1)&amp;nbsp;from&amp;nbsp;table;&lt;/pre&gt;&lt;p&gt;但是当数据量过大的时候，这样的查询就可能会超时，所以我们要换一种查询方式&lt;/p&gt;&lt;pre class=&quot;brush:sql;toolbar:false&quot;&gt;use&amp;nbsp;库名
show&amp;nbsp;table&amp;nbsp;status&amp;nbsp;like&amp;nbsp;&amp;#39;表名&amp;#39;&amp;nbsp;;&amp;nbsp;或&amp;nbsp;show&amp;nbsp;table&amp;nbsp;status&amp;nbsp;like&amp;nbsp;&amp;#39;表名&amp;#39;\G&amp;nbsp;;&lt;/pre&gt;&lt;p&gt;&lt;span style=&quot;font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; font-size: 16px; background-color: #FFFFFF;&quot;&gt;上述方法不仅可以查询表的数据，还可以输出表的详细信息 , 加 \G 可以格式化输出。包括表名 存储引擎 版本 行数 每行的字节数等等。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; font-size: 16px; background-color: #FFFFFF;&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;三、磁盘空间&lt;/h1&gt;&lt;p&gt;查看指定数据库容量大小&lt;/p&gt;&lt;pre class=&quot;brush:sql;toolbar:false&quot;&gt;select
table_schema&amp;nbsp;as&amp;nbsp;&amp;#39;数据库&amp;#39;,
table_name&amp;nbsp;as&amp;nbsp;&amp;#39;表名&amp;#39;,
table_rows&amp;nbsp;as&amp;nbsp;&amp;#39;记录数&amp;#39;,
truncate(data_length/1024/1024,&amp;nbsp;2)&amp;nbsp;as&amp;nbsp;&amp;#39;数据容量(MB)&amp;#39;,
truncate(index_length/1024/1024,&amp;nbsp;2)&amp;nbsp;as&amp;nbsp;&amp;#39;索引容量(MB)&amp;#39;
from&amp;nbsp;information_schema.tables
order&amp;nbsp;by&amp;nbsp;data_length&amp;nbsp;desc,&amp;nbsp;index_length&amp;nbsp;desc;&lt;/pre&gt;&lt;p&gt;&lt;span style=&quot;font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; font-size: 16px; background-color: #FFFFFF;&quot;&gt;查询单个库中所有表磁盘占用大小&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;brush:sql;toolbar:false&quot;&gt;select
table_schema&amp;nbsp;as&amp;nbsp;&amp;#39;数据库&amp;#39;,
table_name&amp;nbsp;as&amp;nbsp;&amp;#39;表名&amp;#39;,
table_rows&amp;nbsp;as&amp;nbsp;&amp;#39;记录数&amp;#39;,
truncate(data_length/1024/1024,&amp;nbsp;2)&amp;nbsp;as&amp;nbsp;&amp;#39;数据容量(MB)&amp;#39;,
truncate(index_length/1024/1024,&amp;nbsp;2)&amp;nbsp;as&amp;nbsp;&amp;#39;索引容量(MB)&amp;#39;
from&amp;nbsp;information_schema.tables
where&amp;nbsp;table_schema=&amp;#39;mysql&amp;#39;
order&amp;nbsp;by&amp;nbsp;data_length&amp;nbsp;desc,&amp;nbsp;index_length&amp;nbsp;desc;&lt;/pre&gt;&lt;p&gt;&lt;span style=&quot;font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; font-size: 16px; background-color: #FFFFFF;&quot;&gt;&lt;/span&gt;&lt;img src=&quot;https://blog.zender.top/zb_users/upload/2023/202304191681874098418400.png&quot; alt=&quot;image.png&quot;/&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; font-size: 16px; background-color: #FFFFFF;&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;建议数据量占磁盘使用率的70%以内。同时，对于一些数据增长较快，可以考虑使用大的慢盘进行数据归档。&lt;/p&gt;&lt;h3 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.25rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;实例容量&lt;/h3&gt;&lt;p&gt;MySQL是基于线程的服务模型，因此在一些并发较高的场景下，单实例并不能充分利用服务器的CPU资源，吞吐量反而会卡在MySQL层，可以根据业务考虑自己的实例模式。&lt;/p&gt;&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;四、出现问题原因&lt;/h1&gt;&lt;p&gt;上面我们已经查到我们数据表的体量了 那么为什么单表数据量越大 业务的执行效率就越慢 根本原因是什么呢？&lt;/p&gt;&lt;p&gt;一个表的数据量达到好几千万或者上亿时，加索引的效果没那么明显啦。性能之所以会变差，是因为维护索引的B+树结构层级变得更高了，查询一条数据时，需要经历的磁盘IO变多，因此查询性能变慢。&lt;/p&gt;&lt;h4 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.17rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;大家是否还记得，一个B+树大概可以存放多少数据量呢？&lt;/h4&gt;&lt;p&gt;InnoDB存储引擎最小储存单元是页，一页大小就是16k。&lt;/p&gt;&lt;p&gt;B+树叶子存的是数据，内部节点存的是键值+指针。索引组织表通过非叶子节点的二分查找法以及指针确定数据在哪个页中，进而再去数据页中找到需要的数据；&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://blog.zender.top/zb_users/upload/2023/202304191681874108327630.png&quot; alt=&quot;image.png&quot;/&gt;&lt;span style=&quot;font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; font-size: 16px; background-color: #FFFFFF;&quot;&gt;&lt;br/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;假设B+树的高度为2的话，即有一个根结点和若干个叶子结点。这棵B+树的存放总记录数为=&lt;strong&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;color: #FF0000;&quot;&gt;根结点指针数*单个叶子节点记录行数&lt;/span&gt;&lt;/strong&gt;。&lt;/p&gt;&lt;ul style=&quot;margin-top: 8px; padding-left: 2rem;&quot; class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;如果一行记录的数据大小为1k，那么单个叶子节点可以存的记录数 =16k/1k =16。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;非叶子节点内存放多少指针呢？我们假设主键ID为bigint类型，长度为8字节(面试官问你int类型，一个int就是32位，4字节)，而指针大小在InnoDB源码中设置为6字节，所以就是8+6=14字节，16k/14B =16*1024B/14B = 1170。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;因此，一棵高度为2的B+树，能存放1170 * 16=18720条这样的数据记录。同理一棵高度为3的B+树，能存放1170 *1170 *16 =21902400，也就是说，可以存放两千万左右的记录。B+树高度一般为1-3层，已经满足千万级别的数据存储。&lt;/p&gt;&lt;p&gt;如果B+树想存储更多的数据，那树结构层级就会更高，查询一条数据时，需要&lt;strong&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;color: #FF0000;&quot;&gt;经历的磁盘IO变多，因此查询性能变慢&lt;/span&gt;&lt;/strong&gt;。&lt;/p&gt;&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;五、如何解决单表数据量太大，查询变慢的问题&lt;/h1&gt;&lt;p&gt;知道了根本原因之后，我们就需要考虑如何优化数据库来解决问题了。&lt;/p&gt;&lt;p&gt;这里提供了三种解决方案，包括数据表分区，分库分表，冷热数据归档 了解完这些方案之后大家可以选取适合自己业务的方案。&lt;/p&gt;&lt;h2 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.5rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;方案一：数据表分区&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;color: #FF0000;&quot;&gt;表分区可以在区间内查询对应的数据，降低查询范围 并且索引分区 也可以进一步提高命中率，提升查询效率&lt;/span&gt;&lt;/strong&gt;。&lt;br/&gt;&lt;/p&gt;&lt;p&gt;分区是指将一个表的数据按照条件分布到不同的文件上面，未分区前都是存放在一个文件上面的，但是它还是指向的同一张表，只是把数据分散到了不同文件而已。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;我们首先看一下分区有什么优缺点&lt;/strong&gt;&lt;/p&gt;&lt;ol class=&quot;wiz-list-level1 list-paddingleft-2&quot; style=&quot;margin-top: 8px; padding-left: 2rem;&quot;&gt;&lt;li&gt;&lt;p&gt;与单个磁盘或文件系统分区相比，可以存储更多的数据。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;对于那些已经失去保存意义的数据，通常可以通过删除与那些数据有关的分区，很容易地删除那些数据。相反地，在某些情况下，添加新数据的过程又可以通过为那些新数据专门增加一个新的分区，来很方便地实现。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;一些查询可以得到极大的优化，这主要是借助于满足一个给定WHERE语句的数据可以只保存在一个或多个分区内，这样在查找时就不用查找其他剩余的分区。因为分区可以在创建了分区表后进行修改，所以在第一次配置分区方案时还不曾这么做时，可以重新组织数据，来提高那些常用查询的效率。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;涉及到例如SUM()和COUNT()这样聚合函数的查询，可以很容易地进行并行处理。这种查询的一个简单例子如 “SELECT salesperson_id, COUNT (orders) as order_total FROM sales GROUP BY salesperson_id；”。通过“并行”，这意味着该查询可以在每个分区上同时进行，最终结果只需通过总计所有分区得到的结果。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;通过跨多个磁盘来分散数据查询，来获得更大的查询吞吐量。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;表分区的限制因素&lt;/strong&gt;&lt;/p&gt;&lt;ol class=&quot;wiz-list-level1 list-paddingleft-2&quot; style=&quot;margin-top: 8px; padding-left: 2rem;&quot;&gt;&lt;li&gt;&lt;p&gt;一个表最多只能有1024个分区。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;MySQL5.1中，分区表达式必须是整数，或者返回整数的表达式。在MySQL5.5中提供了非整数表达式分区的支持。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;如果分区字段中有主键或者唯一索引的列，那么多有主键列和唯一索引列都必须包含进来。即：分区字段要么不包含主键或者索引列，要么包含全部主键和索引列。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;分区表中无法使用外键约束。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;MySQL的分区适用于一个表的所有数据和索引，不能只对表数据分区而不对索引分区，也不能只对索引分区而不对表分区，也不能只对表的一部分数据分区。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;在进行分区之前可以用如下方法 看下数据库表是否支持分区&lt;/p&gt;&lt;pre class=&quot;brush:sql;toolbar:false&quot;&gt;mysql&amp;gt;&amp;nbsp;show&amp;nbsp;variables&amp;nbsp;like&amp;nbsp;&amp;#39;%partition%&amp;#39;;
+-------------------+-------+
|&amp;nbsp;Variable_name&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;|&amp;nbsp;Value&amp;nbsp;|
+-------------------+-------+
|&amp;nbsp;have_partitioning&amp;nbsp;|&amp;nbsp;YES&amp;nbsp;&amp;nbsp;&amp;nbsp;|
+-------------------+-------+
1&amp;nbsp;row&amp;nbsp;in&amp;nbsp;set&amp;nbsp;(0.00&amp;nbsp;sec)&lt;/pre&gt;&lt;p&gt;&lt;span style=&quot;font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; font-size: 16px; background-color: #FFFFFF;&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;h2 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.5rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;方案二：数据库分表&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;color: #FF0000;&quot;&gt;分表后，显而易见，单表数据量降低，树的高度变低，查询经历的磁盘io变少，则可以提高效率&lt;/span&gt;&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;MySQL分表分为两种：水平分表和垂直分表&lt;/p&gt;&lt;p&gt;分库分表就是为了解决由于数据量过大而导致数据库性能降低的问题，将原来独立的数据库拆分成若干数据库组成 ，将数据大表拆分成若干数据表组成，使得单一数据库、单一数据表的数据量变小，从而达到提升数据库性能的目的。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;水平分表&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;定义：数据表行的拆分，通俗点就是把数据按照某些规则拆分成多张表或者多个库来存放。分为库内分表和分库。 比如一个表有4000万数据，查询很慢，可以分到四个表，每个表有1000万数据。&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; font-size: 16px; background-color: #FFFFFF;&quot;&gt;&lt;img src=&quot;https://blog.zender.top/zb_users/upload/2023/202304191681874129537089.png&quot; alt=&quot;image.png&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; font-size: 16px; background-color: #FFFFFF;&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;垂直分表&lt;br/&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;定义：列的拆分，根据表之间的相关性进行拆分。常见的就是一个表把不常用的字段和常用的字段就行拆分，然后利用主键关联。或者一个数据库里面有订单表和用户表，数据量都很大，进行垂直拆分，用户库存用户表的数据，订单库存订单表的数据。&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://blog.zender.top/zb_users/upload/2023/202304191681874138572882.png&quot; alt=&quot;image.png&quot;/&gt;&lt;span style=&quot;font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; font-size: 16px; background-color: #FFFFFF;&quot;&gt;&lt;br/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;缺点：垂直分隔的缺点比较明显，数据不在一张表中，会增加join 或 union之类的操作&lt;/p&gt;&lt;h3 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.25rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;分表方案&lt;/h3&gt;&lt;p&gt;知道了两个知识后，我们来看一下分库分表的方案。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;1、取模方案&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;拆分之前，先预估一下数据量。比如用户表有4000w数据，现在要把这些数据分到4个表user1 user2&amp;nbsp; uesr3 user4。 比如id = 17，17对4取模为1，加上 ，所以这条数据存到user2表。&lt;/p&gt;&lt;p&gt;注意：进行水平拆分后的表要去掉auto_increment自增长。这时候的id可以用一个id 自增长临时表获得，或者使用&amp;nbsp; redis incr的方法。&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; font-size: 16px; background-color: #FFFFFF;&quot;&gt;&lt;img src=&quot;https://blog.zender.top/zb_users/upload/2023/202304191681874149305434.png&quot; alt=&quot;image.png&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; font-size: 16px; background-color: #FFFFFF;&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;优点： 数据均匀的分到各个表中，出现热点问题的概率很低。&lt;/p&gt;&lt;p&gt;缺点：以后的数据扩容迁移比较困难难，当数据量变大之后，以前分到4个表现在要分到8个表，取模的值就变了，需要重新进行数据迁移。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;2、range 范围方案&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;以范围进行拆分数据，就是在某个范围内的订单，存放到某个表中。比如id=12存放到user1表，id=1300万的存放到user2表。&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://blog.zender.top/zb_users/upload/2023/202304191681874158296678.png&quot; alt=&quot;image.png&quot;/&gt;&lt;span style=&quot;font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; font-size: 16px; background-color: #FFFFFF;&quot;&gt;&lt;br/&gt;&lt;/span&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; font-size: 16px; background-color: #FFFFFF;&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;优点：有利于将来对数据的扩容&lt;/p&gt;&lt;p&gt;缺点：如果热点数据都存在一个表中，则压力都在一个表中，其他表没有压力。&lt;/p&gt;&lt;p&gt;我们看到以上两种方案 都存在缺点 但是却又是互补的，那么我们将这两个方案结合会怎样呢？&lt;/p&gt;&lt;p&gt;&lt;strong&gt;3、hash取模和range方案结合&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;如下图 我们可以看到 group 组存放id 为0~4000万的数据，然后有三个数据库 DB0 DB1 DB2，DB0里面有四个数据库，DB1 和DB2 有三个数据库&lt;/p&gt;&lt;p&gt;假如id为15000 然后对10取模（为啥对10 取模 因为有10个表），取0 然后 落在DB_0,然后在根据range 范围，落在Table_0 里面。&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;https://blog.zender.top/zb_users/upload/2023/202304191681874168400460.png&quot; alt=&quot;image.png&quot;/&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;background-color: #FFFFFF;&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;总结：采用hash取模和range方案结合既可以避免热点数据的问题，也有利于将来对数据的扩容。&lt;/p&gt;&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;六、分区分表的区别&lt;/h1&gt;&lt;p&gt;&lt;strong&gt;1、实现方式上&lt;/strong&gt;&lt;/p&gt;&lt;ul style=&quot;margin-top: 8px; padding-left: 2rem;&quot; class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;mysql的分表是真正的分表，一张表分成很多表后，每一个小表都是完整的一张表，都对应三个文件，一个.MYD数据文件，.MYI索引文件，.frm表结构。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;分区不一样，一张大表进行分区后，他还是一张表，不会变成二张表，但是他存放数据的区块变多了。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;2、提高性能上&lt;/strong&gt;&lt;/p&gt;&lt;ul style=&quot;margin-top: 8px; padding-left: 2rem;&quot; class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;分表重点是存取数据时，如何提高mysql并发能力上。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;而分区呢，如何突破磁盘的读写能力，从而达到提高mysql性能的目的。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;3、实现的难易度上&lt;/strong&gt;&lt;/p&gt;&lt;ul style=&quot;margin-top: 8px; padding-left: 2rem;&quot; class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;分表的方法有很多，用merge来分表，是最简单的一种方式。这种方式根分区难易度差不多，并且对程序代码来说可以做到透明的。如果是用其他分表方式就比分区麻烦了。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;分区实现是比较简单的，建立分区表，根建平常的表没什么区别，并且对开代码端来说是透明的。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.25rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;分区分表的联系&lt;/h3&gt;&lt;ol class=&quot;wiz-list-level1 list-paddingleft-2&quot; style=&quot;margin-top: 8px; padding-left: 2rem;&quot;&gt;&lt;li&gt;&lt;p&gt;都能提高mysql的性高，在高并发状态下都有一个良好的表现。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;分表和分区不矛盾，可以相互配合的，对于那些大访问量，并且表数据比较多的表，我们可以采取分表和分区结合的方式，访问量不大，但是表数据很多的表，我们可以采取分区的方式等。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h3 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.25rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;分库分表存在的问题&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;1、事务问题&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;在执行分库分表之后，由于数据存储到了不同的库上，数据库事务管理出现了困难。如果依赖数据库本身的分布式事务管理功能去执行事务，将付出高昂的性能代价；如果由应用程序去协助控制，形成程序逻辑上的事务，又会造成编程方面的负担。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;2、跨库跨表的join问题&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;在执行了分库分表之后，难以避免会将原本逻辑关联性很强的数据划分到不同的表、不同的库上，这时，表的关联操作将受到限制，我们无法join位于不同分库的表，也无法join分表粒度不同的表，结果原本一次查询能够完成的业务，可能需要多次查询才能完成。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;3、额外的数据管理负担和数据运算压力&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;额外的数据管理负担，最显而易见的就是数据的定位问题和数据的增删改查的重复执行问题，这些都可以通过应用程序解决，但必然引起额外的逻辑运算，例如，对于一个记录用户成绩的用户数据表userTable，业务要求查出成绩最好的100位，在进行分表之前，只需一个order by语句就可以搞定，但是在进行分表之后，将需要n个order by语句，分别查出每一个分表的前100名用户数据，然后再对这些数据进行合并计算，才能得出结果。&lt;/p&gt;&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;七、数据的冷热归档&lt;/h1&gt;&lt;p&gt;为什么要冷热归档：其实原因和方案二类似，都是降低单表数据量，树的高度变低，查询经历的磁盘io变少，则可以提高效率&lt;/p&gt;&lt;p&gt;如果大家的业务数据，有明显的冷热区分，比如：只需要展示近一周或一个月的数据。那么这种情况这一周喝一个月的数据我们称之为热数据，其余数据为冷数据。那么我们可以将冷数据归档在其他的库表中，提高我们热数据的操作效率。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;接下来讲一下归档的过程&lt;/strong&gt;&lt;/p&gt;&lt;ol class=&quot;wiz-list-level1 list-paddingleft-2&quot; style=&quot;margin-top: 8px; padding-left: 2rem;&quot;&gt;&lt;li&gt;&lt;p&gt;创建归档表 创建的归档表 原则上要与原表保持一致。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;归档表数据的初始化。&lt;/p&gt;&lt;/li&gt;&lt;ol class=&quot; list-paddingleft-2&quot; style=&quot;list-style-type: lower-alpha;&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;img src=&quot;https://blog.zender.top/zb_users/upload/2023/202304191681874182799150.png&quot; alt=&quot;image.png&quot; style=&quot;white-space: normal;&quot;/&gt;&lt;br/&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;li&gt;&lt;p&gt;业务增量数据处理过程。&lt;/p&gt;&lt;/li&gt;&lt;ol class=&quot; list-paddingleft-2&quot; style=&quot;list-style-type: lower-alpha;&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;img src=&quot;https://blog.zender.top/zb_users/upload/2023/202304191681874190303342.png&quot; alt=&quot;image.png&quot; style=&quot;white-space: normal;&quot;/&gt;&lt;br/&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;li&gt;&lt;p&gt;数据的获取过程。&lt;/p&gt;&lt;/li&gt;&lt;ol class=&quot; list-paddingleft-2&quot; style=&quot;list-style-type: lower-alpha;&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;img src=&quot;https://blog.zender.top/zb_users/upload/2023/202304191681874197281803.png&quot; alt=&quot;image.png&quot; style=&quot;font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; font-size: 16px; white-space: normal;&quot;/&gt;&lt;br/&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/ol&gt;&lt;p&gt;&lt;span style=&quot;font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; font-size: 16px; background-color: #FFFFFF;&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;八、以上三种方案我们如何选型&lt;/h1&gt;&lt;table width=&quot;1545&quot;&gt;&lt;thead&gt;&lt;tr class=&quot;firstRow&quot;&gt;&lt;th style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none; width: 125px;&quot;&gt;方案&lt;/th&gt;&lt;th style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none; width: 259px;&quot;&gt;试用场景&lt;/th&gt;&lt;th style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none; width: 398px;&quot;&gt;优点&lt;/th&gt;&lt;th style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none; width: 762px;&quot;&gt;缺点&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot; width=&quot;125&quot;&gt;数据表分区&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot; width=&quot;259&quot;&gt;1、数据量较大&lt;br/&gt;2、查询场景只在某个区&amp;nbsp;&lt;br/&gt;3、没有联合查询的场景&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border: 1px solid rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot; width=&quot;398&quot;&gt;分区分表是在物理上对数据表所对应的文件进行拆分，对应的表名是不变的，所以不会影响到之前业务逻辑的sql&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border: 1px solid rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot; width=&quot;762&quot;&gt;分表后的查询等业务会创建对应的对象，也会造成一定的开销分区数据若要聚合的话 耗费时间也较长；使用范围不适合数据量千万级以上的&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border: 1px solid rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot; width=&quot;125&quot;&gt;数据表分表&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border: 1px solid rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot; width=&quot;259&quot;&gt;1、数据量较大&lt;br/&gt;2、无法区分明显冷热区且数据可以完整按照区间划分&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border: 1px solid rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot; width=&quot;398&quot;&gt;适用于对冷热分区的界限不是很明显的数据，对后续类似的数据可以采用该方式，将大表拆分成小表 提高查询插入等效率&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border: 1px solid rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot; width=&quot;762&quot;&gt;若大数据表逐渐增多 那么对应的数据库表越来越多 每个表都需要分表；区间的划分较为固定 若后续单表的数据量大起来 也会对性能造成影响；实现复杂度相对方案三比较复杂&amp;nbsp; 需要测试整个实现过程 在编码层处理 对原有业务有影响；&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border: 1px solid rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot; width=&quot;125&quot;&gt;冷热归档分库&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border: 1px solid rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot; width=&quot;259&quot;&gt;1、数据量较大&lt;br/&gt;2、数据冷热分区明显&lt;br/&gt;3、冷数据使用频率极低&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border: 1px solid rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot; width=&quot;398&quot;&gt;数据迁移的过程对业务的影响较小 开发量也较少减少成本&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border: 1px solid rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot; width=&quot;762&quot;&gt;需要确认分表规则&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;p&gt;URL:https://juejin.cn/post/7146016771936354312&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; font-size: 16px; background-color: #FFFFFF;&quot;&gt;&lt;br/&gt;&lt;/span&gt;&lt;br/&gt;&lt;/p&gt;</description><pubDate>Tue, 18 Apr 2023 11:12:36 +0800</pubDate></item><item><title>MySQL单表一千万的数据，如何快速查询</title><link>https://blog.zender.top/post/MySQL_select_01.html</link><description>&lt;p&gt;&lt;span style=&quot;font-family: 微软雅黑; font-size: 16px; background-color: #FFFFFF;&quot;&gt;先准备数据&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;brush:sql;toolbar:false&quot;&gt;CREATE&amp;nbsp;TABLE&amp;nbsp;`user_operation_log`&amp;nbsp;&amp;nbsp;(
&amp;nbsp;&amp;nbsp;`id`&amp;nbsp;int(11)&amp;nbsp;NOT&amp;nbsp;NULL&amp;nbsp;AUTO_INCREMENT,
&amp;nbsp;&amp;nbsp;`user_id`&amp;nbsp;varchar(64)&amp;nbsp;DEFAULT&amp;nbsp;NULL,
&amp;nbsp;&amp;nbsp;`ip`&amp;nbsp;varchar(20)&amp;nbsp;DEFAULT&amp;nbsp;NULL,
&amp;nbsp;&amp;nbsp;`op_data`&amp;nbsp;varchar(255)&amp;nbsp;DEFAULT&amp;nbsp;NULL,
&amp;nbsp;&amp;nbsp;`attr1`&amp;nbsp;varchar(255)&amp;nbsp;DEFAULT&amp;nbsp;NULL,
&amp;nbsp;&amp;nbsp;`attr2`&amp;nbsp;varchar(255)&amp;nbsp;DEFAULT&amp;nbsp;NULL,
&amp;nbsp;&amp;nbsp;`attr3`&amp;nbsp;varchar(255)&amp;nbsp;DEFAULT&amp;nbsp;NULL,
&amp;nbsp;&amp;nbsp;`attr4`&amp;nbsp;varchar(255)&amp;nbsp;DEFAULT&amp;nbsp;NULL,
&amp;nbsp;&amp;nbsp;`attr5`&amp;nbsp;varchar(255)&amp;nbsp;DEFAULT&amp;nbsp;NULL,
&amp;nbsp;&amp;nbsp;`attr6`&amp;nbsp;varchar(255)&amp;nbsp;DEFAULT&amp;nbsp;NULL,
&amp;nbsp;&amp;nbsp;`attr7`&amp;nbsp;varchar(255)&amp;nbsp;DEFAULT&amp;nbsp;NULL,
&amp;nbsp;&amp;nbsp;`attr8`&amp;nbsp;varchar(255)&amp;nbsp;DEFAULT&amp;nbsp;NULL,
&amp;nbsp;&amp;nbsp;`attr9`&amp;nbsp;varchar(255)&amp;nbsp;DEFAULT&amp;nbsp;NULL,
&amp;nbsp;&amp;nbsp;`attr10`&amp;nbsp;varchar(255)&amp;nbsp;DEFAULT&amp;nbsp;NULL,
&amp;nbsp;&amp;nbsp;`attr11`&amp;nbsp;varchar(255)&amp;nbsp;DEFAULT&amp;nbsp;NULL,
&amp;nbsp;&amp;nbsp;`attr12`&amp;nbsp;varchar(255)&amp;nbsp;DEFAULT&amp;nbsp;NULL,
&amp;nbsp;&amp;nbsp;PRIMARY&amp;nbsp;KEY&amp;nbsp;(`id`)&amp;nbsp;USING&amp;nbsp;BTREE
);&lt;/pre&gt;&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;创建数据脚本&lt;/span&gt;&lt;/h1&gt;&lt;p&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;采用批量插入，效率会快很多，而且每1000条数就commit，数据量太大，也会导致批量插入效率慢。&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;brush:sql;toolbar:false&quot;&gt;DELIMITER&amp;nbsp;;;
CREATE&amp;nbsp;PROCEDURE&amp;nbsp;batch_insert_log()
BEGIN
&amp;nbsp;&amp;nbsp;DECLARE&amp;nbsp;i&amp;nbsp;INT&amp;nbsp;DEFAULT&amp;nbsp;1;
&amp;nbsp;&amp;nbsp;DECLARE&amp;nbsp;userId&amp;nbsp;INT&amp;nbsp;DEFAULT&amp;nbsp;10000000;
&amp;nbsp;set&amp;nbsp;@execSql&amp;nbsp;=&amp;nbsp;&amp;#39;INSERT&amp;nbsp;INTO&amp;nbsp;`test`.`user_operation_log`(`user_id`,&amp;nbsp;`ip`,&amp;nbsp;`op_data`,&amp;nbsp;`attr1`,&amp;nbsp;`attr2`,&amp;nbsp;`attr3`,&amp;nbsp;`attr4`,&amp;nbsp;`attr5`,&amp;nbsp;`attr6`,&amp;nbsp;`attr7`,&amp;nbsp;`attr8`,&amp;nbsp;`attr9`,&amp;nbsp;`attr10`,&amp;nbsp;`attr11`,&amp;nbsp;`attr12`)&amp;nbsp;VALUES&amp;#39;;
&amp;nbsp;set&amp;nbsp;@execData&amp;nbsp;=&amp;nbsp;&amp;#39;&amp;#39;;
&amp;nbsp;&amp;nbsp;WHILE&amp;nbsp;i&amp;lt;=10000000&amp;nbsp;DO
&amp;nbsp;&amp;nbsp;&amp;nbsp;set&amp;nbsp;@attr&amp;nbsp;=&amp;nbsp;&amp;quot;&amp;#39;这个地址有点长这个地址有点长这个地址有点长这个地址有点长这个地址有点长&amp;#39;&amp;quot;;
&amp;nbsp;&amp;nbsp;set&amp;nbsp;@execData&amp;nbsp;=&amp;nbsp;concat(@execData,&amp;nbsp;&amp;quot;(&amp;quot;,&amp;nbsp;userId&amp;nbsp;+&amp;nbsp;i,&amp;nbsp;&amp;quot;,&amp;nbsp;&amp;#39;10.0.69.175&amp;#39;,&amp;nbsp;&amp;#39;用户登录操作&amp;#39;&amp;quot;,&amp;nbsp;&amp;quot;,&amp;quot;,&amp;nbsp;@attr,&amp;nbsp;&amp;quot;,&amp;quot;,&amp;nbsp;@attr,&amp;nbsp;&amp;quot;,&amp;quot;,&amp;nbsp;@attr,&amp;nbsp;&amp;quot;,&amp;quot;,&amp;nbsp;@attr,&amp;nbsp;&amp;quot;,&amp;quot;,&amp;nbsp;@attr,&amp;nbsp;&amp;quot;,&amp;quot;,&amp;nbsp;@attr,&amp;nbsp;&amp;quot;,&amp;quot;,&amp;nbsp;@attr,&amp;nbsp;&amp;quot;,&amp;quot;,&amp;nbsp;@attr,&amp;nbsp;&amp;quot;,&amp;quot;,&amp;nbsp;@attr,&amp;nbsp;&amp;quot;,&amp;quot;,&amp;nbsp;@attr,&amp;nbsp;&amp;quot;,&amp;quot;,&amp;nbsp;@attr,&amp;nbsp;&amp;quot;,&amp;quot;,&amp;nbsp;@attr,&amp;nbsp;&amp;quot;)&amp;quot;);
&amp;nbsp;&amp;nbsp;if&amp;nbsp;i&amp;nbsp;%&amp;nbsp;1000&amp;nbsp;=&amp;nbsp;0
&amp;nbsp;&amp;nbsp;then
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;set&amp;nbsp;@stmtSql&amp;nbsp;=&amp;nbsp;concat(@execSql,&amp;nbsp;@execData,&amp;quot;;&amp;quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;prepare&amp;nbsp;stmt&amp;nbsp;from&amp;nbsp;@stmtSql;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;execute&amp;nbsp;stmt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;DEALLOCATE&amp;nbsp;prepare&amp;nbsp;stmt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;commit;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;set&amp;nbsp;@execData&amp;nbsp;=&amp;nbsp;&amp;quot;&amp;quot;;
&amp;nbsp;&amp;nbsp;&amp;nbsp;else
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;set&amp;nbsp;@execData&amp;nbsp;=&amp;nbsp;concat(@execData,&amp;nbsp;&amp;quot;,&amp;quot;);
&amp;nbsp;&amp;nbsp;&amp;nbsp;end&amp;nbsp;if;
&amp;nbsp;&amp;nbsp;SET&amp;nbsp;i=i+1;
&amp;nbsp;&amp;nbsp;END&amp;nbsp;WHILE;

END;;
DELIMITER&amp;nbsp;;&lt;/pre&gt;&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;测试&lt;/span&gt;&lt;/h1&gt;&lt;pre class=&quot;brush:sql;toolbar:false&quot;&gt;SELECT&amp;nbsp;count(1)&amp;nbsp;FROM&amp;nbsp;`user_operation_log`;

返回结果：3148000
三次查询时间分别为：
14060&amp;nbsp;ms
13755&amp;nbsp;ms
13447&amp;nbsp;ms&lt;/pre&gt;&lt;h2 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.5rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;普通分页查询&lt;/span&gt;&lt;/h2&gt;&lt;p&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;MySQL 支持 LIMIT 语句来选取指定的条数数据，MySQL分页查询语法如下：&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;brush:sql;toolbar:false&quot;&gt;SELECT&amp;nbsp;*&amp;nbsp;FROM&amp;nbsp;table&amp;nbsp;LIMIT&amp;nbsp;[offset,]&amp;nbsp;rows&amp;nbsp;|&amp;nbsp;rows&amp;nbsp;OFFSET&amp;nbsp;offset

第一个参数offset指定第一个返回记录行的偏移量
第二个参数rows指定返回记录行的最大数目&lt;/pre&gt;&lt;p&gt;&lt;span style=&quot;font-family: 微软雅黑; font-size: 16px; background-color: #FFFFFF;&quot;&gt;下面我们开始测试查询结果：&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;brush:sql;toolbar:false&quot;&gt;SELECT&amp;nbsp;*&amp;nbsp;FROM&amp;nbsp;`user_operation_log`&amp;nbsp;LIMIT&amp;nbsp;10000,&amp;nbsp;10;

查询3次时间分别为：
59&amp;nbsp;ms
49&amp;nbsp;ms
50&amp;nbsp;ms&lt;/pre&gt;&lt;h2 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.5rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;不同条件测试&lt;/span&gt;&lt;/h2&gt;&lt;h3 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.25rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;相同偏移量，不同数据量&lt;/span&gt;&lt;/h3&gt;&lt;pre class=&quot;brush:sql;toolbar:false&quot;&gt;SELECT&amp;nbsp;*&amp;nbsp;FROM&amp;nbsp;`user_operation_log`&amp;nbsp;LIMIT&amp;nbsp;10000,&amp;nbsp;10;
SELECT&amp;nbsp;*&amp;nbsp;FROM&amp;nbsp;`user_operation_log`&amp;nbsp;LIMIT&amp;nbsp;10000,&amp;nbsp;100;
SELECT&amp;nbsp;*&amp;nbsp;FROM&amp;nbsp;`user_operation_log`&amp;nbsp;LIMIT&amp;nbsp;10000,&amp;nbsp;1000;
SELECT&amp;nbsp;*&amp;nbsp;FROM&amp;nbsp;`user_operation_log`&amp;nbsp;LIMIT&amp;nbsp;10000,&amp;nbsp;10000;
SELECT&amp;nbsp;*&amp;nbsp;FROM&amp;nbsp;`user_operation_log`&amp;nbsp;LIMIT&amp;nbsp;10000,&amp;nbsp;100000;
SELECT&amp;nbsp;*&amp;nbsp;FROM&amp;nbsp;`user_operation_log`&amp;nbsp;LIMIT&amp;nbsp;10000,&amp;nbsp;1000000;&lt;/pre&gt;&lt;p&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;查询时间如下：&lt;/span&gt;&lt;/p&gt;&lt;table width=&quot;657&quot;&gt;&lt;thead&gt;&lt;tr class=&quot;firstRow&quot;&gt;&lt;th style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none; background: rgb(240, 240, 240);&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;数量&lt;/span&gt;&lt;/th&gt;&lt;th style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none; background: rgb(240, 240, 240);&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;第一次&lt;/span&gt;&lt;/th&gt;&lt;th style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none; background: rgb(240, 240, 240);&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;第二次&lt;/span&gt;&lt;/th&gt;&lt;th style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none; background: rgb(240, 240, 240);&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;第三次&lt;/span&gt;&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;10条&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;53ms&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;52ms&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;47ms&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;background-color: rgb(248, 248, 248);&quot;&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;100条&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;50ms&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;60ms&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;55ms&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;1000条&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;61ms&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;74ms&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;60ms&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;background-color: rgb(248, 248, 248);&quot;&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;10000条&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;164ms&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;180ms&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;217ms&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;100000条&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;1609ms&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;1741ms&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;1764ms&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;background-color: rgb(248, 248, 248);&quot;&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;1000000条&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;16219ms&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;16889ms&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;17081ms&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-size: 16px; color: #595959; font-family: 微软雅黑;&quot;&gt;从上面结果可以得出结束：&lt;/span&gt;&lt;strong style=&quot;font-size: 16px; white-space: normal; font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; color: rgb(71, 193, 168);&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;数据量越大，花费时间越长&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&lt;h3 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.25rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;相同数据量，不同偏移量&lt;/span&gt;&lt;/h3&gt;&lt;pre class=&quot;brush:sql;toolbar:false&quot;&gt;SELECT&amp;nbsp;*&amp;nbsp;FROM&amp;nbsp;`user_operation_log`&amp;nbsp;LIMIT&amp;nbsp;100,&amp;nbsp;100;
SELECT&amp;nbsp;*&amp;nbsp;FROM&amp;nbsp;`user_operation_log`&amp;nbsp;LIMIT&amp;nbsp;1000,&amp;nbsp;100;
SELECT&amp;nbsp;*&amp;nbsp;FROM&amp;nbsp;`user_operation_log`&amp;nbsp;LIMIT&amp;nbsp;10000,&amp;nbsp;100;
SELECT&amp;nbsp;*&amp;nbsp;FROM&amp;nbsp;`user_operation_log`&amp;nbsp;LIMIT&amp;nbsp;100000,&amp;nbsp;100;
SELECT&amp;nbsp;*&amp;nbsp;FROM&amp;nbsp;`user_operation_log`&amp;nbsp;LIMIT&amp;nbsp;1000000,&amp;nbsp;100;&lt;/pre&gt;&lt;table width=&quot;657&quot;&gt;&lt;thead&gt;&lt;tr class=&quot;firstRow&quot;&gt;&lt;th style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none; background: rgb(240, 240, 240);&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;偏移量&lt;/span&gt;&lt;/th&gt;&lt;th style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none; background: rgb(240, 240, 240);&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;第一次&lt;/span&gt;&lt;/th&gt;&lt;th style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none; background: rgb(240, 240, 240);&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;第二次&lt;/span&gt;&lt;/th&gt;&lt;th style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none; background: rgb(240, 240, 240);&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;第三次&lt;/span&gt;&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;100&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;36ms&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;40ms&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;36ms&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;background-color: rgb(248, 248, 248);&quot;&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;1000&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;31ms&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;38ms&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;32ms&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;10000&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;53ms&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;48ms&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;51ms&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;background-color: rgb(248, 248, 248);&quot;&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;100000&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;622ms&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;576ms&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;627ms&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;1000000&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;4891ms&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;5076ms&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;4856ms&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-size: 16px; color: #595959; font-family: 微软雅黑;&quot;&gt;从上面结果可以得出结束：&lt;/span&gt;&lt;strong style=&quot;font-size: 16px; white-space: normal; font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; color: rgb(71, 193, 168);&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;偏移量越大，花费时间越长&lt;/span&gt;&lt;/strong&gt;&lt;/p&gt;&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;如何优化&lt;/span&gt;&lt;/h1&gt;&lt;p&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;得出了结论，针对上面两个问题：偏移大、数据量大，我们分别优化。&lt;/span&gt;&lt;/p&gt;&lt;h2 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.5rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;优化偏移量大问题&lt;/span&gt;&lt;/h2&gt;&lt;h3 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.25rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;采用子查询方式&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;我们可以先定位偏移位置的 id，然后再查询数据&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;brush:sql;toolbar:false&quot;&gt;SELECT&amp;nbsp;*&amp;nbsp;FROM&amp;nbsp;`user_operation_log`&amp;nbsp;LIMIT&amp;nbsp;1000000,&amp;nbsp;10;
SELECT&amp;nbsp;id&amp;nbsp;FROM&amp;nbsp;`user_operation_log`&amp;nbsp;LIMIT&amp;nbsp;1000000,&amp;nbsp;1;
SELECT&amp;nbsp;*&amp;nbsp;FROM&amp;nbsp;`user_operation_log`&amp;nbsp;WHERE&amp;nbsp;id&amp;nbsp;&amp;gt;=&amp;nbsp;(SELECT&amp;nbsp;id&amp;nbsp;FROM&amp;nbsp;`user_operation_log`&amp;nbsp;LIMIT&amp;nbsp;1000000,&amp;nbsp;1)&amp;nbsp;LIMIT&amp;nbsp;10;&lt;/pre&gt;&lt;p&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;查询结果如下：&lt;/span&gt;&lt;/p&gt;&lt;table width=&quot;657&quot;&gt;&lt;thead&gt;&lt;tr class=&quot;firstRow&quot;&gt;&lt;th style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none; background: rgb(240, 240, 240);&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;sql&lt;/span&gt;&lt;/th&gt;&lt;th style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none; background: rgb(240, 240, 240);&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;花费时间&lt;/span&gt;&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;第一条&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;4818ms&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;background-color: rgb(248, 248, 248);&quot;&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;第二条(无索引情况下)&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;4329ms&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;第二条(有索引情况下)&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;199ms&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;background-color: rgb(248, 248, 248);&quot;&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;第三条(无索引情况下)&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;4319ms&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;第三条(有索引情况下)&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;201ms&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;从上面结果得出结论：&lt;/span&gt;&lt;/p&gt;&lt;ul style=&quot;margin-top: 8px; padding-left: 2rem;&quot; class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;第一条花费的时间最大，第三条比第一条稍微好点&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;子查询使用索引速度更快&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;缺点：只适用于id递增的情况。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;id非递增的情况可以使用以下写法，但这种缺点是分页查询只能放在子查询里面&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;注意：某些 mysql 版本不支持在 in 子句中使用 limit，所以采用了多个嵌套select。&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;brush:sql;toolbar:false&quot;&gt;SELECT&amp;nbsp;*&amp;nbsp;FROM&amp;nbsp;`user_operation_log`&amp;nbsp;WHERE&amp;nbsp;id&amp;nbsp;IN&amp;nbsp;(SELECT&amp;nbsp;t.id&amp;nbsp;FROM&amp;nbsp;(SELECT&amp;nbsp;id&amp;nbsp;FROM&amp;nbsp;`user_operation_log`&amp;nbsp;LIMIT&amp;nbsp;1000000,&amp;nbsp;10)&amp;nbsp;AS&amp;nbsp;t);&lt;/pre&gt;&lt;h3 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.25rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;采用 id 限定方式&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;这种方法要求更高些，id必须是连续递增，而且还得计算id的范围，然后使用 between，sql如下&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;brush:sql;toolbar:false&quot;&gt;SELECT&amp;nbsp;*&amp;nbsp;FROM&amp;nbsp;`user_operation_log`&amp;nbsp;WHERE&amp;nbsp;id&amp;nbsp;between&amp;nbsp;1000000&amp;nbsp;AND&amp;nbsp;1000100&amp;nbsp;LIMIT&amp;nbsp;100;
SELECT&amp;nbsp;*&amp;nbsp;FROM&amp;nbsp;`user_operation_log`&amp;nbsp;WHERE&amp;nbsp;id&amp;nbsp;&amp;gt;=&amp;nbsp;1000000&amp;nbsp;LIMIT&amp;nbsp;100;&lt;/pre&gt;&lt;p&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;查询结果如下：&lt;/span&gt;&lt;/p&gt;&lt;table width=&quot;657&quot;&gt;&lt;thead&gt;&lt;tr class=&quot;firstRow&quot;&gt;&lt;th style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none; background: rgb(240, 240, 240);&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;sql&lt;/span&gt;&lt;/th&gt;&lt;th style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none; background: rgb(240, 240, 240);&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;花费时间&lt;/span&gt;&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;第一条&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;22ms&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;background-color: rgb(248, 248, 248);&quot;&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;第二条&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;21ms&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;h4 data-tool=&quot;mdnice编辑器&quot; style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.125rem; white-space: normal; font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif;&quot;&gt;&lt;/h4&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;margin-top: 8px; margin-bottom: 0px; font-size: 16px; white-space: normal; font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; color: rgb(89, 89, 89);&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;从结果可以看出这种方式非常快&lt;/span&gt;&lt;/p&gt;&lt;p data-tool=&quot;mdnice编辑器&quot; style=&quot;margin-top: 8px; margin-bottom: 0px; font-size: 16px; white-space: normal; font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &amp;quot;PingFang SC&amp;quot;, Cambria, Cochin, Georgia, Times, &amp;quot;Times New Roman&amp;quot;, serif; color: rgb(89, 89, 89);&quot;&gt;&lt;em style=&quot;color: rgb(71, 193, 168);&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;注意：这里的 LIMIT 是限制了条数，没有采用偏移量&lt;/span&gt;&lt;/em&gt;&lt;/p&gt;&lt;h3 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.25rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;优化数据量大问题&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;返回结果的数据量也会直接影响速度&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;brush:sql;toolbar:false&quot;&gt;SELECT&amp;nbsp;*&amp;nbsp;FROM&amp;nbsp;`user_operation_log`&amp;nbsp;LIMIT&amp;nbsp;1,&amp;nbsp;1000000;
SELECT&amp;nbsp;id&amp;nbsp;FROM&amp;nbsp;`user_operation_log`&amp;nbsp;LIMIT&amp;nbsp;1,&amp;nbsp;1000000;
SELECT&amp;nbsp;
	id,&amp;nbsp;user_id,&amp;nbsp;ip,&amp;nbsp;op_data,&amp;nbsp;attr1,&amp;nbsp;attr2,&amp;nbsp;attr3,&amp;nbsp;attr4,&amp;nbsp;attr5,&amp;nbsp;attr6,&amp;nbsp;attr7,&amp;nbsp;attr8,&amp;nbsp;attr9,&amp;nbsp;attr10,&amp;nbsp;attr11,&amp;nbsp;attr12&amp;nbsp;
FROM&amp;nbsp;`user_operation_log`&amp;nbsp;LIMIT&amp;nbsp;1,&amp;nbsp;1000000;&lt;/pre&gt;&lt;p&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;查询结果如下：&lt;/span&gt;&lt;/p&gt;&lt;table width=&quot;657&quot;&gt;&lt;thead&gt;&lt;tr class=&quot;firstRow&quot;&gt;&lt;th style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none; background: rgb(240, 240, 240);&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;sql&lt;/span&gt;&lt;/th&gt;&lt;th style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none; background: rgb(240, 240, 240);&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;花费时间&lt;/span&gt;&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;第一条&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;15676ms&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;background-color: rgb(248, 248, 248);&quot;&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;第二条&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;7298ms&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;第三条&lt;/span&gt;&lt;/td&gt;&lt;td style=&quot;padding: 4px 8px; border-collapse: collapse; border-color: rgb(167, 175, 188); min-height: 28px; word-break: break-word; box-sizing: border-box; outline: none;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;15960ms&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;从结果可以看出减少不需要的列，查询效率也可以得到明显提升。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;第一条和第三条查询速度差不多，这时候你肯定会吐槽，那我还写那么多字段干啥呢，直接 * 不就完事了。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;注意本人的 MySQL 服务器和客户端是在_同一台机器上，所以查询数据相差不多，如果分开，会受到网络带宽影响 。&lt;/span&gt;&lt;/p&gt;&lt;h3 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.25rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;SELECT *问题&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;主要两点：&lt;/span&gt;&lt;/p&gt;&lt;ol class=&quot;wiz-list-level1 list-paddingleft-2&quot; style=&quot;margin-top: 8px; padding-left: 2rem;&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;用 &amp;quot;SELECT * &amp;quot; 数据库需要解析更多的对象、字段、权限、属性等相关内容，在 SQL 语句复杂，硬解析较多的情况下，会对数据库造成沉重的负担。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span data-wiz-span=&quot;data-wiz-span&quot; style=&quot;font-family: 微软雅黑;&quot;&gt;增大网络开销，* 有时会误带上如log、IconMD5之类的无用且大文本字段，数据传输大小会几何增涨。特别是MySQL和应用程序不在同一台机器，这种开销非常明显。&lt;/span&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;</description><pubDate>Mon, 17 Apr 2023 18:35:30 +0800</pubDate></item><item><title>int(1) 和 int(10) 的区别</title><link>https://blog.zender.top/post/MySQL_int_01.html</link><description>&lt;p&gt;&lt;span style=&quot;font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; font-size: 16px; background-color: #FFFFFF;&quot;&gt;我们知道在mysql中 int占4个字节，那么对于无符号的int，最大值是2^32-1 = 4294967295，将近40亿，难道用了int(1)，就不能达到这个最大值吗？&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;brush:sql;toolbar:false&quot;&gt;CREATE&amp;nbsp;TABLE&amp;nbsp;`user`&amp;nbsp;(
&amp;nbsp;&amp;nbsp;`id`&amp;nbsp;int(1)&amp;nbsp;unsigned&amp;nbsp;NOT&amp;nbsp;NULL&amp;nbsp;AUTO_INCREMENT,
&amp;nbsp;&amp;nbsp;&amp;nbsp;PRIMARY&amp;nbsp;KEY&amp;nbsp;(`id`)
)&amp;nbsp;ENGINE=InnoDB&amp;nbsp;AUTO_INCREMENT=1&amp;nbsp;DEFAULT&amp;nbsp;CHARSET=utf8mb4;&lt;/pre&gt;&lt;p&gt;&lt;span style=&quot;font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; font-size: 16px; background-color: #FFFFFF;&quot;&gt;id字段为无符号的int(1)，我来插入一个最大值看看。&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;brush:sql;toolbar:false&quot;&gt;mysql&amp;gt;&amp;nbsp;INSERT&amp;nbsp;INTO&amp;nbsp;`user`&amp;nbsp;(`id`)&amp;nbsp;VALUES&amp;nbsp;(4294967295);
Query&amp;nbsp;OK,&amp;nbsp;1&amp;nbsp;row&amp;nbsp;affected&amp;nbsp;(0.00&amp;nbsp;sec)&lt;/pre&gt;&lt;p&gt;&lt;span style=&quot;color: rgba(0, 0, 0, 0.9); font-family: arial, helvetica, sans-serif;&quot;&gt;可以看到成功了，说明int后面的数字，不影响int本身支持的大小，int(1)、int(2)...int(10)没什么区别。&lt;/span&gt;&lt;/p&gt;&lt;h1 style=&quot;margin: 1.25rem 0px 0.625rem; padding: 0px; font-size: 1.67rem; font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; white-space: normal;&quot;&gt;零填充&lt;/h1&gt;&lt;p&gt;一般int后面的数字，配合zerofill一起使用才有效。先看个例子：&lt;/p&gt;&lt;pre class=&quot;brush:sql;toolbar:false&quot;&gt;CREATE&amp;nbsp;TABLE&amp;nbsp;`user`&amp;nbsp;(
&amp;nbsp;&amp;nbsp;`id`&amp;nbsp;int(4)&amp;nbsp;unsigned&amp;nbsp;zerofill&amp;nbsp;NOT&amp;nbsp;NULL&amp;nbsp;AUTO_INCREMENT,
&amp;nbsp;&amp;nbsp;&amp;nbsp;PRIMARY&amp;nbsp;KEY&amp;nbsp;(`id`)
)&amp;nbsp;ENGINE=InnoDB&amp;nbsp;AUTO_INCREMENT=1&amp;nbsp;DEFAULT&amp;nbsp;CHARSET=utf8mb4;&lt;/pre&gt;&lt;p&gt;注意int(4)后面加了个zerofill，我们先来插入4条数据。&lt;/p&gt;&lt;p&gt;&lt;wiz_code_mirror&gt;&lt;/wiz_code_mirror&gt;&lt;/p&gt;&lt;pre class=&quot;brush:sql;toolbar:false&quot;&gt;mysql&amp;gt;&amp;nbsp;INSERT&amp;nbsp;INTO&amp;nbsp;`user`&amp;nbsp;(`id`)&amp;nbsp;VALUES&amp;nbsp;(1),(10),(100),(1000);
Query&amp;nbsp;OK,&amp;nbsp;4&amp;nbsp;rows&amp;nbsp;affected&amp;nbsp;(0.00&amp;nbsp;sec)
Records:&amp;nbsp;4&amp;nbsp;&amp;nbsp;Duplicates:&amp;nbsp;0&amp;nbsp;&amp;nbsp;Warnings:&amp;nbsp;0&lt;/pre&gt;&lt;p&gt;&lt;span style=&quot;font-family: Helvetica, &amp;quot;Hiragino Sans GB&amp;quot;, 微软雅黑, &amp;quot;Microsoft YaHei UI&amp;quot;, SimSun, SimHei, arial, sans-serif; font-size: 16px; background-color: #FFFFFF;&quot;&gt;分别插入1、10、100、1000 4条数据，然后我们来查询下：&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;brush:sql;toolbar:false&quot;&gt;mysql&amp;gt;&amp;nbsp;select&amp;nbsp;*&amp;nbsp;from&amp;nbsp;user;
+------+
|&amp;nbsp;id&amp;nbsp;&amp;nbsp;&amp;nbsp;|
+------+
|&amp;nbsp;0001&amp;nbsp;|
|&amp;nbsp;0010&amp;nbsp;|
|&amp;nbsp;0100&amp;nbsp;|
|&amp;nbsp;1000&amp;nbsp;|
+------+
4&amp;nbsp;rows&amp;nbsp;in&amp;nbsp;set&amp;nbsp;(0.00&amp;nbsp;sec)&lt;/pre&gt;&lt;p&gt;通过数据可以发现 int(4) + zerofill实现了不足4位补0的现象，单单int(4)是没有用的。而且对于0001这种，底层存储的还是1，只是在展示的会补0。&lt;/p&gt;&lt;p&gt;int后面的数字不能表示字段的长度，int(num)一般加上zerofill，才有效果。&lt;/p&gt;&lt;p&gt;zerofill的作用一般可以用在一些编号相关的数字中，比如学生的编号 001 002 ... 999这种，如果mysql没有零填充的功能，但是你又要格式化输出等长的数字编号时，那么你只能自己处理了。&lt;/p&gt;</description><pubDate>Mon, 10 Apr 2023 18:42:42 +0800</pubDate></item></channel></rss>