Jekyll2024-02-09T14:16:21+08:00https://beangogo.cn/feed.xml豆仔gogo有时候阳光很好,有时候阳光很暗,这就是生活。进击de豆子http协议常用对照表2023-08-13T00:00:00+08:002023-08-13T00:00:00+08:00https://beangogo.cn/2023/08/13/http%E5%8D%8F%E8%AE%AE%E5%B8%B8%E7%94%A8%E5%AF%B9%E7%85%A7%E8%A1%A8<h1 id="http请求方法对照表">HTTP请求方法对照表</h1>
<p>根据HTTP标准,HTTP请求可以使用多种请求方法。</p>
<p>HTTP1.0定义了三种请求方法: GET, POST 和 HEAD方法。</p>
<p>HTTP1.1新增了五种请求方法:OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法。</p>
<table>
<thead>
<tr>
<th>序号</th>
<th>方法</th>
<th> </th>
<th>描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>GET</td>
<td> </td>
<td>请求指定的页面信息,并返回实体主体。</td>
</tr>
<tr>
<td>2</td>
<td>HEAD</td>
<td> </td>
<td>类似于get请求,只不过返回的响应中没有具体的内容,用于获取报头</td>
</tr>
<tr>
<td>3</td>
<td>POST</td>
<td> </td>
<td>向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改。</td>
</tr>
<tr>
<td>4</td>
<td>PUT</td>
<td> </td>
<td>从客户端向服务器传送的数据取代指定的文档的内容。</td>
</tr>
<tr>
<td>5</td>
<td>DELETE</td>
<td> </td>
<td>请求服务器删除指定的页面。</td>
</tr>
<tr>
<td>6</td>
<td>CONNECT</td>
<td> </td>
<td>HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。</td>
</tr>
<tr>
<td>7</td>
<td>OPTIONS</td>
<td> </td>
<td>允许客户端查看服务器的性能。</td>
</tr>
<tr>
<td>8</td>
<td>TRACE</td>
<td> </td>
<td>回显服务器收到的请求,主要用于测试或诊断。</td>
</tr>
<tr>
<td>9</td>
<td>PATCH</td>
<td> </td>
<td>实体中包含一个表,表中说明与该URI所表示的原内容的区别。</td>
</tr>
<tr>
<td>10</td>
<td>MOVE</td>
<td> </td>
<td>请求服务器将指定的页面移至另一个网络地址。</td>
</tr>
<tr>
<td>11</td>
<td>COPY</td>
<td> </td>
<td>请求服务器将指定的页面拷贝至另一个网络地址。</td>
</tr>
<tr>
<td>12</td>
<td>LINK</td>
<td> </td>
<td>请求服务器建立链接关系。</td>
</tr>
<tr>
<td>13</td>
<td>UNLINK</td>
<td> </td>
<td>断开链接关系。</td>
</tr>
<tr>
<td>14</td>
<td>WRAPPED</td>
<td> </td>
<td>允许客户端发送经过封装的请求。</td>
</tr>
<tr>
<td>15</td>
<td>Extension-mothed</td>
<td> </td>
<td>在不改动协议的前提下,可增加另外的方法。</td>
</tr>
</tbody>
</table>
<h1 id="http响应头和请求头信息对照表">HTTP响应头和请求头信息对照表</h1>
<p>HTTP请求头提供了关于请求,响应或者其他的发送实体的信息。HTTP的头信息包括通用头、请求头、响应头和实体头四个部分。每个头域由一个域名,冒号(:)和域值三部分组成。</p>
<ul>
<li>通用头标:即可用于请求,也可用于响应,是作为一个整体而不是特定资源与事务相关联。</li>
<li>请求头标:允许客户端传递关于自身的信息和希望的响应形式。</li>
<li>响应头标:服务器和于传递自身信息的响应。</li>
<li>实体头标:定义被传送资源的信息。即可用于请求,也可用于响应。</li>
</ul>
<p>根据以上分类的HTTP请求头介绍可以<a href="http://www.jb51.net/article/51951.htm">参考此文</a>,本工具根据请求和输出分为Request和Response两部分。</p>
<h3 id="http-request-header">HTTP Request Header</h3>
<table>
<thead>
<tr>
<th>Header</th>
<th>解释</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td>Accept</td>
<td>指定客户端能够接收的内容类型</td>
<td>Accept: text/plain, text/html</td>
</tr>
<tr>
<td>Accept-Charset</td>
<td>浏览器可以接受的字符编码集。</td>
<td>Accept-Charset: iso-8859-5</td>
</tr>
<tr>
<td>Accept-Encoding</td>
<td>指定浏览器可以支持的web服务器返回内容压缩编码类型。</td>
<td>Accept-Encoding: compress, gzip</td>
</tr>
<tr>
<td>Accept-Language</td>
<td>浏览器可接受的语言</td>
<td>Accept-Language: en,zh</td>
</tr>
<tr>
<td>Accept-Ranges</td>
<td>可以请求网页实体的一个或者多个子范围字段</td>
<td>Accept-Ranges: bytes</td>
</tr>
<tr>
<td>Authorization</td>
<td>HTTP授权的授权证书</td>
<td>Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==</td>
</tr>
<tr>
<td>Cache-Control</td>
<td>指定请求和响应遵循的缓存机制</td>
<td>Cache-Control: no-cache</td>
</tr>
<tr>
<td>Connection</td>
<td>表示是否需要持久连接。(HTTP 1.1默认进行持久连接)</td>
<td>Connection: close</td>
</tr>
<tr>
<td>Cookie</td>
<td>HTTP请求发送时,会把保存在该请求域名下的所有cookie值一起发送给web服务器。</td>
<td>Cookie: $Version=1; Skin=new;</td>
</tr>
<tr>
<td>Content-Length</td>
<td>请求的内容长度</td>
<td>Content-Length: 348</td>
</tr>
<tr>
<td>Content-Type</td>
<td>请求的与实体对应的MIME信息</td>
<td>Content-Type: application/x-www-form-urlencoded</td>
</tr>
<tr>
<td>Date</td>
<td>请求发送的日期和时间</td>
<td>Date: Tue, 15 Nov 2010 08:12:31 GMT</td>
</tr>
<tr>
<td>Expect</td>
<td>请求的特定的服务器行为</td>
<td>Expect: 100-continue</td>
</tr>
<tr>
<td>From</td>
<td>发出请求的用户的Email</td>
<td>From: user@email.com</td>
</tr>
<tr>
<td>Host</td>
<td>指定请求的服务器的域名和端口号</td>
<td>Host: www.zcmhi.com</td>
</tr>
<tr>
<td>If-Match</td>
<td>只有请求内容与实体相匹配才有效</td>
<td>If-Match: “737060cd8c284d8af7ad3082f209582d”</td>
</tr>
<tr>
<td>If-Modified-Since</td>
<td>如果请求的部分在指定时间之后被修改则请求成功,未被修改则返回304代码</td>
<td>If-Modified-Since: Sat, 29 Oct 2010 19:43:31 GMT</td>
</tr>
<tr>
<td>If-None-Match</td>
<td>如果内容未改变返回304代码,参数为服务器先前发送的Etag,与服务器回应的Etag比较判断是否改变</td>
<td>If-None-Match: “737060cd8c284d8af7ad3082f209582d”</td>
</tr>
<tr>
<td>If-Range</td>
<td>如果实体未改变,服务器发送客户端丢失的部分,否则发送整个实体。参数也为Etag</td>
<td>If-Range: “737060cd8c284d8af7ad3082f209582d”</td>
</tr>
<tr>
<td>If-Unmodified-Since</td>
<td>只在实体在指定时间之后未被修改才请求成功</td>
<td>If-Unmodified-Since: Sat, 29 Oct 2010 19:43:31 GMT</td>
</tr>
<tr>
<td>Max-Forwards</td>
<td>限制信息通过代理和网关传送的时间</td>
<td>Max-Forwards: 10</td>
</tr>
<tr>
<td>Pragma</td>
<td>用来包含实现特定的指令</td>
<td>Pragma: no-cache</td>
</tr>
<tr>
<td>Proxy-Authorization</td>
<td>连接到代理的授权证书</td>
<td>Proxy-Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==</td>
</tr>
<tr>
<td>Range</td>
<td>只请求实体的一部分,指定范围</td>
<td>Range: bytes=500-999</td>
</tr>
<tr>
<td>Referer</td>
<td>先前网页的地址,当前请求网页紧随其后,即来路</td>
<td>Referer: http://www.zcmhi.com/archives/71.html</td>
</tr>
<tr>
<td>TE</td>
<td>客户端愿意接受的传输编码,并通知服务器接受接受尾加头信息</td>
<td>TE: trailers,deflate;q=0.5</td>
</tr>
<tr>
<td>Upgrade</td>
<td>向服务器指定某种传输协议以便服务器进行转换(如果支持)</td>
<td>Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11</td>
</tr>
<tr>
<td>User-Agent</td>
<td>User-Agent的内容包含发出请求的用户信息</td>
<td>User-Agent: Mozilla/5.0 (Linux; X11)</td>
</tr>
<tr>
<td>Via</td>
<td>通知中间网关或代理服务器地址,通信协议</td>
<td>Via: 1.0 fred, 1.1 nowhere.com (Apache/1.1)</td>
</tr>
<tr>
<td>Warning</td>
<td>关于消息实体的警告信息</td>
<td>Warn: 199 Miscellaneous warning</td>
</tr>
</tbody>
</table>
<h3 id="http-responses-header">HTTP Responses Header</h3>
<table>
<thead>
<tr>
<th>Header</th>
<th>解释</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td>Accept-Ranges</td>
<td>表明服务器是否支持指定范围请求及哪种类型的分段请求</td>
<td>Accept-Ranges: bytes</td>
</tr>
<tr>
<td>Age</td>
<td>从原始服务器到代理缓存形成的估算时间(以秒计,非负)</td>
<td>Age: 12</td>
</tr>
<tr>
<td>Allow</td>
<td>对某网络资源的有效的请求行为,不允许则返回405</td>
<td>Allow: GET, HEAD</td>
</tr>
<tr>
<td>Cache-Control</td>
<td>告诉所有的缓存机制是否可以缓存及哪种类型</td>
<td>Cache-Control: no-cache</td>
</tr>
<tr>
<td>Content-Encoding</td>
<td>web服务器支持的返回内容压缩编码类型。</td>
<td>Content-Encoding: gzip</td>
</tr>
<tr>
<td>Content-Language</td>
<td>响应体的语言</td>
<td>Content-Language: en,zh</td>
</tr>
<tr>
<td>Content-Length</td>
<td>响应体的长度</td>
<td>Content-Length: 348</td>
</tr>
<tr>
<td>Content-Location</td>
<td>请求资源可替代的备用的另一地址</td>
<td>Content-Location: /index.htm</td>
</tr>
<tr>
<td>Content-MD5</td>
<td>返回资源的MD5校验值</td>
<td>Content-MD5: Q2hlY2sgSW50ZWdyaXR5IQ==</td>
</tr>
<tr>
<td>Content-Range</td>
<td>在整个返回体中本部分的字节位置</td>
<td>Content-Range: bytes 21010-47021/47022</td>
</tr>
<tr>
<td>Content-Type</td>
<td>返回内容的MIME类型</td>
<td>Content-Type: text/html; charset=utf-8</td>
</tr>
<tr>
<td>Date</td>
<td>原始服务器消息发出的时间</td>
<td>Date: Tue, 15 Nov 2010 08:12:31 GMT</td>
</tr>
<tr>
<td>ETag</td>
<td>请求变量的实体标签的当前值</td>
<td>ETag: “737060cd8c284d8af7ad3082f209582d”</td>
</tr>
<tr>
<td>Expires</td>
<td>响应过期的日期和时间</td>
<td>Expires: Thu, 01 Dec 2010 16:00:00 GMT</td>
</tr>
<tr>
<td>Last-Modified</td>
<td>请求资源的最后修改时间</td>
<td>Last-Modified: Tue, 15 Nov 2010 12:45:26 GMT</td>
</tr>
<tr>
<td>Location</td>
<td>用来重定向接收方到非请求URL的位置来完成请求或标识新的资源</td>
<td>Location: http://www.zcmhi.com/archives/94.html</td>
</tr>
<tr>
<td>Pragma</td>
<td>包括实现特定的指令,它可应用到响应链上的任何接收方</td>
<td>Pragma: no-cache</td>
</tr>
<tr>
<td>Proxy-Authenticate</td>
<td>它指出认证方案和可应用到代理的该URL上的参数</td>
<td>Proxy-Authenticate: Basic</td>
</tr>
<tr>
<td>refresh</td>
<td>应用于重定向或一个新的资源被创造,在5秒之后重定向(由网景提出,被大部分浏览器支持)</td>
<td>Refresh: 5; url=http://www.zcmhi.com/archives/94.html</td>
</tr>
<tr>
<td>Retry-After</td>
<td>如果实体暂时不可取,通知客户端在指定时间之后再次尝试</td>
<td>Retry-After: 120</td>
</tr>
<tr>
<td>Server</td>
<td>web服务器软件名称</td>
<td>Server: Apache/1.3.27 (Unix) (Red-Hat/Linux)</td>
</tr>
<tr>
<td>Set-Cookie</td>
<td>设置Http Cookie</td>
<td>Set-Cookie: UserID=JohnDoe; Max-Age=3600; Version=1</td>
</tr>
<tr>
<td>Trailer</td>
<td>指出头域在分块传输编码的尾部存在</td>
<td>Trailer: Max-Forwards</td>
</tr>
<tr>
<td>Transfer-Encoding</td>
<td>文件传输编码</td>
<td>Transfer-Encoding:chunked</td>
</tr>
<tr>
<td>Vary</td>
<td>告诉下游代理是使用缓存响应还是从原始服务器请求</td>
<td>Vary: *</td>
</tr>
<tr>
<td>Via</td>
<td>告知代理客户端响应是通过哪里发送的</td>
<td>Via: 1.0 fred, 1.1 nowhere.com (Apache/1.1)</td>
</tr>
<tr>
<td>Warning</td>
<td>警告实体可能存在的问题</td>
<td>Warning: 199 Miscellaneous warning</td>
</tr>
<tr>
<td>WWW-Authenticate</td>
<td>表明客户端请求实体应该使用的授权方案</td>
<td>WWW-Authenticate: Basic</td>
</tr>
</tbody>
</table>
<h1 id="http状态码对照表-http-response-codes">HTTP状态码对照表 HTTP response codes</h1>
<p>当浏览者访问一个网页时,浏览者的浏览器会向网页所在服务器发出请求。当浏览器接收并显示网页前,此网页所在的服务器会返回一个包含HTTP状态码的信息头(server header)用以响应浏览器的请求。</p>
<p>HTTP状态码的英文为HTTP Status Code。 下面是常见的HTTP状态码:</p>
<ul>
<li>200 - 请求成功</li>
<li>301 - 资源(网页等)被永久转移到其它URL</li>
<li>404 - 请求的资源(网页等)不存在</li>
<li>500 - 内部服务器错误</li>
</ul>
<h3 id="http状态码的分类">HTTP状态码的分类</h3>
<p>HTTP状态码由三个十进制数字组成,第一个十进制数字定义了状态码的类型,后两个数字没有分类的作用。HTTP状态码共分为5种类型:</p>
<table>
<thead>
<tr>
<th>分类</th>
<th>分类描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>1**</td>
<td>信息,服务器收到请求,需要请求者继续执行操作</td>
</tr>
<tr>
<td>2**</td>
<td>成功,操作被成功接收并处理</td>
</tr>
<tr>
<td>3**</td>
<td>重定向,需要进一步的操作以完成请求</td>
</tr>
<tr>
<td>4**</td>
<td>客户端错误,请求包含语法错误或无法完成请求</td>
</tr>
<tr>
<td>5**</td>
<td>服务器错误,服务器在处理请求的过程中发生了错误</td>
</tr>
</tbody>
</table>
<h3 id="http状态码表版本1">HTTP状态码表(版本1)</h3>
<table>
<thead>
<tr>
<th>状态码</th>
<th>状态码英文名称</th>
<th>中文描述</th>
</tr>
</thead>
<tbody>
<tr>
<td>1开头的状态码</td>
<td> </td>
<td> </td>
</tr>
<tr>
<td>100</td>
<td>Continue</td>
<td>继续。客户端应继续其请求</td>
</tr>
<tr>
<td>101</td>
<td>Switching Protocols</td>
<td>切换协议。服务器根据客户端的请求切换协议。只能切换到更高级的协议,例如,切换到HTTP的新版本协议</td>
</tr>
<tr>
<td>2开头的状态码</td>
<td> </td>
<td> </td>
</tr>
<tr>
<td>200</td>
<td>OK</td>
<td>请求成功。一般用于GET与POST请求</td>
</tr>
<tr>
<td>201</td>
<td>Created</td>
<td>已创建。成功请求并创建了新的资源</td>
</tr>
<tr>
<td>202</td>
<td>Accepted</td>
<td>已接受。已经接受请求,但未处理完成</td>
</tr>
<tr>
<td>203</td>
<td>Non-Authoritative Information</td>
<td>非授权信息。请求成功。但返回的meta信息不在原始的服务器,而是一个副本</td>
</tr>
<tr>
<td>204</td>
<td>No Content</td>
<td>无内容。服务器成功处理,但未返回内容。在未更新网页的情况下,可确保浏览器继续显示当前文档</td>
</tr>
<tr>
<td>205</td>
<td>Reset Content</td>
<td>重置内容。服务器处理成功,用户终端(例如:浏览器)应重置文档视图。可通过此返回码清除浏览器的表单域</td>
</tr>
<tr>
<td>206</td>
<td>Partial Content</td>
<td>部分内容。服务器成功处理了部分GET请求</td>
</tr>
<tr>
<td>3开头的状态码</td>
<td> </td>
<td> </td>
</tr>
<tr>
<td>300</td>
<td>Multiple Choices</td>
<td>多种选择。请求的资源可包括多个位置,相应可返回一个资源特征与地址的列表用于用户终端(例如:浏览器)选择</td>
</tr>
<tr>
<td>301</td>
<td>Moved Permanently</td>
<td>永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替</td>
</tr>
<tr>
<td>302</td>
<td>Found</td>
<td>临时移动。与301类似。但资源只是临时被移动。客户端应继续使用原有URI</td>
</tr>
<tr>
<td>303</td>
<td>See Other</td>
<td>查看其它地址。与301类似。使用GET和POST请求查看</td>
</tr>
<tr>
<td>304</td>
<td>Not Modified</td>
<td>未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源</td>
</tr>
<tr>
<td>305</td>
<td>Use Proxy</td>
<td>使用代理。所请求的资源必须通过代理访问</td>
</tr>
<tr>
<td>306</td>
<td>Unused</td>
<td>已经被废弃的HTTP状态码</td>
</tr>
<tr>
<td>307</td>
<td>Temporary Redirect</td>
<td>临时重定向。与302类似。使用GET请求重定向</td>
</tr>
<tr>
<td>4开头的状态码</td>
<td> </td>
<td> </td>
</tr>
<tr>
<td>400</td>
<td>Bad Request</td>
<td>客户端请求的语法错误,服务器无法理解</td>
</tr>
<tr>
<td>401</td>
<td>Unauthorized</td>
<td>请求要求用户的身份认证</td>
</tr>
<tr>
<td>402</td>
<td>Payment Required</td>
<td>保留,将来使用</td>
</tr>
<tr>
<td>403</td>
<td>Forbidden</td>
<td>服务器理解请求客户端的请求,但是拒绝执行此请求</td>
</tr>
<tr>
<td>404</td>
<td>Not Found</td>
<td>服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置”您所请求的资源无法找到”的个性页面</td>
</tr>
<tr>
<td>405</td>
<td>Method Not Allowed</td>
<td>客户端请求中的方法被禁止</td>
</tr>
<tr>
<td>406</td>
<td>Not Acceptable</td>
<td>服务器无法根据客户端请求的内容特性完成请求</td>
</tr>
<tr>
<td>407</td>
<td>Proxy Authentication Required</td>
<td>请求要求代理的身份认证,与401类似,但请求者应当使用代理进行授权</td>
</tr>
<tr>
<td>408</td>
<td>Request Time-out</td>
<td>服务器等待客户端发送的请求时间过长,超时</td>
</tr>
<tr>
<td>409</td>
<td>Conflict</td>
<td>服务器完成客户端的PUT请求是可能返回此代码,服务器处理请求时发生了冲突</td>
</tr>
<tr>
<td>410</td>
<td>Gone</td>
<td>客户端请求的资源已经不存在。410不同于404,如果资源以前有现在被永久删除了可使用410代码,网站设计人员可通过301代码指定资源的新位置</td>
</tr>
<tr>
<td>411</td>
<td>Length Required</td>
<td>服务器无法处理客户端发送的不带Content-Length的请求信息</td>
</tr>
<tr>
<td>412</td>
<td>Precondition Failed</td>
<td>客户端请求信息的先决条件错误</td>
</tr>
<tr>
<td>413</td>
<td>Request Entity Too Large</td>
<td>由于请求的实体过大,服务器无法处理,因此拒绝请求。为防止客户端的连续请求,服务器可能会关闭连接。如果只是服务器暂时无法处理,则会包含一个Retry-After的响应信息</td>
</tr>
<tr>
<td>414</td>
<td>Request-URI Too Large</td>
<td>请求的URI过长(URI通常为网址),服务器无法处理</td>
</tr>
<tr>
<td>415</td>
<td>Unsupported Media Type</td>
<td>服务器无法处理请求附带的媒体格式</td>
</tr>
<tr>
<td>416</td>
<td>Requested range not satisfiable</td>
<td>客户端请求的范围无效</td>
</tr>
<tr>
<td>417</td>
<td>Expectation Failed</td>
<td>服务器无法满足Expect的请求头信息</td>
</tr>
<tr>
<td>5开头的状态码</td>
<td> </td>
<td> </td>
</tr>
<tr>
<td>500</td>
<td>Internal Server Error</td>
<td>服务器内部错误,无法完成请求</td>
</tr>
<tr>
<td>501</td>
<td>Not Implemented</td>
<td>服务器不支持请求的功能,无法完成请求</td>
</tr>
<tr>
<td>502</td>
<td>Bad Gateway</td>
<td>充当网关或代理的服务器,从远端服务器接收到了一个无效的请求</td>
</tr>
<tr>
<td>503</td>
<td>Service Unavailable</td>
<td>由于超载或系统维护,服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的Retry-After头信息中</td>
</tr>
<tr>
<td>504</td>
<td>Gateway Time-out</td>
<td>充当网关或代理的服务器,未及时从远端服务器获取请求</td>
</tr>
<tr>
<td>505</td>
<td>HTTP Version not supported</td>
<td>服务器不支持请求的HTTP协议的版本,无法完成处理</td>
</tr>
</tbody>
</table>
<h3 id="http状态码列表版本2">HTTP状态码列表(版本2)</h3>
<table>
<thead>
<tr>
<th>状态码</th>
<th>含义</th>
</tr>
</thead>
<tbody>
<tr>
<td>100</td>
<td>客户端应当继续发送请求。这个临时响应是用来通知客户端它的部分请求已经被服务器接收,且仍未被拒绝。客户端应当继续发送请求的剩余部分,或者如果请求已经完成,忽略这个响应。服务器必须在请求完成后向客户端发送一个最终响应。</td>
</tr>
<tr>
<td>101</td>
<td>服务器已经理解了客户端的请求,并将通过Upgrade 消息头通知客户端采用不同的协议来完成这个请求。在发送完这个响应最后的空行后,服务器将会切换到在Upgrade 消息头中定义的那些协议。 只有在切换新的协议更有好处的时候才应该采取类似措施。例如,切换到新的HTTP 版本比旧版本更有优势,或者切换到一个实时且同步的协议以传送利用此类特性的资源。</td>
</tr>
<tr>
<td>102</td>
<td>由WebDAV(RFC 2518)扩展的状态码,代表处理将被继续执行。</td>
</tr>
<tr>
<td>200</td>
<td>请求已成功,请求所希望的响应头或数据体将随此响应返回。</td>
</tr>
<tr>
<td>201</td>
<td>请求已经被实现,而且有一个新的资源已经依据请求的需要而建立,且其 URI 已经随Location 头信息返回。假如需要的资源无法及时建立的话,应当返回 ‘202 Accepted’。</td>
</tr>
<tr>
<td>202</td>
<td>服务器已接受请求,但尚未处理。正如它可能被拒绝一样,最终该请求可能会也可能不会被执行。在异步操作的场合下,没有比发送这个状态码更方便的做法了。 返回202状态码的响应的目的是允许服务器接受其他过程的请求(例如某个每天只执行一次的基于批处理的操作),而不必让客户端一直保持与服务器的连接直到批处理操作全部完成。在接受请求处理并返回202状态码的响应应当在返回的实体中包含一些指示处理当前状态的信息,以及指向处理状态监视器或状态预测的指针,以便用户能够估计操作是否已经完成。</td>
</tr>
<tr>
<td>203</td>
<td>服务器已成功处理了请求,但返回的实体头部元信息不是在原始服务器上有效的确定集合,而是来自本地或者第三方的拷贝。当前的信息可能是原始版本的子集或者超集。例如,包含资源的元数据可能导致原始服务器知道元信息的超级。使用此状态码不是必须的,而且只有在响应不使用此状态码便会返回200 OK的情况下才是合适的。</td>
</tr>
<tr>
<td>204</td>
<td>服务器成功处理了请求,但不需要返回任何实体内容,并且希望返回更新了的元信息。响应可能通过实体头部的形式,返回新的或更新后的元信息。如果存在这些头部信息,则应当与所请求的变量相呼应。 如果客户端是浏览器的话,那么用户浏览器应保留发送了该请求的页面,而不产生任何文档视图上的变化,即使按照规范新的或更新后的元信息应当被应用到用户浏览器活动视图中的文档。 由于204响应被禁止包含任何消息体,因此它始终以消息头后的第一个空行结尾。</td>
</tr>
<tr>
<td>205</td>
<td>服务器成功处理了请求,且没有返回任何内容。但是与204响应不同,返回此状态码的响应要求请求者重置文档视图。该响应主要是被用于接受用户输入后,立即重置表单,以便用户能够轻松地开始另一次输入。 与204响应一样,该响应也被禁止包含任何消息体,且以消息头后的第一个空行结束。</td>
</tr>
<tr>
<td>206</td>
<td>服务器已经成功处理了部分 GET 请求。类似于 FlashGet 或者迅雷这类的 HTTP 下载工具都是使用此类响应实现断点续传或者将一个大文档分解为多个下载段同时下载。 该请求必须包含 Range 头信息来指示客户端希望得到的内容范围,并且可能包含 If-Range 来作为请求条件。 响应必须包含如下的头部域: Content-Range 用以指示本次响应中返回的内容的范围;如果是 Content-Type 为 multipart/byteranges 的多段下载,则每一 multipart 段中都应包含 Content-Range 域用以指示本段的内容范围。假如响应中包含 Content-Length,那么它的数值必须匹配它返回的内容范围的真实字节数。 Date ETag 和/或 Content-Location,假如同样的请求本应该返回200响应。 Expires, Cache-Control,和/或 Vary,假如其值可能与之前相同变量的其他响应对应的值不同的话。 假如本响应请求使用了 If-Range 强缓存验证,那么本次响应不应该包含其他实体头;假如本响应的请求使用了 If-Range 弱缓存验证,那么本次响应禁止包含其他实体头;这避免了缓存的实体内容和更新了的实体头信息之间的不一致。否则,本响应就应当包含所有本应该返回200响应中应当返回的所有实体头部域。 假如 ETag 或 Last-Modified 头部不能精确匹配的话,则客户端缓存应禁止将206响应返回的内容与之前任何缓存过的内容组合在一起。 任何不支持 Range 以及 Content-Range 头的缓存都禁止缓存206响应返回的内容。</td>
</tr>
<tr>
<td>207</td>
<td>由WebDAV(RFC 2518)扩展的状态码,代表之后的消息体将是一个XML消息,并且可能依照之前子请求数量的不同,包含一系列独立的响应代码。</td>
</tr>
<tr>
<td>300</td>
<td>被请求的资源有一系列可供选择的回馈信息,每个都有自己特定的地址和浏览器驱动的商议信息。用户或浏览器能够自行选择一个首选的地址进行重定向。 除非这是一个 HEAD 请求,否则该响应应当包括一个资源特性及地址的列表的实体,以便用户或浏览器从中选择最合适的重定向地址。这个实体的格式由 Content-Type 定义的格式所决定。浏览器可能根据响应的格式以及浏览器自身能力,自动作出最合适的选择。当然,RFC 2616规范并没有规定这样的自动选择该如何进行。 如果服务器本身已经有了首选的回馈选择,那么在 Location 中应当指明这个回馈的 URI;浏览器可能会将这个 Location 值作为自动重定向的地址。此外,除非额外指定,否则这个响应也是可缓存的。</td>
</tr>
<tr>
<td>301</td>
<td>被请求的资源已永久移动到新位置,并且将来任何对此资源的引用都应该使用本响应返回的若干个 URI 之一。如果可能,拥有链接编辑功能的客户端应当自动把请求的地址修改为从服务器反馈回来的地址。除非额外指定,否则这个响应也是可缓存的。 新的永久性的 URI 应当在响应的 Location 域中返回。除非这是一个 HEAD 请求,否则响应的实体中应当包含指向新的 URI 的超链接及简短说明。 如果这不是一个 GET 或者 HEAD 请求,因此浏览器禁止自动进行重定向,除非得到用户的确认,因为请求的条件可能因此发生变化。 注意:对于某些使用 HTTP/1.0 协议的浏览器,当它们发送的 POST 请求得到了一个301响应的话,接下来的重定向请求将会变成 GET 方式。</td>
</tr>
<tr>
<td>302</td>
<td>请求的资源现在临时从不同的 URI 响应请求。由于这样的重定向是临时的,客户端应当继续向原有地址发送以后的请求。只有在Cache-Control或Expires中进行了指定的情况下,这个响应才是可缓存的。 新的临时性的 URI 应当在响应的 Location 域中返回。除非这是一个 HEAD 请求,否则响应的实体中应当包含指向新的 URI 的超链接及简短说明。 如果这不是一个 GET 或者 HEAD 请求,那么浏览器禁止自动进行重定向,除非得到用户的确认,因为请求的条件可能因此发生变化。 注意:虽然RFC 1945和RFC 2068规范不允许客户端在重定向时改变请求的方法,但是很多现存的浏览器将302响应视作为303响应,并且使用 GET 方式访问在 Location 中规定的 URI,而无视原先请求的方法。状态码303和307被添加了进来,用以明确服务器期待客户端进行何种反应。</td>
</tr>
<tr>
<td>303</td>
<td>对应当前请求的响应可以在另一个 URI 上被找到,而且客户端应当采用 GET 的方式访问那个资源。这个方法的存在主要是为了允许由脚本激活的POST请求输出重定向到一个新的资源。这个新的 URI 不是原始资源的替代引用。同时,303响应禁止被缓存。当然,第二个请求(重定向)可能被缓存。 新的 URI 应当在响应的 Location 域中返回。除非这是一个 HEAD 请求,否则响应的实体中应当包含指向新的 URI 的超链接及简短说明。 注意:许多 HTTP/1.1 版以前的 浏览器不能正确理解303状态。如果需要考虑与这些浏览器之间的互动,302状态码应该可以胜任,因为大多数的浏览器处理302响应时的方式恰恰就是上述规范要求客户端处理303响应时应当做的。</td>
</tr>
<tr>
<td>304</td>
<td>如果客户端发送了一个带条件的 GET 请求且该请求已被允许,而文档的内容(自上次访问以来或者根据请求的条件)并没有改变,则服务器应当返回这个状态码。304响应禁止包含消息体,因此始终以消息头后的第一个空行结尾。 该响应必须包含以下的头信息: Date,除非这个服务器没有时钟。假如没有时钟的服务器也遵守这些规则,那么代理服务器以及客户端可以自行将 Date 字段添加到接收到的响应头中去(正如RFC 2068中规定的一样),缓存机制将会正常工作。 ETag 和/或 Content-Location,假如同样的请求本应返回200响应。 Expires, Cache-Control,和/或Vary,假如其值可能与之前相同变量的其他响应对应的值不同的话。 假如本响应请求使用了强缓存验证,那么本次响应不应该包含其他实体头;否则(例如,某个带条件的 GET 请求使用了弱缓存验证),本次响应禁止包含其他实体头;这避免了缓存了的实体内容和更新了的实体头信息之间的不一致。 假如某个304响应指明了当前某个实体没有缓存,那么缓存系统必须忽视这个响应,并且重复发送不包含限制条件的请求。 假如接收到一个要求更新某个缓存条目的304响应,那么缓存系统必须更新整个条目以反映所有在响应中被更新的字段的值。</td>
</tr>
<tr>
<td>305</td>
<td>被请求的资源必须通过指定的代理才能被访问。Location 域中将给出指定的代理所在的 URI 信息,接收者需要重复发送一个单独的请求,通过这个代理才能访问相应资源。只有原始服务器才能建立305响应。 注意:RFC 2068中没有明确305响应是为了重定向一个单独的请求,而且只能被原始服务器建立。忽视这些限制可能导致严重的安全后果。</td>
</tr>
<tr>
<td>306</td>
<td>在最新版的规范中,306状态码已经不再被使用。</td>
</tr>
<tr>
<td>307</td>
<td>请求的资源现在临时从不同的URI 响应请求。由于这样的重定向是临时的,客户端应当继续向原有地址发送以后的请求。只有在Cache-Control或Expires中进行了指定的情况下,这个响应才是可缓存的。 新的临时性的URI 应当在响应的 Location 域中返回。除非这是一个HEAD 请求,否则响应的实体中应当包含指向新的URI 的超链接及简短说明。因为部分浏览器不能识别307响应,因此需要添加上述必要信息以便用户能够理解并向新的 URI 发出访问请求。 如果这不是一个GET 或者 HEAD 请求,那么浏览器禁止自动进行重定向,除非得到用户的确认,因为请求的条件可能因此发生变化。</td>
</tr>
<tr>
<td>400</td>
<td>1、语义有误,当前请求无法被服务器理解。除非进行修改,否则客户端不应该重复提交这个请求。 2、请求参数有误。</td>
</tr>
<tr>
<td>401</td>
<td>当前请求需要用户验证。该响应必须包含一个适用于被请求资源的 WWW-Authenticate 信息头用以询问用户信息。客户端可以重复提交一个包含恰当的 Authorization 头信息的请求。如果当前请求已经包含了 Authorization 证书,那么401响应代表着服务器验证已经拒绝了那些证书。如果401响应包含了与前一个响应相同的身份验证询问,且浏览器已经至少尝试了一次验证,那么浏览器应当向用户展示响应中包含的实体信息,因为这个实体信息中可能包含了相关诊断信息。参见RFC 2617。</td>
</tr>
<tr>
<td>402</td>
<td>该状态码是为了将来可能的需求而预留的。</td>
</tr>
<tr>
<td>403</td>
<td>服务器已经理解请求,但是拒绝执行它。与401响应不同的是,身份验证并不能提供任何帮助,而且这个请求也不应该被重复提交。如果这不是一个 HEAD 请求,而且服务器希望能够讲清楚为何请求不能被执行,那么就应该在实体内描述拒绝的原因。当然服务器也可以返回一个404响应,假如它不希望让客户端获得任何信息。</td>
</tr>
<tr>
<td>404</td>
<td>请求失败,请求所希望得到的资源未被在服务器上发现。没有信息能够告诉用户这个状况到底是暂时的还是永久的。假如服务器知道情况的话,应当使用410状态码来告知旧资源因为某些内部的配置机制问题,已经永久的不可用,而且没有任何可以跳转的地址。404这个状态码被广泛应用于当服务器不想揭示到底为何请求被拒绝或者没有其他适合的响应可用的情况下。</td>
</tr>
<tr>
<td>405</td>
<td>请求行中指定的请求方法不能被用于请求相应的资源。该响应必须返回一个Allow 头信息用以表示出当前资源能够接受的请求方法的列表。 鉴于 PUT,DELETE 方法会对服务器上的资源进行写操作,因而绝大部分的网页服务器都不支持或者在默认配置下不允许上述请求方法,对于此类请求均会返回405错误。</td>
</tr>
<tr>
<td>406</td>
<td>请求的资源的内容特性无法满足请求头中的条件,因而无法生成响应实体。 除非这是一个 HEAD 请求,否则该响应就应当返回一个包含可以让用户或者浏览器从中选择最合适的实体特性以及地址列表的实体。实体的格式由 Content-Type 头中定义的媒体类型决定。浏览器可以根据格式及自身能力自行作出最佳选择。但是,规范中并没有定义任何作出此类自动选择的标准。</td>
</tr>
<tr>
<td>407</td>
<td>与401响应类似,只不过客户端必须在代理服务器上进行身份验证。代理服务器必须返回一个 Proxy-Authenticate 用以进行身份询问。客户端可以返回一个 Proxy-Authorization 信息头用以验证。参见RFC 2617。</td>
</tr>
<tr>
<td>408</td>
<td>请求超时。客户端没有在服务器预备等待的时间内完成一个请求的发送。客户端可以随时再次提交这一请求而无需进行任何更改。</td>
</tr>
<tr>
<td>409</td>
<td>由于和被请求的资源的当前状态之间存在冲突,请求无法完成。这个代码只允许用在这样的情况下才能被使用:用户被认为能够解决冲突,并且会重新提交新的请求。该响应应当包含足够的信息以便用户发现冲突的源头。 冲突通常发生于对 PUT 请求的处理中。例如,在采用版本检查的环境下,某次 PUT 提交的对特定资源的修改请求所附带的版本信息与之前的某个(第三方)请求向冲突,那么此时服务器就应该返回一个409错误,告知用户请求无法完成。此时,响应实体中很可能会包含两个冲突版本之间的差异比较,以便用户重新提交归并以后的新版本。</td>
</tr>
<tr>
<td>410</td>
<td>被请求的资源在服务器上已经不再可用,而且没有任何已知的转发地址。这样的状况应当被认为是永久性的。如果可能,拥有链接编辑功能的客户端应当在获得用户许可后删除所有指向这个地址的引用。如果服务器不知道或者无法确定这个状况是否是永久的,那么就应该使用404状态码。除非额外说明,否则这个响应是可缓存的。 410响应的目的主要是帮助网站管理员维护网站,通知用户该资源已经不再可用,并且服务器拥有者希望所有指向这个资源的远端连接也被删除。这类事件在限时、增值服务中很普遍。同样,410响应也被用于通知客户端在当前服务器站点上,原本属于某个个人的资源已经不再可用。当然,是否需要把所有永久不可用的资源标记为’410 Gone’,以及是否需要保持此标记多长时间,完全取决于服务器拥有者。</td>
</tr>
<tr>
<td>411</td>
<td>服务器拒绝在没有定义 Content-Length 头的情况下接受请求。在添加了表明请求消息体长度的有效 Content-Length 头之后,客户端可以再次提交该请求。</td>
</tr>
<tr>
<td>412</td>
<td>服务器在验证在请求的头字段中给出先决条件时,没能满足其中的一个或多个。这个状态码允许客户端在获取资源时在请求的元信息(请求头字段数据)中设置先决条件,以此避免该请求方法被应用到其希望的内容以外的资源上。</td>
</tr>
<tr>
<td>413</td>
<td>服务器拒绝处理当前请求,因为该请求提交的实体数据大小超过了服务器愿意或者能够处理的范围。此种情况下,服务器可以关闭连接以免客户端继续发送此请求。 如果这个状况是临时的,服务器应当返回一个 Retry-After 的响应头,以告知客户端可以在多少时间以后重新尝试。</td>
</tr>
<tr>
<td>414</td>
<td>请求的URI 长度超过了服务器能够解释的长度,因此服务器拒绝对该请求提供服务。这比较少见,通常的情况包括: 本应使用POST方法的表单提交变成了GET方法,导致查询字符串(Query String)过长。 重定向URI “黑洞”,例如每次重定向把旧的 URI 作为新的 URI 的一部分,导致在若干次重定向后 URI 超长。 客户端正在尝试利用某些服务器中存在的安全漏洞攻击服务器。这类服务器使用固定长度的缓冲读取或操作请求的 URI,当 GET 后的参数超过某个数值后,可能会产生缓冲区溢出,导致任意代码被执行[1]。没有此类漏洞的服务器,应当返回414状态码。</td>
</tr>
<tr>
<td>415</td>
<td>对于当前请求的方法和所请求的资源,请求中提交的实体并不是服务器中所支持的格式,因此请求被拒绝。</td>
</tr>
<tr>
<td>416</td>
<td>如果请求中包含了 Range 请求头,并且 Range 中指定的任何数据范围都与当前资源的可用范围不重合,同时请求中又没有定义 If-Range 请求头,那么服务器就应当返回416状态码。 假如 Range 使用的是字节范围,那么这种情况就是指请求指定的所有数据范围的首字节位置都超过了当前资源的长度。服务器也应当在返回416状态码的同时,包含一个 Content-Range 实体头,用以指明当前资源的长度。这个响应也被禁止使用 multipart/byteranges 作为其 Content-Type。</td>
</tr>
<tr>
<td>417</td>
<td>在请求头 Expect 中指定的预期内容无法被服务器满足,或者这个服务器是一个代理服务器,它有明显的证据证明在当前路由的下一个节点上,Expect 的内容无法被满足。</td>
</tr>
<tr>
<td>421</td>
<td>从当前客户端所在的IP地址到服务器的连接数超过了服务器许可的最大范围。通常,这里的IP地址指的是从服务器上看到的客户端地址(比如用户的网关或者代理服务器地址)。在这种情况下,连接数的计算可能涉及到不止一个终端用户。</td>
</tr>
<tr>
<td>422</td>
<td>从当前客户端所在的IP地址到服务器的连接数超过了服务器许可的最大范围。通常,这里的IP地址指的是从服务器上看到的客户端地址(比如用户的网关或者代理服务器地址)。在这种情况下,连接数的计算可能涉及到不止一个终端用户。</td>
</tr>
<tr>
<td>422</td>
<td>请求格式正确,但是由于含有语义错误,无法响应。(RFC 4918 WebDAV)423 Locked 当前资源被锁定。(RFC 4918 WebDAV)</td>
</tr>
<tr>
<td>424</td>
<td>由于之前的某个请求发生的错误,导致当前请求失败,例如 PROPPATCH。(RFC 4918 WebDAV)</td>
</tr>
<tr>
<td>425</td>
<td>在WebDav Advanced Collections 草案中定义,但是未出现在《WebDAV 顺序集协议》(RFC 3658)中。</td>
</tr>
<tr>
<td>426</td>
<td>客户端应当切换到TLS/1.0。(RFC 2817)</td>
</tr>
<tr>
<td>449</td>
<td>由微软扩展,代表请求应当在执行完适当的操作后进行重试。</td>
</tr>
<tr>
<td>500</td>
<td>服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。一般来说,这个问题都会在服务器的程序码出错时出现。</td>
</tr>
<tr>
<td>501</td>
<td>服务器不支持当前请求所需要的某个功能。当服务器无法识别请求的方法,并且无法支持其对任何资源的请求。</td>
</tr>
<tr>
<td>502</td>
<td>作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应。</td>
</tr>
<tr>
<td>503</td>
<td>由于临时的服务器维护或者过载,服务器当前无法处理请求。这个状况是临时的,并且将在一段时间以后恢复。如果能够预计延迟时间,那么响应中可以包含一个 Retry-After 头用以标明这个延迟时间。如果没有给出这个 Retry-After 信息,那么客户端应当以处理500响应的方式处理它。 注意:503状态码的存在并不意味着服务器在过载的时候必须使用它。某些服务器只不过是希望拒绝客户端的连接。</td>
</tr>
<tr>
<td>504</td>
<td>作为网关或者代理工作的服务器尝试执行请求时,未能及时从上游服务器(URI标识出的服务器,例如HTTP、FTP、LDAP)或者辅助服务器(例如DNS)收到响应。 注意:某些代理服务器在DNS查询超时时会返回400或者500错误</td>
</tr>
<tr>
<td>505</td>
<td>服务器不支持,或者拒绝支持在请求中使用的 HTTP 版本。这暗示着服务器不能或不愿使用与客户端相同的版本。响应中应当包含一个描述了为何版本不被支持以及服务器支持哪些协议的实体。</td>
</tr>
<tr>
<td>506</td>
<td>由《透明内容协商协议》(RFC 2295)扩展,代表服务器存在内部配置错误:被请求的协商变元资源被配置为在透明内容协商中使用自己,因此在一个协商处理中不是一个合适的重点。</td>
</tr>
<tr>
<td>507</td>
<td>服务器无法存储完成请求所必须的内容。这个状况被认为是临时的。WebDAV (RFC 4918)</td>
</tr>
<tr>
<td>509</td>
<td>服务器达到带宽限制。这不是一个官方的状态码,但是仍被广泛使用。</td>
</tr>
<tr>
<td>510</td>
<td>获取资源所需要的策略并没有没满足。(RFC 2774)</td>
</tr>
</tbody>
</table>
<h1 id="参考资料">参考资料</h1>
<ul>
<li>http://tools.jb51.net/table/http_status_code</li>
</ul>进击de豆子HTTP请求方法对照表go单元测试-基本概念及命令2023-05-01T00:00:00+08:002023-05-01T00:00:00+08:00https://beangogo.cn/2023/05/01/go-test-1-utest<p>本文主要介绍golang的单元测试,包含单元测试基本概念,如何进行不同函数单元测试,压测等</p>
<h1 id="一单元测试">一、单元测试</h1>
<h2 id="单元测试框架基准">单元测试框架基准</h2>
<p>1、文件命名规则: 含有单元测试代码的go文件必须以_test.go结尾,一般测试文件和待测试文件在同一个文件夹内。</p>
<p>2、函数声明规则: 测试函数的签名必须接收一个指向testing.T类型的指针,并且函数没有返回值。</p>
<p>3、函数命名规则:单元测试的函数名必须以Test开头,是可导出公开的函数,最好是Test+要测试的方法函数名。</p>
<h2 id="常用选项">常用选项</h2>
<pre><code class="language-plain">-v :查看更详细的测试结果输出
-run:指定输出哪个函数的测试结果,默认为当前路径下所有单元测试函数
-coverprofile:指定生成覆盖率测试输出文件
</code></pre>
<h1 id="二-基准测试">二 、基准测试</h1>
<p>工具推荐:benchstat</p>
<p>安装:go get golang.org/x/perf/cmd/benchstat (将会安装到$GOPATH/bin目录下,需要将该目录配置到环境变量中)</p>
<h2 id="1什么是基准测试">1、什么是基准测试</h2>
<p>基准测试是一种<strong>测试代码性能</strong>的方法,主要通过测试CPU和内存等因素,来评估代码性能,以此来调优代码性能。</p>
<h2 id="2编写规则">2、编写规则</h2>
<pre><code class="language-plain">1、文件命名规则:
含有单元测试代码的go文件必须以_test.go结尾,Go语言测试工具只认符合这个规则的文件
单元测试文件名_test.go前面的部分最好是被测试的方法所在go文件的文件名。
2、函数声明规则:
测试函数的签名必须接收一个指向testing.B类型的指针,并且函数没有返回值。
3、函数命名规则:
单元测试的函数名必须以Benchmark开头,是可导出公开的函数,最好是Benchmark+要测试的方法函数名。
4、函数体设计规则:
b.N 是基准测试框架提供,用于控制循环次数,循环调用测试代码评估性能。
b.ResetTimer()/b.StartTimer()/b.StopTimer()用于控制计时器,准确控制用于性能测试代码的耗时。
</code></pre>
<h2 id="3基准测试命令选项">3、基准测试命令选项</h2>
<h3 id="命令参数"><strong>命令参数</strong></h3>
<ul>
<li><code class="language-plaintext highlighter-rouge">-bench=.</code> 表示指定执行测试函数。<code class="language-plaintext highlighter-rouge">.</code>表示执行所有,如果修改为<code class="language-plaintext highlighter-rouge">go test -bench=BenchmarkJoinStrUseSprint</code>那么只会执行<code class="language-plaintext highlighter-rouge">BenchmarkJoinStrUseSprint</code>。</li>
<li><code class="language-plaintext highlighter-rouge">-benchtime=1s</code>指定执行时间为<code class="language-plaintext highlighter-rouge">1s</code></li>
<li><code class="language-plaintext highlighter-rouge">-benchmem</code>显示内存情况</li>
<li><code class="language-plaintext highlighter-rouge">-count=1</code>表示执行一次</li>
</ul>
<h3 id="响应参数"><strong>响应参数</strong></h3>
<ul>
<li><code class="language-plaintext highlighter-rouge">goos: linux</code> 操作系统</li>
<li><code class="language-plaintext highlighter-rouge">goarch: amd64</code> 系统体系架构</li>
<li><code class="language-plaintext highlighter-rouge">BenchmarkJoinStrUseNor-8</code> 执行的函数名称以及对应的<code class="language-plaintext highlighter-rouge">GOMAXPROCS</code>值。</li>
<li><code class="language-plaintext highlighter-rouge">79888155</code><code class="language-plaintext highlighter-rouge">b.N</code>的值</li>
<li><code class="language-plaintext highlighter-rouge">15.5 ns/op</code> 执行一次函数所花费的时间</li>
<li><code class="language-plaintext highlighter-rouge">0 B/op</code> 执行一次函数分配的内存</li>
<li><code class="language-plaintext highlighter-rouge">0 allocs/op</code> 执行一次函数所分配的内存次数</li>
</ul>
<p>这几个数当然是<strong>越小越好</strong></p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">操作命令</span><span class="err">:</span><span class="k">go</span> <span class="n">test</span> <span class="o">-</span><span class="n">bench</span><span class="o">=.</span> <span class="n">benchtime</span><span class="o">=</span><span class="m">3</span><span class="n">s</span> <span class="o">-</span><span class="n">run</span><span class="o">=</span><span class="n">none</span> <span class="o">-</span><span class="n">cpuprofile</span>
<span class="o">-</span><span class="n">bench</span> <span class="err">:</span><span class="k">go</span> <span class="n">test默认不会基准测试</span><span class="err">,</span><span class="n">需要使用bench启动基准测试</span><span class="err">,</span><span class="n">指定匹配基准测试的函数</span><span class="err">,“</span><span class="o">.</span><span class="err">”</span><span class="n">表示运行所有基准测试</span>
<span class="o">-</span><span class="n">benchtime</span> <span class="err">:</span><span class="n">测试时间默认为1s</span><span class="err">,</span><span class="n">如果想测试运行时间更长</span><span class="err">,</span><span class="n">用</span><span class="o">-</span><span class="n">benchtime指定</span>
<span class="o">-</span><span class="n">run</span> <span class="err">:</span><span class="n">默认情况下go</span> <span class="n">test会运行单元测试</span><span class="err">,</span><span class="n">为防止其干扰基准测试输出结果</span><span class="err">,</span>
<span class="n">可使用</span><span class="o">-</span><span class="n">run过滤单元测试</span><span class="err">,</span><span class="n">使用none完全屏蔽</span><span class="err">,“</span><span class="o">.</span><span class="err">”</span><span class="n">运行所有单元测试</span>
<span class="o">-</span><span class="n">count</span> <span class="err">:</span><span class="n">指定执行多少次</span>
<span class="err">$</span> <span class="k">go</span> <span class="n">test</span> <span class="o">-</span><span class="n">bench</span><span class="o">=.</span> <span class="o">-</span><span class="n">run</span><span class="o">=</span><span class="n">none</span>
<span class="n">goos</span><span class="o">:</span> <span class="n">linux</span>
<span class="n">goarch</span><span class="o">:</span> <span class="n">amd64</span>
<span class="n">pkg</span><span class="o">:</span> <span class="n">learning</span><span class="o">/</span><span class="n">test</span><span class="o">/</span><span class="n">benchmark</span>
<span class="n">BenchmarkSprintf</span><span class="o">-</span><span class="m">12</span> <span class="m">20000000</span> <span class="m">70.6</span> <span class="n">ns</span><span class="o">/</span><span class="n">op</span>
<span class="n">PASS</span>
<span class="n">ok</span> <span class="n">learning</span><span class="o">/</span><span class="n">test</span><span class="o">/</span><span class="n">benchmark</span> <span class="m">1.485</span><span class="n">s</span>
<span class="n">其中BenchmarkSprintf</span><span class="o">-</span><span class="m">12</span><span class="err">,</span><span class="m">12</span><span class="n">表示GOMAXPROCS</span><span class="err">,</span><span class="m">20000000</span><span class="n">表示循环次数</span><span class="err">,</span><span class="m">70.6</span> <span class="n">ns</span><span class="o">/</span><span class="n">op表示单次循环操作花费时间</span>
<span class="o">-</span><span class="n">cpuprofile</span> <span class="err">:</span><span class="n">生成运行时CPU详细信息</span>
<span class="k">go</span> <span class="n">test</span> <span class="o">-</span><span class="n">bench</span><span class="o">=.</span> <span class="o">-</span><span class="n">run</span><span class="o">=</span><span class="n">none</span> <span class="o">-</span><span class="n">cpuprofile</span><span class="o">=</span><span class="n">xxx</span> <span class="n">xxx</span>
<span class="p">(</span><span class="n">pprof</span><span class="p">)</span> <span class="n">quit</span>
<span class="o">-</span><span class="n">benchmem</span> <span class="err">:</span><span class="n">提供每次操作分配内存的次数</span><span class="err">,</span><span class="n">以及每次分配的字节数</span><span class="err">。</span>
<span class="err">$</span> <span class="k">go</span> <span class="n">test</span> <span class="o">-</span><span class="n">bench</span><span class="o">=.</span> <span class="o">-</span><span class="n">benchmem</span> <span class="o">-</span><span class="n">run</span><span class="o">=</span><span class="n">none</span>
<span class="n">goos</span><span class="o">:</span> <span class="n">linux</span>
<span class="n">goarch</span><span class="o">:</span> <span class="n">amd64</span>
<span class="n">pkg</span><span class="o">:</span> <span class="n">learning</span><span class="o">/</span><span class="n">test</span><span class="o">/</span><span class="n">benchmark</span>
<span class="n">BenchmarkSprintf</span><span class="o">-</span><span class="m">12</span> <span class="m">20000000</span> <span class="m">70.5</span> <span class="n">ns</span><span class="o">/</span><span class="n">op</span> <span class="m">16</span> <span class="n">B</span><span class="o">/</span><span class="n">op</span> <span class="m">2</span> <span class="n">allocs</span><span class="o">/</span><span class="n">op</span>
<span class="n">BenchmarkFormat</span><span class="o">-</span><span class="m">12</span> <span class="m">20000000</span> <span class="m">77.1</span> <span class="n">ns</span><span class="o">/</span><span class="n">op</span> <span class="m">18</span> <span class="n">B</span><span class="o">/</span><span class="n">op</span> <span class="m">2</span> <span class="n">allocs</span><span class="o">/</span><span class="n">op</span>
<span class="n">BenchmarkItoa</span><span class="o">-</span><span class="m">12</span> <span class="m">20000000</span> <span class="m">78.4</span> <span class="n">ns</span><span class="o">/</span><span class="n">op</span> <span class="m">18</span> <span class="n">B</span><span class="o">/</span><span class="n">op</span> <span class="m">2</span> <span class="n">allocs</span><span class="o">/</span><span class="n">op</span>
<span class="n">PASS</span>
<span class="n">ok</span> <span class="n">learning</span><span class="o">/</span><span class="n">test</span><span class="o">/</span><span class="n">benchmark</span> <span class="m">4.754</span><span class="n">s</span>
</code></pre></div></div>
<h2 id="4实例1slice的不同使用方式">4、实例1:slice的不同使用方式</h2>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="n">benchmark</span>
<span class="k">import</span> <span class="p">(</span>
<span class="s">"testing"</span>
<span class="p">)</span>
<span class="k">const</span> <span class="n">TotalTimes</span> <span class="o">=</span> <span class="m">1000000</span>
<span class="k">func</span> <span class="n">StaticCapacity</span><span class="p">()</span> <span class="p">{</span>
<span class="c">// 提前一次性分配好slice所需内存空间,中间不需要再扩容,len为0,cap为1000000</span>
<span class="k">var</span> <span class="n">s</span> <span class="o">=</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="n">TotalTimes</span><span class="p">)</span>
<span class="k">for</span> <span class="n">i</span> <span class="o">:=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">TotalTimes</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span> <span class="p">{</span>
<span class="n">s</span> <span class="o">=</span> <span class="nb">append</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="m">0</span><span class="p">)</span>
<span class="c">//fmt.Printf("len = %d, cap = %d\n", len(s), cap(s))</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">func</span> <span class="n">DynamicCapacity</span><span class="p">()</span> <span class="p">{</span>
<span class="c">// 依赖slice底层自动扩容,中间会有很多次扩容,每次都从新分配一段新的内存空间,</span>
<span class="c">// 然后把数据拷贝到新的slice内存空间,然后释放旧空间,导致引发不必要的GC</span>
<span class="k">var</span> <span class="n">s</span> <span class="p">[]</span><span class="kt">byte</span>
<span class="k">for</span> <span class="n">i</span> <span class="o">:=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">TotalTimes</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span> <span class="p">{</span>
<span class="n">s</span> <span class="o">=</span> <span class="nb">append</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="m">0</span><span class="p">)</span>
<span class="c">//fmt.Printf("len = %d, cap = %d\n", len(s), cap(s))</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">func</span> <span class="n">BenchmarkStaticCapacity</span><span class="p">(</span><span class="n">b</span> <span class="o">*</span><span class="n">testing</span><span class="o">.</span><span class="n">B</span><span class="p">)</span> <span class="p">{</span>
<span class="n">b</span><span class="o">.</span><span class="n">ResetTimer</span><span class="p">()</span>
<span class="k">for</span> <span class="n">i</span> <span class="o">:=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">b</span><span class="o">.</span><span class="n">N</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span> <span class="p">{</span>
<span class="n">StaticCapacity</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">func</span> <span class="n">BenchmarkDynamicCapacity</span><span class="p">(</span><span class="n">b</span> <span class="o">*</span><span class="n">testing</span><span class="o">.</span><span class="n">B</span><span class="p">)</span> <span class="p">{</span>
<span class="n">b</span><span class="o">.</span><span class="n">ResetTimer</span><span class="p">()</span>
<span class="k">for</span> <span class="n">i</span> <span class="o">:=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">b</span><span class="o">.</span><span class="n">N</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span> <span class="p">{</span>
<span class="n">DynamicCapacity</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>测试结果:</p>
<table>
<tbody>
<tr>
<td>模式</td>
<td>操作时间消耗 ns/op</td>
<td>内存分配大小 B/op</td>
<td>内存分配次数 allocs/op</td>
</tr>
</tbody>
</table>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err">$</span> <span class="k">go</span> <span class="n">test</span> <span class="o">-</span><span class="n">bench</span><span class="o">=.</span> <span class="o">-</span><span class="n">run</span><span class="o">=</span><span class="n">none</span> <span class="o">-</span><span class="n">benchmem</span>
<span class="n">goos</span><span class="o">:</span> <span class="n">linux</span>
<span class="n">goarch</span><span class="o">:</span> <span class="n">amd64</span>
<span class="n">pkg</span><span class="o">:</span> <span class="n">learning</span><span class="o">/</span><span class="n">testing</span><span class="o">/</span><span class="n">benchmark</span>
<span class="n">BenchmarkStaticCapacity</span><span class="o">-</span><span class="m">12</span> <span class="m">3000</span> <span class="m">912668</span> <span class="n">ns</span><span class="o">/</span><span class="n">op</span> <span class="m">1007617</span> <span class="n">B</span><span class="o">/</span><span class="n">op</span> <span class="m">1</span> <span class="n">allocs</span><span class="o">/</span><span class="n">op</span>
<span class="n">BenchmarkDynamicCapacity</span><span class="o">-</span><span class="m">12</span> <span class="m">1000</span> <span class="m">1935269</span> <span class="n">ns</span><span class="o">/</span><span class="n">op</span> <span class="m">5863427</span> <span class="n">B</span><span class="o">/</span><span class="n">op</span> <span class="m">35</span> <span class="n">allocs</span><span class="o">/</span><span class="n">op</span>
<span class="n">PASS</span>
<span class="n">ok</span> <span class="n">learning</span><span class="o">/</span><span class="n">testing</span><span class="o">/</span><span class="n">benchmark</span> <span class="m">4.914</span><span class="n">s</span>
</code></pre></div></div>
<p>可以看出StaticCapacity 性能明显优于DynamicCapacity,所以如果同一个slice被大量循环使用,可提前一次性分配好适量的内存空间</p>
<p>总结:在代码设计过程中,对于性能要求比较高的地方,编写基准测试非常重要,这有助于我们开发出性能更优的代码。不过性能、可用性、复用性等也要有一个相对的取舍,不能为了追求性能而过度优化。</p>
<h1 id="参考资料">参考资料</h1>
<ul>
<li>https://github.com/sxs2473/go-performane-tuning</li>
<li>https://www.cnblogs.com/wayne666/p/10559900.html</li>
</ul>进击de豆子本文主要介绍golang的单元测试,包含单元测试基本概念,如何进行不同函数单元测试,压测等linux环境使用clash代理2023-04-09T00:00:00+08:002023-04-09T00:00:00+08:00https://beangogo.cn/2023/04/09/clash-linux<p>本文介绍linux环境clash代理使用</p>
<h3 id="1-项目介绍">1. 项目介绍</h3>
<p>此项目是通过使用<a href="https://www.zhihu.com/search?q=开源项目&search_source=Entity&hybrid_search_source=Entity&hybrid_search_extra={"sourceType"%3A"answer"%2C"sourceId"%3A2906870859}">开源项目</a><a href="https://link.zhihu.com/?target=https%3A//github.com/Dreamacro/clash">clash</a>作为核心程序,再结合脚本实现简单的代理功能。</p>
<p>主要是为了解决我们在服务器上下载GitHub等一些国外资源速度慢的问题。</p>
<h3 id="2-下载-clash-for-linux">2. 下载 clash-for-linux</h3>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>git clone https://github.com/wanhebin/clash-for-linux.git
</code></pre></div></div>
<p>进入到项目目录,编辑start.sh<a href="https://www.zhihu.com/search?q=脚本文件&search_source=Entity&hybrid_search_source=Entity&hybrid_search_extra={"sourceType"%3A"answer"%2C"sourceId"%3A2906870859}">脚本文件</a>,修改变量URL的值,此值为Clash订阅地址。</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">cd </span>clash-for-linux
<span class="nv">$ </span>vim start.sh
</code></pre></div></div>
<h3 id="3-启动程序">3. 启动程序</h3>
<p>直接<a href="https://www.zhihu.com/search?q=运行脚本&search_source=Entity&hybrid_search_source=Entity&hybrid_search_extra={"sourceType"%3A"answer"%2C"sourceId"%3A2906870859}">运行脚本</a>文件start.sh</p>
<p>进入项目目录</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">cd </span>clash-for-linux
</code></pre></div></div>
<p>运行启动脚本</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>sh start.sh
Clash订阅地址可访问! <span class="o">[</span> OK <span class="o">]</span>
配置文件config.yaml下载成功! <span class="o">[</span> OK <span class="o">]</span>
服务启动成功! <span class="o">[</span> OK <span class="o">]</span>
Clash Dashboard 访问地址:http://IP:9090/ui
Secret:xxxxxxxxxxxxx
请执行以下命令加载环境变量: <span class="nb">source</span> /etc/profile.d/clash.sh
请执行以下命令开启系统代理: proxy_on
若要临时关闭系统代理,请执行: proxy_off
</code></pre></div></div>
<p>然后通过访问<code class="language-plaintext highlighter-rouge">Clash Dashboard</code>(http://IP:9090/ui) 来选择相关节点</p>
<p>命令行登陆</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span> curl http://192.168.10.29:9090/ui <span class="nt">-H</span> <span class="s2">"Authentication: b&ZlKTte5OnEt2Sn"</span>
<span class="nv">$ </span><span class="nb">source</span> /etc/profile.d/clash.sh
<span class="nv">$ </span>proxy_on
</code></pre></div></div>
<p>检查服务端口</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>netstat <span class="nt">-tln</span> | <span class="nb">grep</span> <span class="nt">-E</span> <span class="s1">'9090|789.'</span>
tcp 0 0 127.0.0.1:9090 0.0.0.0:<span class="k">*</span> LISTEN
tcp6 0 0 :::7890 :::<span class="k">*</span> LISTEN
tcp6 0 0 :::7891 :::<span class="k">*</span> LISTEN
tcp6 0 0 :::7892 :::<span class="k">*</span> LISTEN
</code></pre></div></div>
<p>检查环境变量</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">env</span> | <span class="nb">grep</span> <span class="nt">-E</span> <span class="s1">'http_proxy|https_proxy'</span>
<span class="nv">http_proxy</span><span class="o">=</span>http://127.0.0.1:7890
<span class="nv">https_proxy</span><span class="o">=</span>http://127.0.0.1:7890
</code></pre></div></div>
<p>以上步鄹如果正常,说明服务clash程序启动成功,现在就可以体验高速下载github资源了。</p>
<h3 id="4-停止程序">4. 停止程序</h3>
<p>进入项目目录</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">cd </span>clash-for-linux
</code></pre></div></div>
<p>关闭服务</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>sh shutdown.sh
</code></pre></div></div>
<p>服务关闭成功,请执行以下命令关闭系统代理:proxy_off</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>proxy_off
</code></pre></div></div>
<table>
<tbody>
<tr>
<td>然后检查程序端口、进程以及环境变量http_proxy</td>
<td>https_proxy,若都没则说明服务正常关闭。</td>
</tr>
</tbody>
</table>
<h3 id="5-clash-dashboard">5. Clash Dashboard</h3>
<p>访问 Clash Dashboard 通过浏览器访问 start.sh 执行成功后输出的地址,例如:<code class="language-plaintext highlighter-rouge">http://192.168.0.1:9090/ui</code></p>
<p>登录管理界面 在API Base URL一栏中输入:<code class="language-plaintext highlighter-rouge">http://IP:9090</code> ,在<code class="language-plaintext highlighter-rouge">Secret(optional)</code>一栏中输入启动成功后输出的Secret。</p>
<p>点击Add并选择刚刚输入的管理界面地址,之后便可在浏览器上进行一些配置。</p>
<p>更多教程 此 Clash Dashboard 使用的是<a href="https://link.zhihu.com/?target=https%3A//github.com/haishanh/yacd">yacd</a>项目,详细使用方法请移步到yacd上查询。</p>
<h3 id="6-使用须知">6. 使用须知</h3>
<ul>
<li>此项目不提供任何订阅信息,请自行准备Clash订阅地址。</li>
<li>运行前请手动更改start.sh脚本中的URL变量值,否则无法正常运行。</li>
<li>当前在RHEL系列和Debian系列Linux系统中测试过,其他系列可能需要适当修改脚本。</li>
<li>支持 <code class="language-plaintext highlighter-rouge">x86_64/aarch64</code> 平台</li>
</ul>
<blockquote>
<p>作者:爱死亡机器人
链接:https://www.zhihu.com/question/585741578/answer/2906870859
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。</p>
</blockquote>
<p>参考资料:</p>
<ul>
<li>https://www.zhihu.com/question/585741578/answer/2913305080</li>
</ul>进击de豆子本文介绍linux环境clash代理使用物料库处理流程设计2023-04-01T00:00:00+08:002023-04-01T00:00:00+08:00https://beangogo.cn/2023/04/01/%E7%89%A9%E6%96%99%E5%BA%93%E5%A4%84%E7%90%86%E6%B5%81%E7%A8%8B%E8%AE%BE%E8%AE%A1<p>本文介绍一种物料处理通用流程数据库设计</p>
<h2 id="1数据库设计">1、数据库设计</h2>
<p><img src="http://beangogo.cn/assets/images/artcles/2023-04-01-物料库处理流程设计.assets/image-20230405190723801.png" alt="image-20230405190723801" /></p>
<p>navicat 模型原文件 : <a href="http://beangogo.cn/assets/images/artcles/2023-04-01-物料库处理流程设计.assets/matrial.ndm2">download</a></p>
<h2 id="2数据库设计说明">2、数据库设计说明</h2>
<p>设计说明:</p>
<ol>
<li>物料表中uuid唯一,同一个物料只允许存在一次</li>
<li>一个物料可能被多个流程处理</li>
</ol>
<p>查询说明:</p>
<p>查询一个物料被哪些流程处理过</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">select</span> <span class="o">*</span> <span class="k">from</span> <span class="n">common_matrial</span> <span class="n">a</span>
<span class="k">left</span> <span class="k">join</span> <span class="n">common_mps</span> <span class="n">b</span> <span class="k">on</span> <span class="n">a</span><span class="p">.</span><span class="n">id</span> <span class="o">=</span> <span class="n">b</span><span class="p">.</span><span class="n">matrial_id</span>
<span class="k">left</span> <span class="k">join</span> <span class="n">common_process</span> <span class="k">c</span> <span class="k">on</span> <span class="n">b</span><span class="p">.</span><span class="n">process_id</span> <span class="o">=</span> <span class="k">c</span><span class="p">.</span><span class="n">id</span>
<span class="k">where</span> <span class="n">a</span><span class="p">.</span><span class="n">uuid</span> <span class="o">=</span> <span class="nv">"xxxx"</span><span class="p">;</span>
</code></pre></div></div>
<p>附</p>
<p>数据库db-sql</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CREATE</span> <span class="k">TABLE</span> <span class="nv">`common_matrial`</span> <span class="p">(</span>
<span class="nv">`id`</span> <span class="nb">int</span> <span class="k">NOT</span> <span class="k">NULL</span> <span class="n">AUTO_INCREMENT</span> <span class="k">COMMENT</span> <span class="s1">'自增ID'</span><span class="p">,</span>
<span class="nv">`uuid`</span> <span class="nb">varchar</span><span class="p">(</span><span class="mi">255</span><span class="p">)</span> <span class="k">NULL</span> <span class="k">COMMENT</span> <span class="s1">'系统唯一ID'</span><span class="p">,</span>
<span class="nv">`source_id`</span> <span class="nb">varchar</span><span class="p">(</span><span class="mi">255</span><span class="p">)</span> <span class="k">NULL</span> <span class="k">COMMENT</span> <span class="s1">'三方ID'</span><span class="p">,</span>
<span class="nv">`status`</span> <span class="nb">int</span> <span class="k">NULL</span> <span class="k">COMMENT</span> <span class="s1">'物料状态 1-有效 2-无效'</span><span class="p">,</span>
<span class="nv">`detail`</span> <span class="nb">text</span> <span class="k">NULL</span> <span class="k">COMMENT</span> <span class="s1">'物料详情 可存放json字段'</span><span class="p">,</span>
<span class="nv">`create_time`</span> <span class="nb">int</span> <span class="k">NULL</span> <span class="k">COMMENT</span> <span class="s1">'创建时间'</span><span class="p">,</span>
<span class="nv">`update_time`</span> <span class="nb">int</span> <span class="k">NULL</span> <span class="k">COMMENT</span> <span class="s1">'更新时间'</span><span class="p">,</span>
<span class="k">PRIMARY</span> <span class="k">KEY</span> <span class="p">(</span><span class="nv">`id`</span><span class="p">)</span>
<span class="p">)</span> <span class="k">COMMENT</span> <span class="o">=</span> <span class="s1">'物料表'</span><span class="p">;</span>
<span class="k">CREATE</span> <span class="k">TABLE</span> <span class="nv">`common_mps`</span> <span class="p">(</span>
<span class="nv">`id`</span> <span class="nb">int</span> <span class="k">NOT</span> <span class="k">NULL</span> <span class="n">AUTO_INCREMENT</span> <span class="k">COMMENT</span> <span class="s1">'自增ID'</span><span class="p">,</span>
<span class="nv">`matrial_id`</span> <span class="nb">int</span> <span class="k">NOT</span> <span class="k">NULL</span> <span class="k">COMMENT</span> <span class="s1">'物料ID'</span><span class="p">,</span>
<span class="nv">`process_id`</span> <span class="nb">int</span> <span class="k">NULL</span> <span class="k">COMMENT</span> <span class="s1">'流程ID'</span><span class="p">,</span>
<span class="nv">`create_time`</span> <span class="nb">int</span> <span class="k">NULL</span> <span class="k">COMMENT</span> <span class="s1">'创建时间'</span><span class="p">,</span>
<span class="nv">`update_time`</span> <span class="nb">int</span> <span class="k">NULL</span> <span class="k">COMMENT</span> <span class="s1">'更新时间'</span><span class="p">,</span>
<span class="k">PRIMARY</span> <span class="k">KEY</span> <span class="p">(</span><span class="nv">`id`</span><span class="p">)</span>
<span class="p">)</span> <span class="k">COMMENT</span> <span class="o">=</span> <span class="s1">'物料-流程-关联表'</span><span class="p">;</span>
<span class="k">CREATE</span> <span class="k">TABLE</span> <span class="nv">`common_process`</span> <span class="p">(</span>
<span class="nv">`id`</span> <span class="nb">int</span> <span class="k">NOT</span> <span class="k">NULL</span> <span class="n">AUTO_INCREMENT</span> <span class="k">COMMENT</span> <span class="s1">'自增主键'</span><span class="p">,</span>
<span class="nv">`process_code`</span> <span class="nb">int</span> <span class="k">NULL</span> <span class="k">COMMENT</span> <span class="s1">'流程code'</span><span class="p">,</span>
<span class="nv">`process_name`</span> <span class="nb">varchar</span><span class="p">(</span><span class="mi">255</span><span class="p">)</span> <span class="k">NULL</span> <span class="k">COMMENT</span> <span class="s1">'流程名称'</span><span class="p">,</span>
<span class="nv">`status`</span> <span class="nb">int</span> <span class="k">NULL</span> <span class="k">COMMENT</span> <span class="s1">'状态 1-处理中 2-处理成功 3-处理失败'</span><span class="p">,</span>
<span class="nv">`create_time`</span> <span class="nb">int</span> <span class="k">NULL</span> <span class="k">COMMENT</span> <span class="s1">'创建时间'</span><span class="p">,</span>
<span class="nv">`update_time`</span> <span class="nb">int</span> <span class="k">NULL</span> <span class="k">COMMENT</span> <span class="s1">'更新时间'</span><span class="p">,</span>
<span class="k">PRIMARY</span> <span class="k">KEY</span> <span class="p">(</span><span class="nv">`id`</span><span class="p">)</span>
<span class="p">)</span> <span class="k">COMMENT</span> <span class="o">=</span> <span class="s1">'流程处理表'</span><span class="p">;</span>
<span class="k">ALTER</span> <span class="k">TABLE</span> <span class="nv">`common_mps`</span> <span class="k">ADD</span> <span class="k">CONSTRAINT</span> <span class="nv">`fk_common_mps_common_mps_1`</span> <span class="k">FOREIGN</span> <span class="k">KEY</span> <span class="p">(</span><span class="nv">`matrial_id`</span><span class="p">)</span> <span class="k">REFERENCES</span> <span class="nv">`common_matrial`</span> <span class="p">(</span><span class="nv">`id`</span><span class="p">);</span>
<span class="k">ALTER</span> <span class="k">TABLE</span> <span class="nv">`common_mps`</span> <span class="k">ADD</span> <span class="k">CONSTRAINT</span> <span class="nv">`fk_common_mps_common_mps_2`</span> <span class="k">FOREIGN</span> <span class="k">KEY</span> <span class="p">(</span><span class="nv">`process_id`</span><span class="p">)</span> <span class="k">REFERENCES</span> <span class="nv">`common_process`</span> <span class="p">(</span><span class="nv">`id`</span><span class="p">);</span>
</code></pre></div></div>进击de豆子本文介绍一种物料处理通用流程数据库设计leetcdoe题目合集2023-03-30T00:00:00+08:002023-03-30T00:00:00+08:00https://beangogo.cn/2023/03/30/leetcode<p>leetcode题目大合集</p>
<h3 id="频次排序">频次排序</h3>
<table>
<thead>
<tr>
<th>题目</th>
<th>难度</th>
<th>最近考察时间</th>
<th>频次</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="https://leetcode.cn/problems/longest-substring-without-repeating-characters">3. 无重复字符的最长子串</a></td>
<td>中等</td>
<td>2023-03-07</td>
<td>556.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/reverse-linked-list">206. 反转链表</a></td>
<td>容易</td>
<td>2023-02-24</td>
<td>536.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/lru-cache">146. LRU缓存机制</a></td>
<td>中等</td>
<td>2023-03-03</td>
<td>442.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/kth-largest-element-in-an-array">215. 数组中的第K个最大元素</a></td>
<td>中等</td>
<td>2023-02-13</td>
<td>382.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/reverse-nodes-in-k-group">25. K 个一组翻转链表</a></td>
<td>困难</td>
<td>2023-03-05</td>
<td>293.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/3sum">15. 三数之和</a></td>
<td>中等</td>
<td>2023-02-16</td>
<td>271.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/maximum-subarray">53. 最大子序和</a></td>
<td>容易</td>
<td>2023-03-05</td>
<td>240.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/sort-an-array">补充题4. 手撕快速排序</a></td>
<td>中等</td>
<td>2023-02-27</td>
<td>239.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/merge-two-sorted-lists">21. 合并两个有序链表</a></td>
<td>容易</td>
<td>2023-02-22</td>
<td>222.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/two-sum">1. 两数之和</a></td>
<td>容易</td>
<td>2023-03-03</td>
<td>214.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/binary-tree-level-order-traversal">102. 二叉树的层序遍历</a></td>
<td>中等</td>
<td>2022-09-22</td>
<td>202.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/search-in-rotated-sorted-array">33. 搜索旋转排序数组</a></td>
<td>中等</td>
<td>2023-03-08</td>
<td>196.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/valid-parentheses">20. 有效的括号</a></td>
<td>容易</td>
<td>2023-01-12</td>
<td>194.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/longest-palindromic-substring">5. 最长回文子串</a></td>
<td>中等</td>
<td>2023-03-06</td>
<td>193.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/best-time-to-buy-and-sell-stock">121. 买卖股票的最佳时机</a></td>
<td>容易</td>
<td>2023-01-13</td>
<td>192.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/number-of-islands">200. 岛屿数量</a></td>
<td>中等</td>
<td>2023-03-08</td>
<td>191.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/linked-list-cycle">141. 环形链表</a></td>
<td>容易</td>
<td>2023-02-26</td>
<td>191.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/merge-sorted-array">88. 合并两个有序数组</a></td>
<td>容易</td>
<td>2023-02-27</td>
<td>187.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/binary-tree-zigzag-level-order-traversal">103. 二叉树的锯齿形层次遍历</a></td>
<td>中等</td>
<td>2023-02-27</td>
<td>186.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree">236. 二叉树的最近公共祖先</a></td>
<td>中等</td>
<td>2023-02-13</td>
<td>185.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/permutations">46. 全排列</a></td>
<td>中等</td>
<td>2023-01-06</td>
<td>181.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/intersection-of-two-linked-lists">160. 相交链表</a></td>
<td>容易</td>
<td>2022-12-08</td>
<td>170.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/spiral-matrix">54. 螺旋矩阵</a></td>
<td>中等</td>
<td>2023-03-08</td>
<td>165.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/merge-k-sorted-lists">23. 合并K个排序链表</a></td>
<td>困难</td>
<td>2022-12-09</td>
<td>158.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/reverse-linked-list-ii">92. 反转链表 II</a></td>
<td>中等</td>
<td>2023-03-01</td>
<td>155.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/add-strings">415. 字符串相加</a></td>
<td>容易</td>
<td>2023-02-24</td>
<td>149.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/linked-list-cycle-ii">142. 环形链表 II</a></td>
<td>中等</td>
<td>2022-09-27</td>
<td>144.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/longest-increasing-subsequence">300. 最长上升子序列</a></td>
<td>中等</td>
<td>2023-03-06</td>
<td>140.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/trapping-rain-water">42. 接雨水</a></td>
<td>困难</td>
<td>2023-03-03</td>
<td>138.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/reorder-list">143. 重排链表</a></td>
<td>中等</td>
<td>2023-03-02</td>
<td>123.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/binary-tree-maximum-path-sum">124. 二叉树中的最大路径和</a></td>
<td>困难</td>
<td>2022-12-13</td>
<td>122.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/remove-nth-node-from-end-of-list">19. 删除链表的倒数第N个节点</a></td>
<td>中等</td>
<td>2023-03-08</td>
<td>116.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/binary-tree-inorder-traversal">94. 二叉树的中序遍历</a></td>
<td>容易</td>
<td>2023-02-23</td>
<td>116.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/edit-distance">72. 编辑距离</a></td>
<td>困难</td>
<td>2023-03-07</td>
<td>115.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/implement-queue-using-stacks">232. 用栈实现队列</a></td>
<td>容易</td>
<td>2023-02-24</td>
<td>115.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/binary-search">704. 二分查找</a></td>
<td>容易</td>
<td>2023-02-06</td>
<td>115.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/median-of-two-sorted-arrays">4. 寻找两个正序数组的中位数</a></td>
<td>困难</td>
<td>2023-03-04</td>
<td>114.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/binary-tree-right-side-view">199. 二叉树的右视图</a></td>
<td>中等</td>
<td>2022-12-01</td>
<td>111.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/merge-intervals">56. 合并区间</a></td>
<td>中等</td>
<td>2023-02-03</td>
<td>107.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/climbing-stairs">70. 爬楼梯</a></td>
<td>容易</td>
<td>2022-12-16</td>
<td>106.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/next-permutation">31. 下一个排列</a></td>
<td>中等</td>
<td>2023-02-16</td>
<td>105.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/sort-list">148. 排序链表</a></td>
<td>中等</td>
<td>2022-12-13</td>
<td>105.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/remove-duplicates-from-sorted-list-ii">82. 删除排序链表中的重复元素 II</a></td>
<td>中等</td>
<td>2022-11-29</td>
<td>104.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/longest-common-subsequence">1143. 最长公共子序列</a></td>
<td>中等</td>
<td>2023-02-27</td>
<td>103.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/sqrtx">69. x 的平方根</a></td>
<td>容易</td>
<td>2022-12-29</td>
<td>101.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/add-two-numbers">2. 两数相加</a></td>
<td>中等</td>
<td>2022-11-02</td>
<td>100.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/string-to-integer-atoi">8. 字符串转换整数 (atoi)</a></td>
<td>中等</td>
<td>2023-01-01</td>
<td>98.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/restore-ip-addresses">93. 复原IP地址</a></td>
<td>中等</td>
<td>2023-02-04</td>
<td>97.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/generate-parentheses">22. 括号生成</a></td>
<td>中等</td>
<td>2023-02-24</td>
<td>96.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/sliding-window-maximum">239. 滑动窗口最大值</a></td>
<td>困难</td>
<td>2022-11-15</td>
<td>93.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/first-missing-positive">41. 缺失的第一个正数</a></td>
<td>困难</td>
<td>2022-10-24</td>
<td>91.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof">剑指 Offer 22. 链表中倒数第k个节点</a></td>
<td>容易</td>
<td>2022-09-02</td>
<td>91.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/minimum-window-substring">76. 最小覆盖子串</a></td>
<td>困难</td>
<td>2022-12-22</td>
<td>80.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/construct-binary-tree-from-preorder-and-inorder-traversal">105. 从前序与中序遍历序列构造二叉树</a></td>
<td>中等</td>
<td>2022-10-10</td>
<td>80.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/reverse-words-in-a-string">151. 翻转字符串里的单词</a></td>
<td>中等</td>
<td>2023-02-09</td>
<td>79.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/coin-change">322. 零钱兑换</a></td>
<td>中等</td>
<td>2022-12-13</td>
<td>79.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/compare-version-numbers">165. 比较版本号</a></td>
<td>中等</td>
<td>2022-11-14</td>
<td>79.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/binary-tree-preorder-traversal">144. 二叉树的前序遍历</a></td>
<td>容易</td>
<td>2022-08-01</td>
<td>77.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/subsets">78. 子集</a></td>
<td>中等</td>
<td>2023-02-03</td>
<td>76.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/multiply-strings">43. 字符串相乘</a></td>
<td>中等</td>
<td>2023-01-13</td>
<td>76.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/min-stack">155. 最小栈</a></td>
<td>容易</td>
<td>2023-02-16</td>
<td>75.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/balanced-binary-tree">110. 平衡二叉树</a></td>
<td>容易</td>
<td>2022-12-27</td>
<td>75.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/maximum-depth-of-binary-tree">104. 二叉树的最大深度</a></td>
<td>容易</td>
<td>2023-03-08</td>
<td>74.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/longest-valid-parentheses">32. 最长有效括号</a></td>
<td>困难</td>
<td>2023-02-06</td>
<td>74.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/symmetric-tree">101. 对称二叉树</a></td>
<td>容易</td>
<td>2023-02-28</td>
<td>73.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/sum-root-to-leaf-numbers">129. 求根到叶子节点数字之和</a></td>
<td>中等</td>
<td>2023-02-04</td>
<td>73.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/diameter-of-binary-tree">543. 二叉树的直径</a></td>
<td>容易</td>
<td>2022-11-03</td>
<td>69.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/path-sum-ii">113. 路径总和 II</a></td>
<td>中等</td>
<td>2023-02-24</td>
<td>68.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/validate-binary-search-tree">98. 验证二叉搜索树</a></td>
<td>中等</td>
<td>2022-11-10</td>
<td>68.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/minimum-path-sum">64. 最小路径和</a></td>
<td>中等</td>
<td>2022-11-02</td>
<td>68.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/implement-rand10-using-rand7">470. 用 Rand7() 实现 Rand10()</a></td>
<td>中等</td>
<td>2022-11-16</td>
<td>67.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/rotate-image">48. 旋转图像</a></td>
<td>中等</td>
<td>2023-03-05</td>
<td>64.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/path-sum">112. 路径总和</a></td>
<td>容易</td>
<td>2023-02-07</td>
<td>64.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/combination-sum">39. 组合总和</a></td>
<td>中等</td>
<td>2023-02-09</td>
<td>63.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/palindrome-linked-list">234. 回文链表</a></td>
<td>容易</td>
<td>2023-02-22</td>
<td>62.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/majority-element">169. 多数元素</a></td>
<td>容易</td>
<td>2022-05-25</td>
<td>60.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/decode-string">394. 字符串解码</a></td>
<td>中等</td>
<td>2023-02-21</td>
<td>58.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/maximal-square">221. 最大正方形</a></td>
<td>中等</td>
<td>2023-02-13</td>
<td>58.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/maximum-length-of-repeated-subarray">718. 最长重复子数组</a></td>
<td>中等</td>
<td>2022-09-23</td>
<td>58.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/search-a-2d-matrix-ii">240. 搜索二维矩阵 II</a></td>
<td>中等</td>
<td>2023-02-02</td>
<td>57.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/longest-common-prefix">14. 最长公共前缀</a></td>
<td>容易</td>
<td>2022-12-22</td>
<td>56.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array">34. 在排序数组中查找元素的第一个和最后一个位置</a></td>
<td>中等</td>
<td>2022-11-24</td>
<td>56.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/invert-binary-tree">226. 翻转二叉树</a></td>
<td>容易</td>
<td>2022-09-12</td>
<td>56.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/longest-consecutive-sequence">128. 最长连续序列</a></td>
<td>中等</td>
<td>2022-12-02</td>
<td>53.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/find-peak-element">162. 寻找峰值</a></td>
<td>中等</td>
<td>2022-07-29</td>
<td>53.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/basic-calculator-ii">227. 基本计算器 II</a></td>
<td>中等</td>
<td>2023-02-27</td>
<td>52.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/unique-paths">62. 不同路径</a></td>
<td>中等</td>
<td>2023-01-18</td>
<td>52.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/max-area-of-island">695. 岛屿的最大面积</a></td>
<td>中等</td>
<td>2022-11-21</td>
<td>52.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/remove-duplicates-from-sorted-list">83. 删除排序链表中的重复元素</a></td>
<td>容易</td>
<td>2022-07-22</td>
<td>51.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/house-robber">198. 打家劫舍</a></td>
<td>中等</td>
<td>2023-03-08</td>
<td>50.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/sort-an-array">补充题6. 手撕堆排序</a></td>
<td>中等</td>
<td>2023-02-23</td>
<td>50.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/maximum-width-of-binary-tree">662. 二叉树最大宽度</a></td>
<td>中等</td>
<td>2023-03-08</td>
<td>49.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/maximum-product-subarray">152. 乘积最大子数组</a></td>
<td>中等</td>
<td>2023-02-15</td>
<td>49.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii">122. 买卖股票的最佳时机 II</a></td>
<td>容易</td>
<td>2023-01-01</td>
<td>49.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/find-minimum-in-rotated-sorted-array">153. 寻找旋转排序数组中的最小值</a></td>
<td>中等</td>
<td>2023-02-27</td>
<td>48.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/largest-number">179. 最大数</a></td>
<td>中等</td>
<td>2022-12-28</td>
<td>48.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/swap-nodes-in-pairs">24. 两两交换链表中的节点</a></td>
<td>中等</td>
<td>2022-07-28</td>
<td>48.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/move-zeroes">283. 移动零</a></td>
<td>容易</td>
<td>2022-09-19</td>
<td>47.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/copy-list-with-random-pointer">138. 复制带随机指针的链表</a></td>
<td>中等</td>
<td>2022-09-13</td>
<td>47.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/validate-ip-address">468. 验证IP地址</a></td>
<td>中等</td>
<td>2023-02-24</td>
<td>46.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/subarray-sum-equals-k">560. 和为K的子数组</a></td>
<td>中等</td>
<td>2023-02-16</td>
<td>46.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/word-break">139. 单词拆分</a></td>
<td>中等</td>
<td>2022-12-09</td>
<td>46.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/serialize-and-deserialize-binary-tree">297. 二叉树的序列化与反序列化</a></td>
<td>困难</td>
<td>2022-10-12</td>
<td>46.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/single-number">136. 只出现一次的数字</a></td>
<td>容易</td>
<td>2023-03-07</td>
<td>44.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/minimum-size-subarray-sum">209. 长度最小的子数组</a></td>
<td>中等</td>
<td>2022-11-01</td>
<td>44.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/basic-calculator">224. 基本计算器</a></td>
<td>困难</td>
<td>2023-02-02</td>
<td>42.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/yong-liang-ge-zhan-shi-xian-dui-lie-lcof">剑指 Offer 09. 用两个栈实现队列</a></td>
<td>容易</td>
<td>2023-01-24</td>
<td>42.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/daily-temperatures">739. 每日温度</a></td>
<td>中等</td>
<td>2023-02-13</td>
<td>41.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/diagonal-traverse">498. 对角线遍历</a></td>
<td>中等</td>
<td>2022-10-13</td>
<td>41.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/course-schedule">207. 课程表</a></td>
<td>中等</td>
<td>2023-03-06</td>
<td>40.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/lfu-cache">460. LFU缓存</a></td>
<td>困难</td>
<td>2023-01-09</td>
<td>40.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/remove-k-digits">402. 移掉K位数字</a></td>
<td>中等</td>
<td>2022-10-15</td>
<td>40.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/er-cha-sou-suo-shu-yu-shuang-xiang-lian-biao-lcof">剑指 Offer 36. 二叉搜索树与双向链表</a></td>
<td>中等</td>
<td>2022-09-14</td>
<td>40.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/sort-an-array">补充题5. 手撕归并排序</a></td>
<td>中等</td>
<td>2022-08-15</td>
<td>40.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/check-completeness-of-a-binary-tree">958. 二叉树的完全性检验</a></td>
<td>中等</td>
<td>2023-02-14</td>
<td>39.</td>
</tr>
<tr>
<td><a href="https://mp.weixin.qq.com/s/pCRscwKqQdYYN7M1Sia7xA">补充题23. 检测循环依赖</a></td>
<td>中等</td>
<td>2022-11-29</td>
<td>38.</td>
</tr>
<tr>
<td><a href="https://mp.weixin.qq.com/s/0WVa2wIAeG0nYnVndZiEXQ">补充题1. 排序奇升偶降链表</a></td>
<td>中等</td>
<td>2022-09-26</td>
<td>38.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/rotate-list">61. 旋转链表</a></td>
<td>中等</td>
<td>2023-03-03</td>
<td>37.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/er-cha-sou-suo-shu-de-di-kda-jie-dian-lcof">剑指 Offer 54. 二叉搜索树的第k大节点</a></td>
<td>容易</td>
<td>2022-05-26</td>
<td>37.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/container-with-most-water">11. 盛最多水的容器</a></td>
<td>中等</td>
<td>2022-09-28</td>
<td>36.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/qing-wa-tiao-tai-jie-wen-ti-lcof">剑指 Offer 10- II. 青蛙跳台阶问题</a></td>
<td>容易</td>
<td>2022-09-19</td>
<td>36.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/word-search">79. 单词搜索</a></td>
<td>中等</td>
<td>2022-08-12</td>
<td>36.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/permutations-ii">47. 全排列 II</a></td>
<td>中等</td>
<td>2023-03-08</td>
<td>35.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/jump-game">55. 跳跃游戏</a></td>
<td>中等</td>
<td>2023-01-16</td>
<td>35.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/combination-sum-ii">40. 组合总和 II</a></td>
<td>中等</td>
<td>2022-11-23</td>
<td>35.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/lian-xu-zi-shu-zu-de-zui-da-he-lcof">剑指 Offer 42. 连续子数组的最大和</a></td>
<td>容易</td>
<td>2023-02-15</td>
<td>34.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/powx-n">50. Pow(x, n)</a></td>
<td>中等</td>
<td>2023-01-12</td>
<td>34.</td>
</tr>
<tr>
<td><a href="https://mp.weixin.qq.com/s/NZPaFsFrTybO3K3s7p7EVg">补充题2. 圆环回原点问题</a></td>
<td>中等</td>
<td>2022-11-30</td>
<td>34.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/spiral-matrix-ii">59. 螺旋矩阵 II</a></td>
<td>中等</td>
<td>2022-11-15</td>
<td>34.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/coin-change-2">518. 零钱兑换 II</a></td>
<td>中等</td>
<td>2022-10-27</td>
<td>34.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/binary-tree-postorder-traversal">145. 二叉树的后序遍历</a></td>
<td>中等</td>
<td>2022-09-01</td>
<td>34.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/shu-zu-zhong-de-ni-xu-dui-lcof">剑指 Offer 51. 数组中的逆序对</a></td>
<td>困难</td>
<td>2022-08-23</td>
<td>34.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/search-a-2d-matrix">74. 搜索二维矩阵</a></td>
<td>中等</td>
<td>2022-10-30</td>
<td>33.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/remove-duplicates-from-sorted-array">26. 删除排序数组中的重复项</a></td>
<td>容易</td>
<td>2022-10-18</td>
<td>33.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/fei-bo-na-qi-shu-lie-lcof">剑指 Offer 10- I. 斐波那契数列</a></td>
<td>容易</td>
<td>2022-07-20</td>
<td>33.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/reverse-integer">7. 整数反转</a></td>
<td>容易</td>
<td>2023-02-06</td>
<td>32.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iii">123. 买卖股票的最佳时机 III</a></td>
<td>困难</td>
<td>2023-01-03</td>
<td>32.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/zui-xiao-de-kge-shu-lcof">剑指 Offer 40. 最小的k个数</a></td>
<td>容易</td>
<td>2022-09-16</td>
<td>32.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/k-th-smallest-in-lexicographical-order">440. 字典序的第K小数字</a></td>
<td>困难</td>
<td>2023-02-17</td>
<td>31.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/delete-node-in-a-bst">450. 删除二叉搜索树中的节点</a></td>
<td>中等</td>
<td>2022-09-27</td>
<td>31.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/sort-colors">75. 颜色分类</a></td>
<td>中等</td>
<td>2023-02-27</td>
<td>30.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/diao-zheng-shu-zu-shun-xu-shi-qi-shu-wei-yu-ou-shu-qian-mian-lcof">剑指 Offer 21. 调整数组顺序使奇数位于偶数前面</a></td>
<td>容易</td>
<td>2022-09-27</td>
<td>30.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/kth-smallest-element-in-a-bst">230. 二叉搜索树中第K小的元素</a></td>
<td>中等</td>
<td>2022-09-26</td>
<td>30.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/implement-stack-using-queues">225. 用队列实现栈</a></td>
<td>容易</td>
<td>2022-10-30</td>
<td>29.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/candy">135. 分发糖果</a></td>
<td>困难</td>
<td>2022-08-25</td>
<td>29.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/yuan-quan-zhong-zui-hou-sheng-xia-de-shu-zi-lcof">剑指 Offer 62. 圆圈中最后剩下的数字</a></td>
<td>容易</td>
<td>2022-05-04</td>
<td>29.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/longest-increasing-path-in-a-matrix">329. 矩阵中的最长递增路径</a></td>
<td>困难</td>
<td>2022-08-31</td>
<td>28.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/er-wei-shu-zu-zhong-de-cha-zhao-lcof">剑指 Offer 04. 二维数组中的查找</a></td>
<td>容易</td>
<td>2022-11-09</td>
<td>27.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/odd-even-linked-list">328. 奇偶链表</a></td>
<td>中等</td>
<td>2022-10-09</td>
<td>27.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/decode-ways">91. 解码方法</a></td>
<td>中等</td>
<td>2022-09-22</td>
<td>27.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/rotate-array">189. 轮转数组</a></td>
<td>中等</td>
<td>2023-03-01</td>
<td>26.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/jump-game-ii">45. 跳跃游戏 II</a></td>
<td>中等</td>
<td>2023-01-17</td>
<td>26.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/subtree-of-another-tree">572. 另一个树的子树</a></td>
<td>容易</td>
<td>2022-08-22</td>
<td>26.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/shuffle-an-array">384. 打乱数组</a></td>
<td>中等</td>
<td>2022-07-07</td>
<td>26.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/valid-palindrome">125. 验证回文串</a></td>
<td>容易</td>
<td>2022-05-17</td>
<td>26.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/shu-de-zi-jie-gou-lcof">剑指 Offer 26. 树的子结构</a></td>
<td>中等</td>
<td>2023-01-06</td>
<td>25.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/implement-trie-prefix-tree">208. 实现 Trie (前缀树)</a></td>
<td>中等</td>
<td>2022-11-15</td>
<td>25.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/palindrome-number">9. 回文数</a></td>
<td>容易</td>
<td>2022-11-10</td>
<td>25.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/add-two-numbers-ii">445. 两数相加 II</a></td>
<td>中等</td>
<td>2022-09-20</td>
<td>25.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/er-cha-shu-de-jing-xiang-lcof">剑指 Offer 27. 二叉树的镜像</a></td>
<td>容易</td>
<td>2022-04-19</td>
<td>25.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/regular-expression-matching">10. 正则表达式匹配</a></td>
<td>困难</td>
<td>2023-02-25</td>
<td>24.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/find-the-duplicate-number">287. 寻找重复数</a></td>
<td>中等</td>
<td>2022-11-15</td>
<td>24.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/find-median-from-data-stream">295. 数据流的中位数</a></td>
<td>困难</td>
<td>2022-09-03</td>
<td>24.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/flatten-binary-tree-to-linked-list">114. 二叉树展开为链表</a></td>
<td>中等</td>
<td>2022-08-02</td>
<td>24.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/shun-shi-zhen-da-yin-ju-zhen-lcof">剑指 Offer 29. 顺时针打印矩阵</a></td>
<td>容易</td>
<td>2023-02-24</td>
<td>23.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/unique-binary-search-trees">96. 不同的二叉搜索树</a></td>
<td>中等</td>
<td>2022-12-02</td>
<td>23.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/3sum-closest">16. 最接近的三数之和</a></td>
<td>中等</td>
<td>2022-09-29</td>
<td>23.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/house-robber-ii">213. 打家劫舍 II</a></td>
<td>中等</td>
<td>2022-08-31</td>
<td>23.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/intersection-of-two-arrays">349. 两个数组的交集</a></td>
<td>容易</td>
<td>2023-01-27</td>
<td>22.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/triangle">120. 三角形最小路径和</a></td>
<td>中等</td>
<td>2022-10-13</td>
<td>22.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/construct-binary-tree-from-inorder-and-postorder-traversal">106. 从中序与后序遍历序列构造二叉树</a></td>
<td>中等</td>
<td>2022-08-04</td>
<td>22.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/super-egg-drop">887. 鸡蛋掉落</a></td>
<td>困难</td>
<td>2023-01-16</td>
<td>21.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/nth-digit">400. 第N个数字</a></td>
<td>中等</td>
<td>2022-11-11</td>
<td>21.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/excel-sheet-column-title">168. Excel表列名称</a></td>
<td>容易</td>
<td>2023-02-27</td>
<td>20.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/bu-ke-pai-zhong-de-shun-zi-lcof">剑指 Offer 61. 扑克牌中的顺子</a></td>
<td>容易</td>
<td>2022-11-30</td>
<td>20.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/sum-lists-lcci">面试题 02.05. 链表求和</a></td>
<td>中等</td>
<td>2022-09-30</td>
<td>20.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/valid-parenthesis-string">678. 有效的括号字符串</a></td>
<td>中等</td>
<td>2022-09-24</td>
<td>20.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/liang-ge-lian-biao-de-di-yi-ge-gong-gong-jie-dian-lcof">剑指 Offer 52. 两个链表的第一个公共节点</a></td>
<td>容易</td>
<td>2022-09-07</td>
<td>20.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/top-k-frequent-elements">347. 前 K 个高频元素</a></td>
<td>中等</td>
<td>2022-08-29</td>
<td>20.</td>
</tr>
<tr>
<td><a href="https://mp.weixin.qq.com/s/XcKQwnwCh5nZsz-DLHJwzQ">补充题9. 36进制加法</a></td>
<td>中等</td>
<td>2022-03-23</td>
<td>20.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/24-game">679. 24 点游戏</a></td>
<td>困难</td>
<td>2023-02-28</td>
<td>19.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/gas-station">134. 加油站</a></td>
<td>中等</td>
<td>2023-02-26</td>
<td>19.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/max-consecutive-ones-iii">1004. 最大连续1的个数 III</a></td>
<td>中等</td>
<td>2023-01-18</td>
<td>19.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/valid-triangle-number">611. 有效三角形的个数</a></td>
<td>中等</td>
<td>2022-12-21</td>
<td>19.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/shu-zu-zhong-chu-xian-ci-shu-chao-guo-yi-ban-de-shu-zi-lcof">剑指 Offer 39. 数组中出现次数超过一半的数字</a></td>
<td>容易</td>
<td>2022-10-14</td>
<td>19.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/number-of-longest-increasing-subsequence">673. 最长递增子序列的个数</a></td>
<td>中等</td>
<td>2022-08-04</td>
<td>19.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/simplify-path">71. 简化路径</a></td>
<td>中等</td>
<td>2022-07-14</td>
<td>19.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/find-all-duplicates-in-an-array">442. 数组中重复的数据</a></td>
<td>中等</td>
<td>2022-07-11</td>
<td>19.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/fibonacci-number">509. 斐波那契数</a></td>
<td>容易</td>
<td>2022-07-05</td>
<td>19.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/remove-all-adjacent-duplicates-in-string">1047. 删除字符串中的所有相邻重复项</a></td>
<td>容易</td>
<td>2022-03-31</td>
<td>19.</td>
</tr>
<tr>
<td>补充题14. 阿拉伯数字转中文数字</td>
<td>中等</td>
<td>2023-01-07</td>
<td>18.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/er-cha-shu-zhong-he-wei-mou-yi-zhi-de-lu-jing-lcof">剑指 Offer 34. 二叉树中和为某一值的路径</a></td>
<td>中等</td>
<td>2022-09-02</td>
<td>18.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/zai-pai-xu-shu-zu-zhong-cha-zhao-shu-zi-lcof">剑指 Offer 53 - I. 在排序数组中查找数字 I</a></td>
<td>容易</td>
<td>2022-08-08</td>
<td>18.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/partition-list">86. 分隔链表</a></td>
<td>中等</td>
<td>2022-07-24</td>
<td>18.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/course-schedule-ii">210. 课程表 II</a></td>
<td>中等</td>
<td>2023-03-06</td>
<td>17.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/valid-anagram">242. 有效的字母异位词</a></td>
<td>容易</td>
<td>2023-01-27</td>
<td>17.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/repeated-substring-pattern">459. 重复的子字符串</a></td>
<td>容易</td>
<td>2022-12-31</td>
<td>17.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/minimum-depth-of-binary-tree">111. 二叉树的最小深度</a></td>
<td>容易</td>
<td>2022-11-03</td>
<td>17.</td>
</tr>
<tr>
<td><a href="https://leetcode.cn/problems/next-greater-element-iii">556. 下一个更大元素 III</a></td>
<td>中等</td>
<td>2022-11-03</td>
<td>17.</td>
</tr>
</tbody>
</table>进击de豆子leetcode题目大合集python操作csv-批量生成sql2023-03-19T00:00:00+08:002023-03-19T00:00:00+08:00https://beangogo.cn/2023/03/19/sql%E7%94%9F%E6%88%90%E5%99%A8<h2 id="python操作csv文件">python操作csv文件</h2>
<p>参考资料</p>
<ul>
<li><a href="https://www.cnblogs.com/unnameable/p/7366437.html">Python对于CSV文件的读取与写入</a></li>
<li><a href="https://www.cnblogs.com/shengulong/p/7097869.html">python读写csv时中文乱码问题解决办法</a></li>
<li><a href="https://www.cnblogs.com/xxby/p/5571620.html">python 字符串格式化</a></li>
</ul>
<p>需要读取操作的文件:test_data.csv</p>
<pre><code class="language-csv">'name','age'
'tao','12'
'hong','13'
'明','15'
</code></pre>
<p>第一次读测试:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#encoding:utf-8
</span><span class="kn">import</span> <span class="nn">csv</span>
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">'__main__'</span><span class="p">:</span>
<span class="n">csv_file</span> <span class="o">=</span> <span class="n">csv</span><span class="p">.</span><span class="n">reader</span><span class="p">(</span><span class="nb">open</span><span class="p">(</span><span class="s">'test_data.csv'</span><span class="p">,</span><span class="s">'r'</span><span class="p">))</span>
<span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">csv_file</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="n">line</span><span class="p">)</span>
</code></pre></div></div>
<p><strong>测试结果</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>D:\Anaconda\python.exe E:/projects/python/python_deoms/CSV-demo/CSV_Demo.py
Traceback (most recent call last):
File "E:/projects/python/python_deoms/CSV-demo/CSV_Demo.py", line 6, in <module>
for line in csv_file:
UnicodeDecodeError: 'gbk' codec can't decode byte 0x8e in position 42: illegal multibyte sequence
</code></pre></div></div>
<p>我们可以看到主要的报错信息是编码错误,我们只需要解决的编码问题即可,详见第二次测试</p>
<p>第二次读测试:
添加了codes模块</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">#encoding:utf-8
</span><span class="kn">import</span> <span class="nn">csv</span>
<span class="kn">import</span> <span class="nn">codecs</span>
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">'__main__'</span><span class="p">:</span>
<span class="n">csv_file</span> <span class="o">=</span> <span class="n">csv</span><span class="p">.</span><span class="n">reader</span><span class="p">(</span><span class="n">codecs</span><span class="p">.</span><span class="nb">open</span><span class="p">(</span><span class="s">'test_data.csv'</span><span class="p">,</span><span class="s">'r'</span><span class="p">,</span><span class="s">'utf-8'</span><span class="p">))</span>
<span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">csv_file</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="n">line</span><span class="p">)</span>
</code></pre></div></div>
<p><strong>测试结果</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>D:\Anaconda\python.exe E:/projects/python/python_deoms/CSV-demo/CSV_Demo.py
["'name'", "'age'"]
["'tao'", "'12'"]
["'hong'", "'13'"]
["'明'", "'15'"]
</code></pre></div></div>
<p>根据结果,我们看到,我们成功读出了csv中的内容</p>
<p>关于python中的format字符串格式化</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">tp1</span> <span class="o">=</span> <span class="s">"i am {}, age {}, {}"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="s">"seven"</span><span class="p">,</span> <span class="mi">18</span><span class="p">,</span> <span class="s">'alex'</span><span class="p">)</span>
<span class="n">tp2</span> <span class="o">=</span> <span class="s">"i am {}, age {}, {}"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="o">*</span><span class="p">[</span><span class="s">"seven"</span><span class="p">,</span> <span class="mi">18</span><span class="p">,</span> <span class="s">'alex'</span><span class="p">])</span>
<span class="n">tp3</span> <span class="o">=</span> <span class="s">"i am {0}, age {1}, really {0}"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="s">"seven"</span><span class="p">,</span> <span class="mi">18</span><span class="p">)</span>
<span class="n">tp4</span> <span class="o">=</span> <span class="s">"i am {0}, age {1}, really {0}"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="o">*</span><span class="p">[</span><span class="s">"seven"</span><span class="p">,</span> <span class="mi">18</span><span class="p">])</span>
<span class="n">tp5</span> <span class="o">=</span> <span class="s">"i am {name}, age {age}, really {name}"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s">"seven"</span><span class="p">,</span> <span class="n">age</span><span class="o">=</span><span class="mi">18</span><span class="p">)</span>
<span class="n">tp6</span> <span class="o">=</span> <span class="s">"i am {name}, age {age}, really {name}"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="o">**</span><span class="p">{</span><span class="s">"name"</span><span class="p">:</span> <span class="s">"seven"</span><span class="p">,</span> <span class="s">"age"</span><span class="p">:</span> <span class="mi">18</span><span class="p">})</span>
<span class="n">tp7</span> <span class="o">=</span> <span class="s">"i am {0[0]}, age {0[1]}, really {0[2]}"</span><span class="p">.</span><span class="nb">format</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">],</span> <span class="p">[</span><span class="mi">11</span><span class="p">,</span> <span class="mi">22</span><span class="p">,</span> <span class="mi">33</span><span class="p">])</span>
<span class="n">tp8</span> <span class="o">=</span> <span class="s">"i am {:s}, age {:d}, money {:f}"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="s">"seven"</span><span class="p">,</span> <span class="mi">18</span><span class="p">,</span> <span class="mf">88888.1</span><span class="p">)</span>
<span class="n">tp9</span> <span class="o">=</span> <span class="s">"i am {:s}, age {:d}"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="o">*</span><span class="p">[</span><span class="s">"seven"</span><span class="p">,</span> <span class="mi">18</span><span class="p">])</span>
<span class="n">tp10</span> <span class="o">=</span> <span class="s">"i am {name:s}, age {age:d}"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">name</span><span class="o">=</span><span class="s">"seven"</span><span class="p">,</span> <span class="n">age</span><span class="o">=</span><span class="mi">18</span><span class="p">)</span>
<span class="n">tp11</span> <span class="o">=</span> <span class="s">"i am {name:s}, age {age:d}"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="o">**</span><span class="p">{</span><span class="s">"name"</span><span class="p">:</span> <span class="s">"seven"</span><span class="p">,</span> <span class="s">"age"</span><span class="p">:</span> <span class="mi">18</span><span class="p">})</span>
</code></pre></div></div>进击de豆子python操作csv文件golang版protoc安装及使用2023-02-23T00:00:00+08:002023-02-23T00:00:00+08:00https://beangogo.cn/2023/02/23/protoc-install<p>要生成 gRPC 代码,需要安装 protoc 工具和 golang 的 protoc 插件。</p>
<p>首先安装 protoc 工具。可以从 protobuf releases 页面 下载相应平台的预编译二进制文件,或者从包管理器中安装。</p>
<p>安装 golang 的 protoc 插件。</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>go install google.golang.org/protobuf/cmd/protoc-gen-go
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc
</code></pre></div></div>
<p>这里使用了 go install 命令来安装插件。如果你没有将 $GOPATH/bin 添加到 $PATH 环境变量中,可能需要手动添加。</p>
<p>安装完成后,可以使用以下命令生成 gRPC 代码:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>protoc --go_out=. --go-grpc_out=. your_proto_file.proto
</code></pre></div></div>
<p>其中 your_proto_file.proto 是你的 proto 文件名。执行以上命令后,会在当前目录下生成对应的 gRPC 代码文件</p>进击de豆子要生成 gRPC 代码,需要安装 protoc 工具和 golang 的 protoc 插件。安卓手机变身linux服务器2023-01-01T00:00:00+08:002023-01-01T00:00:00+08:00https://beangogo.cn/2023/01/01/android-linux<h2 id="前言分析">前言分析</h2>
<p>主要目的:把安卓手机变成随身服务器,变身为个人随时工作站,主要使用的相关功能</p>
<ul>
<li>能够使用相关开发语言:python3,go</li>
<li>安装code-server,使用web版本随时在局域网内通过vscode进行编程工作</li>
</ul>
<p>优势</p>
<ul>
<li>取代云服务器,满足随时编码需求</li>
<li>使用vpn更方便,只要手机能使用vpn即可</li>
</ul>
<p>劣势</p>
<p>稳定性可能较差,及数据存储可能不是很稳定,不建议做大量的数据存储使用。</p>
<p>数据及时做备份传输,备份方案后期可研究下。</p>
<h2 id="方式1--userland">方式1 : userLAnd</h2>
<p>相关命令</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># ubuntu相关组件相关命令</span>
<span class="c"># 登录</span>
ssh username@host <span class="nt">-p</span> 2022 <span class="c"># userLAnd 默认的端口是2022</span>
<span class="nb">sudo </span>apt update <span class="c"># 更新源</span>
<span class="nb">sudo </span>apt <span class="nb">install </span>vim <span class="nt">-y</span> <span class="c"># 安装vim</span>
<span class="nb">sudo </span>apt <span class="nb">install </span>net-tools <span class="nt">-y</span> <span class="c"># 安装网络工具,以使用ifconfig命令</span>
<span class="nb">sudo </span>apt <span class="nb">install </span>inetutils-ping <span class="nt">-y</span> <span class="c"># 安装ping工具</span>
<span class="nb">sudo </span>apt <span class="nb">install </span>git <span class="nt">-y</span> <span class="c"># 安装配置git</span>
<span class="nb">sudo </span>apt <span class="nb">install </span>redis-server <span class="nt">-y</span> <span class="c"># 安装redis</span>
<span class="nb">sudo </span>apt <span class="nb">install </span>python3 <span class="nt">-y</span>
<span class="nb">sudo </span>apt <span class="nb">install </span>pip <span class="nt">-y</span>
curl <span class="nt">-fsSL</span> https://code-server.dev/install.sh | sh <span class="c"># 安装vscode-server</span>
</code></pre></div></div>
<h2 id="方式2termux--anlinux">方式2:termux + AnLinux</h2>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># termux centos 相关组件安装命令</span>
ssh Linux@host <span class="nt">-p</span> 8022 <span class="c"># termux 默认的端口是8022,username 默认为Linux</span>
<span class="c"># 在termux中执行的相关命令</span>
pkg <span class="nb">install </span>x11-repo
pkg <span class="nb">install </span>root-repo
<span class="c"># 在linux中执行的相关命令(选用的是ubuntu),相关安装命令可参考上面</span>
<span class="c"># ------文件互通------</span>
<span class="c"># 方式1:修改start-ununtu.sh,同时在该文件夹下创建一个映射文件夹,用来互传文件</span>
<span class="c"># 方式2:可以访问/sdcard,用来直接访问系统相关文件夹</span>
<span class="c"># 方式3:手机端在文件系统中访问Android>data,进入安卓存储框架,在右侧中通过此方式访问文档</span>
</code></pre></div></div>
<p><img src="http://beangogo.cn/assets/images/artcles/2023-01-01-android-linux.assets/image-20230101213524460.png" alt="image-20230101213524460" /></p>
<p><img src="http://beangogo.cn/assets/images/artcles/2023-01-01-android-linux.assets/image-20230101214525768.png" alt="image-20230101214525768" style="zoom:50%;" /></p>
<p><img src="http://beangogo.cn/assets/images/artcles/2023-01-01-android-linux.assets/image-20230101214728481.png" alt="image-20230101214728481" style="zoom:50%;" /></p>
<h2 id="方式3-aidlux">方式3 :AidLux</h2>
<p>还没尝试过,后期有时间的话可以用下此方案,因为包比较大,所有之前的方案优先选的轻量级方案,且aidlux对旧手机可能不太友好</p>
<ul>
<li>https://aidlux.com/</li>
<li>https://github.com/aidlearning/AidLearning-Framework</li>
</ul>
<h2 id="相关未解决问题">相关未解决问题</h2>
<p>主要为使用userLAnd时出现的问题</p>
<h3 id="1-无法启用docker服务">1. 无法启用docker服务</h3>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 问题复现</span>
tao@localhost:~<span class="nv">$ </span><span class="nb">sudo </span>service docker start
/etc/init.d/docker: 96: <span class="nb">ulimit</span>: error setting limit <span class="o">(</span>Operation not permitted<span class="o">)</span>
<span class="c"># 问题追踪</span>
对于96行报错的是:ulimit <span class="nt">-n</span> 1048576,尝试直接执行相关命令进行验证
tao@localhost:~<span class="nv">$ </span><span class="nb">sudo ulimit</span> <span class="nt">-n</span> 1048576
bash: line 0: <span class="nb">ulimit</span>: open files: cannot modify limit: Operation not permitted
<span class="c"># 问题修复(TODO)</span>
目前该问题暂时先不想修复,因为暂时用不到docker,后期尝试【直接使用root账号创建资源】将账号统一化,看下该问题能否修复
</code></pre></div></div>
<h2 id="参考资料">参考资料</h2>
<ul>
<li>
<p><a href="https://yeasy.gitbook.io/docker_practice/install/ubuntu">Ubuntu安装docker</a></p>
</li>
<li>
<p><a href="https://zhuanlan.zhihu.com/p/95865982">极致安卓之—Termux安装完整版Linux</a></p>
</li>
<li>
<p><a href="https://github.com/CypherpunkArmory/UserLAnd/wiki/Importing-and-exporting-files-in-UserLAnd">Importing and exporting files in UserLAnd</a></p>
</li>
</ul>进击de豆子前言分析golang-atomic详解2022-07-13T00:00:00+08:002022-07-13T00:00:00+08:00https://beangogo.cn/2022/07/13/go-atomic<p>详解atomic相关原理</p>
<p>参考资料:</p>
<ul>
<li><a href="https://blog.haohtml.com/archives/25881">Golang中的CAS原子操作 和 锁</a></li>
</ul>进击de豆子详解atomic相关原理golang-sync包2022-07-03T00:00:00+08:002022-07-03T00:00:00+08:00https://beangogo.cn/2022/07/03/go-sync%E5%8C%85<p>快速了解go-sync包相关知识</p>
<h2 id="1-syncmap">1. sync.map</h2>
<h3 id="11-基本使用">1.1 基本使用</h3>
<p>Go 语言原生 map 并不是线程安全的,对它进行并发读写操作的时候,需要加锁。而 <code class="language-plaintext highlighter-rouge">sync.map</code> 则是一种并发安全的 map,在 Go 1.9 引入</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">package</span> <span class="n">main</span>
<span class="k">import</span> <span class="p">(</span>
<span class="s">"fmt"</span>
<span class="s">"sync"</span>
<span class="p">)</span>
<span class="k">func</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
<span class="k">var</span> <span class="n">m</span> <span class="n">sync</span><span class="o">.</span><span class="n">Map</span>
<span class="c">// 1. 写入</span>
<span class="n">m</span><span class="o">.</span><span class="n">Store</span><span class="p">(</span><span class="s">"qcrao"</span><span class="p">,</span> <span class="m">18</span><span class="p">)</span>
<span class="n">m</span><span class="o">.</span><span class="n">Store</span><span class="p">(</span><span class="s">"stefno"</span><span class="p">,</span> <span class="m">20</span><span class="p">)</span>
<span class="c">// 2. 读取</span>
<span class="n">age</span><span class="p">,</span> <span class="n">_</span> <span class="o">:=</span> <span class="n">m</span><span class="o">.</span><span class="n">Load</span><span class="p">(</span><span class="s">"qcrao"</span><span class="p">)</span>
<span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="n">age</span><span class="o">.</span><span class="p">(</span><span class="kt">int</span><span class="p">))</span>
<span class="c">// 3. 遍历</span>
<span class="n">m</span><span class="o">.</span><span class="n">Range</span><span class="p">(</span><span class="k">func</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">value</span> <span class="k">interface</span><span class="p">{})</span> <span class="kt">bool</span> <span class="p">{</span>
<span class="n">name</span> <span class="o">:=</span> <span class="n">key</span><span class="o">.</span><span class="p">(</span><span class="kt">string</span><span class="p">)</span>
<span class="n">age</span> <span class="o">:=</span> <span class="n">value</span><span class="o">.</span><span class="p">(</span><span class="kt">int</span><span class="p">)</span>
<span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">age</span><span class="p">)</span>
<span class="k">return</span> <span class="no">true</span>
<span class="p">})</span>
<span class="c">// 4. 删除</span>
<span class="n">m</span><span class="o">.</span><span class="n">Delete</span><span class="p">(</span><span class="s">"qcrao"</span><span class="p">)</span>
<span class="n">age</span><span class="p">,</span> <span class="n">ok</span> <span class="o">:=</span> <span class="n">m</span><span class="o">.</span><span class="n">Load</span><span class="p">(</span><span class="s">"qcrao"</span><span class="p">)</span>
<span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="n">age</span><span class="p">,</span> <span class="n">ok</span><span class="p">)</span>
<span class="c">// 5. 读取或写入</span>
<span class="n">m</span><span class="o">.</span><span class="n">LoadOrStore</span><span class="p">(</span><span class="s">"stefno"</span><span class="p">,</span> <span class="m">100</span><span class="p">)</span>
<span class="n">age</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="n">m</span><span class="o">.</span><span class="n">Load</span><span class="p">(</span><span class="s">"stefno"</span><span class="p">)</span>
<span class="n">fmt</span><span class="o">.</span><span class="n">Println</span><span class="p">(</span><span class="n">age</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<h3 id="12-源码分析">1.2 源码分析</h3>
<p>摘抄自:https://go.cyub.vip/concurrency/sync-map.html</p>
<p><strong>sync.Map的结构:</strong></p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">type</span> <span class="n">Map</span> <span class="k">struct</span> <span class="p">{</span>
<span class="n">mu</span> <span class="n">Mutex</span> <span class="c">// 排他锁,用于对dirty map操作时候加锁处理</span>
<span class="n">read</span> <span class="n">atomic</span><span class="o">.</span><span class="n">Value</span> <span class="c">// read map</span>
<span class="c">// dirty map。新增key时候,只写入dirty map中,需要使用mu</span>
<span class="n">dirty</span> <span class="k">map</span><span class="p">[</span><span class="k">interface</span><span class="p">{}]</span><span class="o">*</span><span class="n">entry</span>
<span class="c">// 用来记录从read map中读取key时miss的次数</span>
<span class="n">misses</span> <span class="kt">int</span>
<span class="p">}</span>
</code></pre></div></div>
<p>sync.Map结构体中read字段是<code class="language-plaintext highlighter-rouge">atomic.Value</code>类型,底层是<strong>readOnly结构体</strong>:</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">type</span> <span class="n">readOnly</span> <span class="k">struct</span> <span class="p">{</span>
<span class="n">m</span> <span class="k">map</span><span class="p">[</span><span class="k">interface</span><span class="p">{}]</span><span class="o">*</span><span class="n">entry</span>
<span class="n">amended</span> <span class="kt">bool</span> <span class="c">// 当amended为true时候,表示sync.Map中的key也存在dirty map中</span>
<span class="p">}</span>
</code></pre></div></div>
<p>read map和dirty map的value类型是*entry, entry结构体定义如下:</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// expunged用来标记从dirty map删除掉了</span>
<span class="k">var</span> <span class="n">expunged</span> <span class="o">=</span> <span class="n">unsafe</span><span class="o">.</span><span class="n">Pointer</span><span class="p">(</span><span class="nb">new</span><span class="p">(</span><span class="k">interface</span><span class="p">{}))</span>
<span class="k">type</span> <span class="n">entry</span> <span class="k">struct</span> <span class="p">{</span>
<span class="c">// 如果p == nil 说明对应的entry已经被删除掉了, 且m.dirty == nil</span>
<span class="c">// 如果 p == expunged 说明对应的entry已经被删除了,但m.dirty != nil,且该entry不存在m.dirty中</span>
<span class="c">// 上述两种情况外,entry则是合法的值并且在m.read.m[key]中存在</span>
<span class="c">// 如果m.dirty != nil,entry也会在m.dirty[key]中</span>
<span class="c">// p指针指向sync.Map中key对应的Value</span>
<span class="n">p</span> <span class="n">unsafe</span><span class="o">.</span><span class="n">Pointer</span> <span class="c">// *interface{}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>对Map的操作可以分为四类:</p>
<ol>
<li>Add key-value 新增key-value</li>
<li>Update key-value 更新key对应的value值</li>
<li>Get Key-value 获取Key对应的Value值</li>
<li>Delete Key 删除key</li>
</ol>
<p>我们来看看新增和更新操作:</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// Store用来新增和更新操作</span>
<span class="k">func</span> <span class="p">(</span><span class="n">m</span> <span class="o">*</span><span class="n">Map</span><span class="p">)</span> <span class="n">Store</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">value</span> <span class="k">interface</span><span class="p">{})</span> <span class="p">{</span>
<span class="n">read</span><span class="p">,</span> <span class="n">_</span> <span class="o">:=</span> <span class="n">m</span><span class="o">.</span><span class="n">read</span><span class="o">.</span><span class="n">Load</span><span class="p">()</span><span class="o">.</span><span class="p">(</span><span class="n">readOnly</span><span class="p">)</span>
<span class="c">// 如果read map存在该key,且该key对应的value不是expunged时(准确的说key对应的value, value是*entry类型,entry的p字段指向不是expunged时),</span>
<span class="c">// 则使用cas更新value,此操作是原子性的</span>
<span class="k">if</span> <span class="n">e</span><span class="p">,</span> <span class="n">ok</span> <span class="o">:=</span> <span class="n">read</span><span class="o">.</span><span class="n">m</span><span class="p">[</span><span class="n">key</span><span class="p">];</span> <span class="n">ok</span> <span class="o">&&</span> <span class="n">e</span><span class="o">.</span><span class="n">tryStore</span><span class="p">(</span><span class="o">&</span><span class="n">value</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span>
<span class="p">}</span>
<span class="n">m</span><span class="o">.</span><span class="n">mu</span><span class="o">.</span><span class="n">Lock</span><span class="p">()</span> <span class="c">// 先加锁,然后重新读取一次read map,目的是防止dirty map升级到read map(并发Load操作时候),read map更改了。</span>
<span class="n">read</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="n">m</span><span class="o">.</span><span class="n">read</span><span class="o">.</span><span class="n">Load</span><span class="p">()</span><span class="o">.</span><span class="p">(</span><span class="n">readOnly</span><span class="p">)</span>
<span class="k">if</span> <span class="n">e</span><span class="p">,</span> <span class="n">ok</span> <span class="o">:=</span> <span class="n">read</span><span class="o">.</span><span class="n">m</span><span class="p">[</span><span class="n">key</span><span class="p">];</span> <span class="n">ok</span> <span class="p">{</span> <span class="c">// 若read map存在此key,此时就是map的更新操作</span>
<span class="k">if</span> <span class="n">e</span><span class="o">.</span><span class="n">unexpungeLocked</span><span class="p">()</span> <span class="p">{</span> <span class="c">// 将value由expunged更改成nil,</span>
<span class="c">// 若成功则表明dirty map中不存在此key,把key-value添加到dirty map中</span>
<span class="n">m</span><span class="o">.</span><span class="n">dirty</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="n">e</span>
<span class="p">}</span>
<span class="n">e</span><span class="o">.</span><span class="n">storeLocked</span><span class="p">(</span><span class="o">&</span><span class="n">value</span><span class="p">)</span> <span class="c">// 更改value。value是指针类型(*entry),read map和dirty map的value都指向该值。</span>
<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="n">e</span><span class="p">,</span> <span class="n">ok</span> <span class="o">:=</span> <span class="n">m</span><span class="o">.</span><span class="n">dirty</span><span class="p">[</span><span class="n">key</span><span class="p">];</span> <span class="n">ok</span> <span class="p">{</span><span class="c">// 若dirty map存在该key,则直接更改value</span>
<span class="n">e</span><span class="o">.</span><span class="n">storeLocked</span><span class="p">(</span><span class="o">&</span><span class="n">value</span><span class="p">)</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="c">// 若read map和dirty map中都不存在该key,其实就是map的新增key-value操作</span>
<span class="k">if</span> <span class="o">!</span><span class="n">read</span><span class="o">.</span><span class="n">amended</span> <span class="p">{</span><span class="c">// amended为true时表示sync.Map部分key存在dirty map中</span>
<span class="c">// dirtyLocked()做两件事情:</span>
<span class="c">// 1. 若dirty map等于nil,则初始化dirty map。</span>
<span class="c">// 2. 遍历read map,将read map中的key-value复制到dirty map中,从read map中复制的key-value时,value是nil或expunged的(因为nil和expunged是key删除了的)不进行复制。</span>
<span class="c">// 同时若value值为nil,则顺便更改成expunged(用来标记dirty map不包含此key)</span>
<span class="c">// 思考🤔:为啥dirtyLocked()要干事情2,即将read map的key-value复制到dirty map中?</span>
<span class="n">m</span><span class="o">.</span><span class="n">dirtyLocked</span><span class="p">()</span>
<span class="c">// 该新增key-value将添加dirty map中,所以将read map的amended设置为true。当amended为true时候,从sync.Map读取key时候,优先从read map中读取,若read map读取时候不到时候,会从dirty map中读取</span>
<span class="n">m</span><span class="o">.</span><span class="n">read</span><span class="o">.</span><span class="n">Store</span><span class="p">(</span><span class="n">readOnly</span><span class="p">{</span><span class="n">m</span><span class="o">:</span> <span class="n">read</span><span class="o">.</span><span class="n">m</span><span class="p">,</span> <span class="n">amended</span><span class="o">:</span> <span class="no">true</span><span class="p">})</span>
<span class="p">}</span>
<span class="c">// 添加key-value到dirty map中</span>
<span class="n">m</span><span class="o">.</span><span class="n">dirty</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="o">=</span> <span class="n">newEntry</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
<span class="p">}</span>
<span class="c">// 释放锁</span>
<span class="n">m</span><span class="o">.</span><span class="n">mu</span><span class="o">.</span><span class="n">Unlock</span><span class="p">()</span>
<span class="p">}</span>
<span class="k">func</span> <span class="p">(</span><span class="n">e</span> <span class="o">*</span><span class="n">entry</span><span class="p">)</span> <span class="n">tryStore</span><span class="p">(</span><span class="n">i</span> <span class="o">*</span><span class="k">interface</span><span class="p">{})</span> <span class="kt">bool</span> <span class="p">{</span>
<span class="k">for</span> <span class="p">{</span>
<span class="n">p</span> <span class="o">:=</span> <span class="n">atomic</span><span class="o">.</span><span class="n">LoadPointer</span><span class="p">(</span><span class="o">&</span><span class="n">e</span><span class="o">.</span><span class="n">p</span><span class="p">)</span>
<span class="k">if</span> <span class="n">p</span> <span class="o">==</span> <span class="n">expunged</span> <span class="p">{</span>
<span class="k">return</span> <span class="no">false</span>
<span class="p">}</span>
<span class="k">if</span> <span class="n">atomic</span><span class="o">.</span><span class="n">CompareAndSwapPointer</span><span class="p">(</span><span class="o">&</span><span class="n">e</span><span class="o">.</span><span class="n">p</span><span class="p">,</span> <span class="n">p</span><span class="p">,</span> <span class="n">unsafe</span><span class="o">.</span><span class="n">Pointer</span><span class="p">(</span><span class="n">i</span><span class="p">))</span> <span class="p">{</span>
<span class="k">return</span> <span class="no">true</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">func</span> <span class="p">(</span><span class="n">e</span> <span class="o">*</span><span class="n">entry</span><span class="p">)</span> <span class="n">unexpungeLocked</span><span class="p">()</span> <span class="p">(</span><span class="n">wasExpunged</span> <span class="kt">bool</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="n">atomic</span><span class="o">.</span><span class="n">CompareAndSwapPointer</span><span class="p">(</span><span class="o">&</span><span class="n">e</span><span class="o">.</span><span class="n">p</span><span class="p">,</span> <span class="n">expunged</span><span class="p">,</span> <span class="no">nil</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">func</span> <span class="p">(</span><span class="n">e</span> <span class="o">*</span><span class="n">entry</span><span class="p">)</span> <span class="n">storeLocked</span><span class="p">(</span><span class="n">i</span> <span class="o">*</span><span class="k">interface</span><span class="p">{})</span> <span class="p">{</span>
<span class="n">atomic</span><span class="o">.</span><span class="n">StorePointer</span><span class="p">(</span><span class="o">&</span><span class="n">e</span><span class="o">.</span><span class="n">p</span><span class="p">,</span> <span class="n">unsafe</span><span class="o">.</span><span class="n">Pointer</span><span class="p">(</span><span class="n">i</span><span class="p">))</span>
<span class="p">}</span>
<span class="k">func</span> <span class="p">(</span><span class="n">m</span> <span class="o">*</span><span class="n">Map</span><span class="p">)</span> <span class="n">dirtyLocked</span><span class="p">()</span> <span class="p">{</span>
<span class="k">if</span> <span class="n">m</span><span class="o">.</span><span class="n">dirty</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
<span class="k">return</span>
<span class="p">}</span>
<span class="n">read</span><span class="p">,</span> <span class="n">_</span> <span class="o">:=</span> <span class="n">m</span><span class="o">.</span><span class="n">read</span><span class="o">.</span><span class="n">Load</span><span class="p">()</span><span class="o">.</span><span class="p">(</span><span class="n">readOnly</span><span class="p">)</span>
<span class="n">m</span><span class="o">.</span><span class="n">dirty</span> <span class="o">=</span> <span class="nb">make</span><span class="p">(</span><span class="k">map</span><span class="p">[</span><span class="k">interface</span><span class="p">{}]</span><span class="o">*</span><span class="n">entry</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">read</span><span class="o">.</span><span class="n">m</span><span class="p">))</span>
<span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">e</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">read</span><span class="o">.</span><span class="n">m</span> <span class="p">{</span>
<span class="k">if</span> <span class="o">!</span><span class="n">e</span><span class="o">.</span><span class="n">tryExpungeLocked</span><span class="p">()</span> <span class="p">{</span>
<span class="n">m</span><span class="o">.</span><span class="n">dirty</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">=</span> <span class="n">e</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">func</span> <span class="p">(</span><span class="n">e</span> <span class="o">*</span><span class="n">entry</span><span class="p">)</span> <span class="n">tryExpungeLocked</span><span class="p">()</span> <span class="p">(</span><span class="n">isExpunged</span> <span class="kt">bool</span><span class="p">)</span> <span class="p">{</span>
<span class="n">p</span> <span class="o">:=</span> <span class="n">atomic</span><span class="o">.</span><span class="n">LoadPointer</span><span class="p">(</span><span class="o">&</span><span class="n">e</span><span class="o">.</span><span class="n">p</span><span class="p">)</span>
<span class="k">for</span> <span class="n">p</span> <span class="o">==</span> <span class="no">nil</span> <span class="p">{</span>
<span class="k">if</span> <span class="n">atomic</span><span class="o">.</span><span class="n">CompareAndSwapPointer</span><span class="p">(</span><span class="o">&</span><span class="n">e</span><span class="o">.</span><span class="n">p</span><span class="p">,</span> <span class="no">nil</span><span class="p">,</span> <span class="n">expunged</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="no">true</span>
<span class="p">}</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">atomic</span><span class="o">.</span><span class="n">LoadPointer</span><span class="p">(</span><span class="o">&</span><span class="n">e</span><span class="o">.</span><span class="n">p</span><span class="p">)</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">p</span> <span class="o">==</span> <span class="n">expunged</span>
<span class="p">}</span>
</code></pre></div></div>
<p>接下来看看Map的Get操作:</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// Load方法用来获取key对应的value值,返回的ok表名key是否存在sync.Map中</span>
<span class="k">func</span> <span class="p">(</span><span class="n">m</span> <span class="o">*</span><span class="n">Map</span><span class="p">)</span> <span class="n">Load</span><span class="p">(</span><span class="n">key</span> <span class="k">interface</span><span class="p">{})</span> <span class="p">(</span><span class="n">value</span> <span class="k">interface</span><span class="p">{},</span> <span class="n">ok</span> <span class="kt">bool</span><span class="p">)</span> <span class="p">{</span>
<span class="n">read</span><span class="p">,</span> <span class="n">_</span> <span class="o">:=</span> <span class="n">m</span><span class="o">.</span><span class="n">read</span><span class="o">.</span><span class="n">Load</span><span class="p">()</span><span class="o">.</span><span class="p">(</span><span class="n">readOnly</span><span class="p">)</span>
<span class="n">e</span><span class="p">,</span> <span class="n">ok</span> <span class="o">:=</span> <span class="n">read</span><span class="o">.</span><span class="n">m</span><span class="p">[</span><span class="n">key</span><span class="p">]</span>
<span class="k">if</span> <span class="o">!</span><span class="n">ok</span> <span class="o">&&</span> <span class="n">read</span><span class="o">.</span><span class="n">amended</span> <span class="p">{</span> <span class="c">// 若key不存在read map中,且dirty map包含sync.Map中key情况下</span>
<span class="n">m</span><span class="o">.</span><span class="n">mu</span><span class="o">.</span><span class="n">Lock</span><span class="p">()</span> <span class="c">// 加锁</span>
<span class="n">read</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="n">m</span><span class="o">.</span><span class="n">read</span><span class="o">.</span><span class="n">Load</span><span class="p">()</span><span class="o">.</span><span class="p">(</span><span class="n">readOnly</span><span class="p">)</span> <span class="c">// 再次从read map读取key</span>
<span class="n">e</span><span class="p">,</span> <span class="n">ok</span> <span class="o">=</span> <span class="n">read</span><span class="o">.</span><span class="n">m</span><span class="p">[</span><span class="n">key</span><span class="p">]</span>
<span class="k">if</span> <span class="o">!</span><span class="n">ok</span> <span class="o">&&</span> <span class="n">read</span><span class="o">.</span><span class="n">amended</span> <span class="p">{</span>
<span class="n">e</span><span class="p">,</span> <span class="n">ok</span> <span class="o">=</span> <span class="n">m</span><span class="o">.</span><span class="n">dirty</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="c">// 从dirty map中读取key</span>
<span class="c">// missLocked() 首先将misses计数加1,misses用来表明read map读取key没有命中的次数。</span>
<span class="c">// 若misses次数多于dirty map中元素个数时候,则将dirty map升级为read map,dirty map设置为nil, amended置为false</span>
<span class="n">m</span><span class="o">.</span><span class="n">missLocked</span><span class="p">()</span>
<span class="p">}</span>
<span class="n">m</span><span class="o">.</span><span class="n">mu</span><span class="o">.</span><span class="n">Unlock</span><span class="p">()</span>
<span class="p">}</span>
<span class="k">if</span> <span class="o">!</span><span class="n">ok</span> <span class="p">{</span> <span class="c">// read map 和 dirty map中都不存在该key</span>
<span class="k">return</span> <span class="no">nil</span><span class="p">,</span> <span class="no">false</span>
<span class="p">}</span>
<span class="c">// 加载value值</span>
<span class="k">return</span> <span class="n">e</span><span class="o">.</span><span class="n">load</span><span class="p">()</span>
<span class="p">}</span>
<span class="k">func</span> <span class="p">(</span><span class="n">e</span> <span class="o">*</span><span class="n">entry</span><span class="p">)</span> <span class="n">load</span><span class="p">()</span> <span class="p">(</span><span class="n">value</span> <span class="k">interface</span><span class="p">{},</span> <span class="n">ok</span> <span class="kt">bool</span><span class="p">)</span> <span class="p">{</span>
<span class="n">p</span> <span class="o">:=</span> <span class="n">atomic</span><span class="o">.</span><span class="n">LoadPointer</span><span class="p">(</span><span class="o">&</span><span class="n">e</span><span class="o">.</span><span class="n">p</span><span class="p">)</span>
<span class="k">if</span> <span class="n">p</span> <span class="o">==</span> <span class="no">nil</span> <span class="o">||</span> <span class="n">p</span> <span class="o">==</span> <span class="n">expunged</span> <span class="p">{</span> <span class="c">// 若value值是nil或expunged,返回nil, false,表示key不存在</span>
<span class="k">return</span> <span class="no">nil</span><span class="p">,</span> <span class="no">false</span>
<span class="p">}</span>
<span class="k">return</span> <span class="o">*</span><span class="p">(</span><span class="o">*</span><span class="k">interface</span><span class="p">{})(</span><span class="n">p</span><span class="p">),</span> <span class="no">true</span>
<span class="p">}</span>
<span class="k">func</span> <span class="p">(</span><span class="n">m</span> <span class="o">*</span><span class="n">Map</span><span class="p">)</span> <span class="n">missLocked</span><span class="p">()</span> <span class="p">{</span>
<span class="n">m</span><span class="o">.</span><span class="n">misses</span><span class="o">++</span>
<span class="k">if</span> <span class="n">m</span><span class="o">.</span><span class="n">misses</span> <span class="o"><</span> <span class="nb">len</span><span class="p">(</span><span class="n">m</span><span class="o">.</span><span class="n">dirty</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span>
<span class="p">}</span>
<span class="c">// 新创建一个readOnly对象,其中amended为false, 并将m.dirty直接赋值给该对象的m字段,</span>
<span class="c">// 这也是上面思考中的dirtyLocked为什么要干事情2的原因,因为通过2操作之后,m.dirty已包含read map中的所有key,可以直接拿来创建readOnly。</span>
<span class="n">m</span><span class="o">.</span><span class="n">read</span><span class="o">.</span><span class="n">Store</span><span class="p">(</span><span class="n">readOnly</span><span class="p">{</span><span class="n">m</span><span class="o">:</span> <span class="n">m</span><span class="o">.</span><span class="n">dirty</span><span class="p">})</span>
<span class="n">m</span><span class="o">.</span><span class="n">dirty</span> <span class="o">=</span> <span class="no">nil</span>
<span class="n">m</span><span class="o">.</span><span class="n">misses</span> <span class="o">=</span> <span class="m">0</span>
<span class="p">}</span>
</code></pre></div></div>
<p>在接着看看Map的删除操作:</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// Delete用于删除key</span>
<span class="k">func</span> <span class="p">(</span><span class="n">m</span> <span class="o">*</span><span class="n">Map</span><span class="p">)</span> <span class="n">Delete</span><span class="p">(</span><span class="n">key</span> <span class="k">interface</span><span class="p">{})</span> <span class="p">{</span>
<span class="n">read</span><span class="p">,</span> <span class="n">_</span> <span class="o">:=</span> <span class="n">m</span><span class="o">.</span><span class="n">read</span><span class="o">.</span><span class="n">Load</span><span class="p">()</span><span class="o">.</span><span class="p">(</span><span class="n">readOnly</span><span class="p">)</span>
<span class="n">e</span><span class="p">,</span> <span class="n">ok</span> <span class="o">:=</span> <span class="n">read</span><span class="o">.</span><span class="n">m</span><span class="p">[</span><span class="n">key</span><span class="p">]</span>
<span class="k">if</span> <span class="o">!</span><span class="n">ok</span> <span class="o">&&</span> <span class="n">read</span><span class="o">.</span><span class="n">amended</span> <span class="p">{</span>
<span class="n">m</span><span class="o">.</span><span class="n">mu</span><span class="o">.</span><span class="n">Lock</span><span class="p">()</span>
<span class="n">read</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="n">m</span><span class="o">.</span><span class="n">read</span><span class="o">.</span><span class="n">Load</span><span class="p">()</span><span class="o">.</span><span class="p">(</span><span class="n">readOnly</span><span class="p">)</span>
<span class="n">e</span><span class="p">,</span> <span class="n">ok</span> <span class="o">=</span> <span class="n">read</span><span class="o">.</span><span class="n">m</span><span class="p">[</span><span class="n">key</span><span class="p">]</span>
<span class="c">// 若read map不存在该key,但dirty map中存在该key。则直接调用delete,删除dirty map中该key</span>
<span class="k">if</span> <span class="o">!</span><span class="n">ok</span> <span class="o">&&</span> <span class="n">read</span><span class="o">.</span><span class="n">amended</span> <span class="p">{</span>
<span class="nb">delete</span><span class="p">(</span><span class="n">m</span><span class="o">.</span><span class="n">dirty</span><span class="p">,</span> <span class="n">key</span><span class="p">)</span>
<span class="p">}</span>
<span class="n">m</span><span class="o">.</span><span class="n">mu</span><span class="o">.</span><span class="n">Unlock</span><span class="p">()</span>
<span class="p">}</span>
<span class="k">if</span> <span class="n">ok</span> <span class="p">{</span>
<span class="n">e</span><span class="o">.</span><span class="nb">delete</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">func</span> <span class="p">(</span><span class="n">e</span> <span class="o">*</span><span class="n">entry</span><span class="p">)</span> <span class="nb">delete</span><span class="p">()</span> <span class="p">(</span><span class="n">hadValue</span> <span class="kt">bool</span><span class="p">)</span> <span class="p">{</span>
<span class="k">for</span> <span class="p">{</span>
<span class="n">p</span> <span class="o">:=</span> <span class="n">atomic</span><span class="o">.</span><span class="n">LoadPointer</span><span class="p">(</span><span class="o">&</span><span class="n">e</span><span class="o">.</span><span class="n">p</span><span class="p">)</span>
<span class="k">if</span> <span class="n">p</span> <span class="o">==</span> <span class="no">nil</span> <span class="o">||</span> <span class="n">p</span> <span class="o">==</span> <span class="n">expunged</span> <span class="p">{</span> <span class="c">// 若entry中p已经是nil或者expunged则直接返回</span>
<span class="k">return</span> <span class="no">false</span>
<span class="p">}</span>
<span class="k">if</span> <span class="n">atomic</span><span class="o">.</span><span class="n">CompareAndSwapPointer</span><span class="p">(</span><span class="o">&</span><span class="n">e</span><span class="o">.</span><span class="n">p</span><span class="p">,</span> <span class="n">p</span><span class="p">,</span> <span class="no">nil</span><span class="p">)</span> <span class="p">{</span> <span class="c">// 将entry中的p设置为nil</span>
<span class="k">return</span> <span class="no">true</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>sync.Map还提供遍历key-value功能:</p>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// Range方法接受一个迭代回调函数,用来处理遍历的key和value</span>
<span class="k">func</span> <span class="p">(</span><span class="n">m</span> <span class="o">*</span><span class="n">Map</span><span class="p">)</span> <span class="n">Range</span><span class="p">(</span><span class="n">f</span> <span class="k">func</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">value</span> <span class="k">interface</span><span class="p">{})</span> <span class="kt">bool</span><span class="p">)</span> <span class="p">{</span>
<span class="n">read</span><span class="p">,</span> <span class="n">_</span> <span class="o">:=</span> <span class="n">m</span><span class="o">.</span><span class="n">read</span><span class="o">.</span><span class="n">Load</span><span class="p">()</span><span class="o">.</span><span class="p">(</span><span class="n">readOnly</span><span class="p">)</span>
<span class="k">if</span> <span class="n">read</span><span class="o">.</span><span class="n">amended</span> <span class="p">{</span> <span class="c">// 若dirty map中包含sync.Map中key时候</span>
<span class="n">m</span><span class="o">.</span><span class="n">mu</span><span class="o">.</span><span class="n">Lock</span><span class="p">()</span>
<span class="n">read</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="n">m</span><span class="o">.</span><span class="n">read</span><span class="o">.</span><span class="n">Load</span><span class="p">()</span><span class="o">.</span><span class="p">(</span><span class="n">readOnly</span><span class="p">)</span>
<span class="k">if</span> <span class="n">read</span><span class="o">.</span><span class="n">amended</span> <span class="p">{</span><span class="c">// 加锁之后,再次判断,是为了防止并发调用Load方法时候,dirty map升级为read map时候,amended为false情况</span>
<span class="c">// read.amended为true的时候,m.dirty包含sync.Map中所有的key</span>
<span class="n">read</span> <span class="o">=</span> <span class="n">readOnly</span><span class="p">{</span><span class="n">m</span><span class="o">:</span> <span class="n">m</span><span class="o">.</span><span class="n">dirty</span><span class="p">}</span>
<span class="n">m</span><span class="o">.</span><span class="n">read</span><span class="o">.</span><span class="n">Store</span><span class="p">(</span><span class="n">read</span><span class="p">)</span>
<span class="n">m</span><span class="o">.</span><span class="n">dirty</span> <span class="o">=</span> <span class="no">nil</span>
<span class="n">m</span><span class="o">.</span><span class="n">misses</span> <span class="o">=</span> <span class="m">0</span>
<span class="p">}</span>
<span class="n">m</span><span class="o">.</span><span class="n">mu</span><span class="o">.</span><span class="n">Unlock</span><span class="p">()</span>
<span class="p">}</span>
<span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">e</span> <span class="o">:=</span> <span class="k">range</span> <span class="n">read</span><span class="o">.</span><span class="n">m</span> <span class="p">{</span>
<span class="n">v</span><span class="p">,</span> <span class="n">ok</span> <span class="o">:=</span> <span class="n">e</span><span class="o">.</span><span class="n">load</span><span class="p">()</span>
<span class="k">if</span> <span class="o">!</span><span class="n">ok</span> <span class="p">{</span>
<span class="k">continue</span>
<span class="p">}</span>
<span class="k">if</span> <span class="o">!</span><span class="n">f</span><span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span> <span class="p">{</span> <span class="c">//执行迭代回调函数,当返回false时候,停止迭代</span>
<span class="k">break</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h3 id="13-为什么不使用syncmutexmap实现并发的map呢">1.3 为什么不使用sync.Mutex+map实现并发的map呢?</h3>
<p>这个问题可以换个问法就是sync.Map相比sync.Mutex+map实现并发map有哪些优势?</p>
<p>sync.Map优势在于当key存在read map时候,如果进行Store操作,可以使用原子性操作更新,而sync.Mutex+map形式每次写操作都要加锁,这个成本更高。</p>
<p>另外并发读写两个不同的key时候,写操作需要加锁,而读操作是不需要加锁的。</p>
<h3 id="14-读少写多情况下并发map应该怎么设计">1.4 读少写多情况下并发map,应该怎么设计?</h3>
<p>这种情况下,可以使用分片锁,跟据key进行hash处理后,找到其对应读写锁,然后进行锁定处理。通过分片锁机制,可以降低锁的粒度来实现读少写多情况下高并发。可以参见<a href="https://github.com/orcaman/concurrent-map">orcaman/concurrent-map</a>实现。</p>
<h3 id="15-总结">1.5 总结</h3>
<ul>
<li><code class="language-plaintext highlighter-rouge">sync.map</code> 是线程安全的,读取,插入,删除也都保持着常数级的时间复杂度。</li>
<li><code class="language-plaintext highlighter-rouge">sync.map</code> 的零值是有效的,并且零值是一个空的 map。在第一次使用之后,不允许被拷贝。</li>
<li>通过读写分离,降低锁时间来提高效率,适用于读多写少的场景</li>
<li>Range 操作需要提供一个函数,参数是 <code class="language-plaintext highlighter-rouge">k,v</code>,返回值是一个布尔值:<code class="language-plaintext highlighter-rouge">f func(key, value interface{}) bool</code></li>
<li>底层是通过空间换时间的方式,使用 read 和 dirty 两个 map 来进行读写分离,降低锁时间来提高效率。</li>
<li>用 Load 或 LoadOrStore 函数时,如果在 read 中没有找到 key,则会将 misses 值原子地增加 1,当 misses 增加到和 dirty 的长度相等时,会将 dirty 提升为 read。以期减少“读 miss”。</li>
<li><strong>新写入</strong>的 key 会保存到 dirty 中,如果这时 dirty 为 nil,就会先新创建一个 dirty,并将 read 中未被删除的元素遍历拷贝到 dirty(这就是为啥写操作比较耗时的原因)</li>
<li>
<p>当 dirty 为 nil 的时候,read 就代表 map 所有的数据;当 dirty 不为 nil 的时候,dirty 才代表 map 所有的数据</p>
</li>
<li>sync.Map采用空间换时间策略。其底层结构存在两个map,分别是read map和dirty map。当读取操作时候,优先从read map中读取,是不需要加锁的,若key不存在read map中时候,再从dirty map中读取,这个过程是加锁的。当新增key操作时候,只会将新增key添加到dirty map中,此操作是加锁的,但不会影响read map的读操作。当更新key操作时候,如果key已存在read map中时候,只需无锁更新更新read map就行,负责加锁处理在dirty map中情况了。总之sync.Map会优先从read map中读取、更新、删除,因为对read map的读取不需要锁</li>
<li>当sync.Map读取key操作时候,若从read map中一直未读到,若dirty map中存在read map中不存在的keys时,则会把dirty map升级为read map,这个过程是加锁的。这样下次读取时候只需要考虑从read map读取,且读取过程是无锁的</li>
<li>延迟删除机制,删除一个键值时只是打上删除标记,只有在提升dirty map为read map的时候才清理删除的数据</li>
<li>sync.Map中的dirty map要么是nil,要么包含read map中所有未删除的key-value。</li>
<li>sync.Map适用于读多写少场景。根据<a href="https://golang.org/pkg/sync/#Map">包官方文档介绍</a>,它特别适合这两个场景:1. 一个key只写入一次但读取多次时,比如在只会增长的缓存中;2. 当多个goroutine读取、写入和更新不相交的键值对时。</li>
</ul>
<h2 id="2-syncpool">2. sync.pool</h2>
<h2 id="参考资料">参考资料</h2>
<ul>
<li><a href="https://www.cnblogs.com/qcrao-2018/p/12833787.html">深度解密 Go 语言之 sync.map </a></li>
<li><a href="https://www.cnblogs.com/qcrao-2018/p/12736031.html">深度解密 Go 语言之 sync.Pool</a></li>
<li><a href="https://deepzz.com/post/golang-sync-package-usage.html">浅谈 Golang sync 包的相关使用方法</a></li>
<li><a href="https://books.studygolang.com/The-Golang-Standard-Library-by-Example/chapter16/16.01.html">sync - 处理同步需求</a></li>
<li><a href="https://go.cyub.vip/concurrency/sync-map.html">sync.map 源码分析(非常推荐阅读)</a></li>
</ul>进击de豆子快速了解go-sync包相关知识