作者:微信小助手
<blockquote data-tool="mdnice编辑器" style="font-size: 0.9em;overflow: auto;padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;text-size-adjust: 100%;line-height: 1.55em;border-radius: 6px;color: rgb(89, 89, 89);box-sizing: inherit;border-width: 1px;border-top-style: solid;border-right-style: solid;border-bottom-style: solid;border-color: rgba(64, 184, 250, 0.4);background: rgba(64, 184, 250, 0.1);"> <span style="color: RGBA(64, 184, 250, .5);font-size: 34px;line-height: 1;font-weight: 700;">❝</span> <p style="padding-top: 8px;padding-bottom: 8px;font-size: 14px;word-spacing: 2px;line-height: 26px;">每家数字化企业在目前遇到流批一体概念的时候,都会对这个概念抱有一些疑问,到底什么是流批一体?这个概念的来源?这个概念能为用户、开发人员以及企业带来什么样的好处?跟随着博主的理解和脑洞出发吧。</p> <span style="float: right;color: RGBA(64, 184, 250, .5);">❞</span> </blockquote> <p data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;color: rgb(43, 43, 43);font-family: Optima-Regular, Optima, PingFangTC-Light, PingFangSC-light, PingFangTC-light;letter-spacing: 2px;white-space: normal;line-height: 26px;font-size: 14px;word-spacing: 2px;text-align: center;"><span style="color: rgb(64, 184, 250);font-size: 25px;font-weight: bold;">前言</span><span style="color: rgb(64, 184, 250);font-size: 25px;font-weight: bold;"></span></p> <p data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;color: rgb(43, 43, 43);font-family: Optima-Regular, Optima, PingFangTC-Light, PingFangSC-light, PingFangTC-light;letter-spacing: 2px;white-space: normal;line-height: 26px;font-size: 14px;word-spacing: 2px;text-align: center;">到底什么是流批一体?</p> <p data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;color: rgb(43, 43, 43);font-family: Optima-Regular, Optima, PingFangTC-Light, PingFangSC-light, PingFangTC-light;letter-spacing: 2px;white-space: normal;line-height: 26px;font-size: 14px;word-spacing: 2px;text-align: center;">批的来源?流的来源?</p> <p data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;color: rgb(43, 43, 43);font-family: Optima-Regular, Optima, PingFangTC-Light, PingFangSC-light, PingFangTC-light;letter-spacing: 2px;white-space: normal;line-height: 26px;font-size: 14px;word-spacing: 2px;text-align: center;">为什么要做流批一体?</p> <p data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;color: rgb(43, 43, 43);font-family: Optima-Regular, Optima, PingFangTC-Light, PingFangSC-light, PingFangTC-light;letter-spacing: 2px;white-space: normal;line-height: 26px;font-size: 14px;word-spacing: 2px;text-align: center;"><br></p> <p data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;color: rgb(43, 43, 43);font-family: Optima-Regular, Optima, PingFangTC-Light, PingFangSC-light, PingFangTC-light;letter-spacing: 2px;white-space: normal;line-height: 26px;font-size: 14px;word-spacing: 2px;text-align: center;">从</p> <p data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;color: rgb(43, 43, 43);font-family: Optima-Regular, Optima, PingFangTC-Light, PingFangSC-light, PingFangTC-light;letter-spacing: 2px;white-space: normal;line-height: 26px;font-size: 14px;word-spacing: 2px;text-align: center;">数据开发的现状出发</p> <p data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;color: rgb(43, 43, 43);font-family: Optima-Regular, Optima, PingFangTC-Light, PingFangSC-light, PingFangTC-light;letter-spacing: 2px;white-space: normal;line-height: 26px;font-size: 14px;word-spacing: 2px;text-align: center;">探索理想中的流批一体能力支持</p> <p data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;color: rgb(43, 43, 43);font-family: Optima-Regular, Optima, PingFangTC-Light, PingFangSC-light, PingFangTC-light;letter-spacing: 2px;white-space: normal;line-height: 26px;font-size: 14px;word-spacing: 2px;text-align: center;">最终到数仓落地</p> <p data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;color: rgb(43, 43, 43);font-family: Optima-Regular, Optima, PingFangTC-Light, PingFangSC-light, PingFangTC-light;letter-spacing: 2px;white-space: normal;line-height: 26px;font-size: 14px;word-spacing: 2px;text-align: center;">go!!!<br><br></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.625" data-s="300,640" src="/upload/a01f7a363bcf360f75948ef0ae70939e.png" data-type="png" data-w="1280" style="height: auto !important;"><br></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.625" data-s="300,640" src="/upload/41c814da73587017a2cdf64be6988523.png" data-type="png" data-w="1280" style="height: auto !important;"></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.625" data-s="300,640" src="/upload/eadd233b3872ecdbf30753bf67f16f52.png" data-type="png" data-w="1280" style="height: auto !important;"></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.625" data-s="300,640" src="/upload/ed1b7a202dbe6b6470226861087f071.png" data-type="png" data-w="1280" style="height: auto !important;"></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.625" data-s="300,640" src="/upload/88006c1f93c5225927d412ace3c5078.png" data-type="png" data-w="1280" style="height: auto !important;"></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.625" data-s="300,640" src="/upload/a982b754d3487d2095d0186dd7cf8cfa.png" data-type="png" data-w="1280" style="height: auto !important;"></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.625" data-s="300,640" src="/upload/ed4512596df979137297796c33e0e403.png" data-type="png" data-w="1280" style="height: auto !important;"></p> <p data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;color: rgb(43, 43, 43);font-family: Optima-Regular, Optima, PingFangTC-Light, PingFangSC-light, PingFangTC-light;letter-spacing: 2px;white-space: normal;line-height: 26px;font-size: 14px;word-spacing: 2px;text-align: center;">n 年前的引擎能力(hive 等)<br></p> <p data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;color: rgb(43, 43, 43);font-family: Optima-Regular, Optima, PingFangTC-Light, PingFangSC-light, PingFangTC-light;letter-spacing: 2px;white-space: normal;line-height: 26px;font-size: 14px;word-spacing: 2px;text-align: center;">对文件、批量数据处理支持很友好</p> <p data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;color: rgb(43, 43, 43);font-family: Optima-Regular, Optima, PingFangTC-Light, PingFangSC-light, PingFangTC-light;letter-spacing: 2px;white-space: normal;line-height: 26px;font-size: 14px;word-spacing: 2px;text-align: center;"><span style="color: rgb(43, 43, 43);font-family: Optima-Regular, Optima, PingFangTC-Light, PingFangSC-light, PingFangTC-light;font-size: 14px;letter-spacing: 2px;text-align: center;word-spacing: 2px;">数据多是小时、天级别延迟</span></p> <p data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;color: rgb(43, 43, 43);font-family: Optima-Regular, Optima, PingFangTC-Light, PingFangSC-light, PingFangTC-light;letter-spacing: 2px;white-space: normal;line-height: 26px;font-size: 14px;word-spacing: 2px;text-align: center;">结论:批是在批式存储、处理引擎能力支持的角度提出的</p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.625" data-s="300,640" src="/upload/78f098a5015b3138f3bf30139b24540e.png" data-type="png" data-w="1280" style="height: auto !important;"></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.625" data-s="300,640" src="/upload/86e57b34c22180faf5f903e66dcec791.png" data-type="png" data-w="1280" style="height: auto !important;"></p> <p style="text-align: center;"><br></p> <p data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;color: rgb(43, 43, 43);font-family: Optima-Regular, Optima, PingFangTC-Light, PingFangSC-light, PingFangTC-light;letter-spacing: 2px;white-space: normal;line-height: 26px;font-size: 14px;word-spacing: 2px;text-align: center;">近几年的引擎能力(flink 等)<br></p> <p data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;color: rgb(43, 43, 43);font-family: Optima-Regular, Optima, PingFangTC-Light, PingFangSC-light, PingFangTC-light;letter-spacing: 2px;white-space: normal;line-height: 26px;font-size: 14px;word-spacing: 2px;text-align: center;">逐渐对流式数据处理、容错支持更好</p> <p data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;color: rgb(43, 43, 43);font-family: Optima-Regular, Optima, PingFangTC-Light, PingFangSC-light, PingFangTC-light;letter-spacing: 2px;white-space: normal;line-height: 26px;font-size: 14px;word-spacing: 2px;text-align: center;">数据可以做到秒、分钟级别延迟</p> <p data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;color: rgb(43, 43, 43);font-family: Optima-Regular, Optima, PingFangTC-Light, PingFangSC-light, PingFangTC-light;letter-spacing: 2px;white-space: normal;line-height: 26px;font-size: 14px;word-spacing: 2px;text-align: center;">结论:流是在流式存储、处理引擎能力支持的角度提出的</p> <p style="text-align: center;"><br></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.625" data-s="300,640" src="/upload/cd1cd88e1fc294680fac75a41fc636fa.png" data-type="png" data-w="1280" style="height: auto !important;"></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.625" data-s="300,640" src="/upload/aad2822b9798ee5f7bcd8b67eb8280b4.png" data-type="png" data-w="1280" style="height: auto !important;"></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.625" data-s="300,640" src="/upload/e0b3c437ce1d2f1bf498207ffc292804.png" data-type="png" data-w="1280" style="height: auto !important;"></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.625" data-s="300,640" src="/upload/c62debd696d019c792d26e1aa4c152f1.png" data-type="png" data-w="1280" style="height: auto !important;"></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.625" data-s="300,640" src="/upload/3f99860452364b552abaddf9b8885db1.png" data-type="png" data-w="1280" style="height: auto !important;"></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.625" data-s="300,640" src="/upload/761efafa69395c94c76345552f7429c2.png" data-type="png" data-w="1280" style="height: auto !important;"></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.625" data-s="300,640" src="/upload/b1bf832ecbbbd05470d4f6395277618a.png" data-type="png" data-w="1280" style="height: auto !important;"></p> <p style="text-align: center;"><br></p> <p data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;color: rgb(43, 43, 43);font-family: Optima-Regular, Optima, PingFangTC-Light, PingFangSC-light, PingFangTC-light;letter-spacing: 2px;white-space: normal;line-height: 26px;font-size: 14px;word-spacing: 2px;text-align: center;">站在用户的角度来看</p> <p data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;color: rgb(43, 43, 43);font-family: Optima-Regular, Optima, PingFangTC-Light, PingFangSC-light, PingFangTC-light;letter-spacing: 2px;white-space: normal;line-height: 26px;font-size: 14px;word-spacing: 2px;text-align: center;">对于相同的指标,有离线的、实时的,而且部分场景下口径不能统一!<br></p> <p data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;color: rgb(43, 43, 43);font-family: Optima-Regular, Optima, PingFangTC-Light, PingFangSC-light, PingFangTC-light;letter-spacing: 2px;white-space: normal;line-height: 26px;font-size: 14px;word-spacing: 2px;text-align: center;"><br></p> <p style="text-align: center;"><br></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.625" data-s="300,640" src="/upload/5195922d2c03ec6fe9333a5e2e6caea7.png" data-type="png" data-w="1280" style="height: auto !important;"></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.625" data-s="300,640" src="/upload/7b1c2970ca8410ba6404f9e4fec12828.png" data-type="png" data-w="1280" style="height: auto !important;"></p> <p style="text-align: center;"><br></p> <p data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;color: rgb(43, 43, 43);font-family: Optima-Regular, Optima, PingFangTC-Light, PingFangSC-light, PingFangTC-light;letter-spacing: 2px;white-space: normal;line-height: 26px;font-size: 14px;word-spacing: 2px;text-align: center;">博主理解的流批一体更多的是站在平台能力支持的角度上</p> <p data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;color: rgb(43, 43, 43);font-family: Optima-Regular, Optima, PingFangTC-Light, PingFangSC-light, PingFangTC-light;letter-spacing: 2px;white-space: normal;line-height: 26px;font-size: 14px;word-spacing: 2px;text-align: center;">所以这里重点说明引擎 + 工具链上的期望</p> <p data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;padding-top: 8px;padding-bottom: 8px;color: rgb(43, 43, 43);font-family: Optima-Regular, Optima, PingFangTC-Light, PingFangSC-light, PingFangTC-light;letter-spacing: 2px;white-space: normal;line-height: 26px;font-size: 14px;word-spacing: 2px;text-align: center;"><br></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.625" data-s="300,640" src="/upload/a9048771d6762b46ac2fe48ee671e72b.png" data-type="png" data-w="1280" style="height: auto !important;"></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.625" data-s="300,640" src="/upload/386a65bca3e08d7552a7a9b9a9ef8088.png" data-type="png" data-w="1280" style="height: auto !important;"></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.625" data-s="300,640" src="/upload/88305ea3afb0ab1fa41f8becf0ccd598.png" data-type="png" data-w="1280" style="height: auto !important;"></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.625" data-s="300,640" src="/upload/bec8197b96cd715197461fc82217264b.png" data-type="png" data-w="1280" style="height: auto !important;"></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.625" data-s="300,640" src="/upload/ca4e0d4e2b15b27955e0db3c822b5133.png" data-type="png" data-w="1280" style="height: auto !important;"></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.625" data-s="300,640" src="/upload/2c40c23349286f781cb6d0e11c53943c.png" data-type="png" data-w="1280" style="height: auto !important;"></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.625" data-s="300,640" src="/upload/386a65bca3e08d7552a7a9b9a9ef8088.png" data-type="png" data-w="1280" style="height: auto !important;"></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.625" data-s="300,640" src="/upload/e3f3f438119d4f4fde6d17f38dbc27ea.png" data-type="png" data-w="1280" style="height: auto !important;"></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.625" data-s="300,640" src="/upload/a5049bdcf2801fdd27f6cda96951324d.png" data-type="png" data-w="1280" style="height: auto !important;"></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.625" data-s="300,640" src="/upload/a4c0dfc53b4e0107564cfe0e18af10bd.png" data-type="png" data-w="1280" style="height: auto !important;"></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.625" data-s="300,640" src="/upload/2759ec84a8f3a22ef0e66e8cd5da15db.png" data-type="png" data-w="1280" style="height: auto !important;"></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.625" data-s="300,640" src="/upload/fc22ba586f8314f23ea700ef1aed4680.png" data-type="png" data-w="1280" style="height: auto !important;"></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.625" data-s="300,640" src="/upload/845990ab8543b96ae3375c09dcffbb34.png" data-type="png" data-w="1280" style="height: auto !important;"></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.625" data-s="300,640" src="/upload/d405436e5543fbfc9752436ea7b8398d.png" data-type="png" data-w="1280" style="height: auto !important;"></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.625" data-s="300,640" src="/upload/2991c9da6bfa9da326796ca6c397295c.png" data-type="png" data-w="1280" style="height: auto !important;"></p> <p style="text-align: center;"><br></p> <p style="text-align: center;"><br></p>
作者:微信小助手
<section style="display:none;" data-tools="新媒体管家" data-label="powered by xmt.cn" data-mpa-powered-by="yiban.io"> <br> </section> <section class="mp_profile_iframe_wrp"> <mpprofile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-id="MzA5MTU0OTY0Ng==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/zc3KLDBfJlmPt0J5PXYOoiaG8wsQPZrLevbxMZSfgQ0YypNYaicnbS0P9UicluuOySLSP4CjTcRUVHCZzYeXQ9WlA/0?wx_fmt=png" data-nickname="Java派" data-alias="javapai" data-signature="专注Java相关技术栈:Spring全家筒、Docker、k8s、Mysql、集群、微服务、中间件等知识。" data-from="0"></mpprofile> </section> <section style="max-width: 100%;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);min-height: 1em;text-align: left;font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="color: rgb(136, 136, 136);font-family: Optima-Regular, PingFangTC-light;font-size: 10px;letter-spacing: 0.544px;"><br></span> </section> <section style="max-width: 100%;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);min-height: 1em;text-align: left;font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="color: rgb(136, 136, 136);font-family: Optima-Regular, PingFangTC-light;font-size: 10px;letter-spacing: 0.544px;">作者:</span> <span style="color: rgb(136, 136, 136);font-family: Optima-Regular, PingFangTC-light;font-size: 10px;letter-spacing: 0.544px;">mztBang</span> </section> <p style="max-width: 100%;min-height: 1em;letter-spacing: 0.544px;white-space: normal;background-color: rgb(255, 255, 255);font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(136, 136, 136);font-family: Optima-Regular, PingFangTC-light;font-size: 10px;letter-spacing: 0.544px;text-align: left;box-sizing: border-box !important;overflow-wrap: break-word !important;">原文:csdn.net/weixin_43954303/article/details/113781801</span></p> <p style="margin-top: 15px;margin-bottom: 15px;max-width: 100%;min-height: 1em;letter-spacing: 0.544px;white-space: normal;color: rgb(80, 97, 109);font-size: 15px;text-align: start;font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;box-sizing: border-box !important;overflow-wrap: break-word !important;"><br></p> <p style="margin-top: 15px;margin-bottom: 15px;max-width: 100%;min-height: 1em;letter-spacing: 0.544px;white-space: normal;color: rgb(80, 97, 109);font-size: 15px;text-align: start;font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;box-sizing: border-box !important;overflow-wrap: break-word !important;">此组件解决的问题是:<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">「谁」在「什么时间」对「什么」做了「什么事」</p> <blockquote style="margin-bottom: 1.2em;padding: 15px 15px 15px 1rem;border-left-width: 6px;border-left-color: rgb(96, 125, 139);color: rgb(129, 145, 152);font-size: 14px;max-width: 100%;letter-spacing: 0.544px;white-space: normal;line-height: 18px;background: rgb(242, 247, 251);font-family: Helvetica, Arial, sans-serif;text-align: start;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <p style="max-width: 100%;min-height: 1em;font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;box-sizing: border-box !important;overflow-wrap: break-word !important;">本组件目前针对 Spring-boot 做了 Autoconfig,如果是 SpringMVC,也可自己在 xml 初始化 bean</p> </blockquote> <h2 style="margin-top: 2rem;margin-bottom: 0.5rem;font-weight: 700;font-size: 20px;max-width: 100%;letter-spacing: 0.544px;white-space: normal;color: rgb(255, 140, 0);line-height: 1.35;text-align: start;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: Menlo, Monaco, 'Source Code Pro', Consolas, Inconsolata, 'Ubuntu Mono', 'DejaVu Sans Mono', 'Courier New', 'Droid Sans Mono', 'Hiragino Sans GB', 微软雅黑, monospace !important;"><span style="color: rgb(0, 0, 0);">使用方式</span></h2> <h3 style="margin-top: 2rem;margin-bottom: 0.5rem;font-weight: 700;font-size: 18px;max-width: 100%;letter-spacing: 0.544px;white-space: normal;color: rgb(255, 140, 0);line-height: 1.35;text-align: start;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: Menlo, Monaco, 'Source Code Pro', Consolas, Inconsolata, 'Ubuntu Mono', 'DejaVu Sans Mono', 'Courier New', 'Droid Sans Mono', 'Hiragino Sans GB', 微软雅黑, monospace !important;"><span style="color: rgb(0, 0, 0);">基本使用</span></h3> <h4 style="margin-top: 2rem;margin-bottom: 0.5rem;font-weight: 700;font-size: 15px;max-width: 100%;letter-spacing: 0.544px;white-space: normal;color: rgb(255, 140, 0);line-height: 1.35;text-align: start;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: Menlo, Monaco, 'Source Code Pro', Consolas, Inconsolata, 'Ubuntu Mono', 'DejaVu Sans Mono', 'Courier New', 'Droid Sans Mono', 'Hiragino Sans GB', 微软雅黑, monospace !important;"><span style="font-size: 16px;color: rgb(0, 0, 0);">maven 依赖添加 SDK 依赖</span></h4> <pre style="padding: 0.5em;max-width: 100%;letter-spacing: 0.544px;font-family: Courier, 'Courier New', monospace;background: rgb(45, 45, 45);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);overflow-x: auto;color: rgb(204, 204, 204);font-size: 15px;text-align: start;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="max-width: 100%;min-height: 1em;font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;box-sizing: border-box !important;overflow-wrap: break-word !important;"><code style="max-width: 100%;overflow-wrap: normal;font-family: Courier, 'Courier New', monospace;display: inline;overflow: initial;line-height: inherit;border-width: 0px;border-style: initial;border-color: initial;font-size: 13.5px;box-sizing: border-box !important;"> <dependency><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <groupId>io.github.mouzt</groupId><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <artifactId>bizlog-sdk</artifactId><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <version><span style="max-width: 100%;color: rgb(249, 145, 87);box-sizing: border-box !important;overflow-wrap: break-word !important;">1.0</span><span style="max-width: 100%;color: rgb(249, 145, 87);box-sizing: border-box !important;overflow-wrap: break-word !important;">.4</span></version><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> </dependency><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></code></p></pre> <h4 style="margin-top: 2rem;margin-bottom: 0.5rem;font-weight: 700;font-size: 15px;max-width: 100%;letter-spacing: 0.544px;white-space: normal;color: rgb(255, 140, 0);line-height: 1.35;text-align: start;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: Menlo, Monaco, 'Source Code Pro', Consolas, Inconsolata, 'Ubuntu Mono', 'DejaVu Sans Mono', 'Courier New', 'Droid Sans Mono', 'Hiragino Sans GB', 微软雅黑, monospace !important;"><span style="color: rgb(0, 0, 0);font-size: 16px;">SpringBoot 入口打开开关, 添加 @EnableLogRecord 注解</span></h4> <section style="margin-top: 15px;margin-bottom: 15px;max-width: 100%;letter-spacing: 0.544px;white-space: normal;min-height: 1em;color: rgb(80, 97, 109);font-size: 15px;text-align: start;font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;box-sizing: border-box !important;overflow-wrap: break-word !important;"> tenant 是代表租户的标识,一般一个服务或者一个业务下的多个服务都写死一个 tenant 就可以 </section> <pre style="padding: 0.5em;max-width: 100%;letter-spacing: 0.544px;font-family: Courier, 'Courier New', monospace;background: rgb(45, 45, 45);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);overflow-x: auto;color: rgb(204, 204, 204);font-size: 15px;text-align: start;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="max-width: 100%;font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <code style="max-width: 100%;overflow-wrap: normal;font-family: Courier, 'Courier New', monospace;display: inline;overflow: initial;line-height: inherit;border-width: 0px;border-style: initial;border-color: initial;font-size: 13.5px;box-sizing: border-box !important;"><span style="max-width: 100%;color: rgb(249, 145, 87);box-sizing: border-box !important;overflow-wrap: break-word !important;">@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)</span><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(249, 145, 87);box-sizing: border-box !important;overflow-wrap: break-word !important;">@EnableTransactionManagement</span><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;color: rgb(249, 145, 87);box-sizing: border-box !important;overflow-wrap: break-word !important;">@EnableLogRecord(tenant = "com.mzt.test")</span><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-weight: 700;color: rgb(204, 153, 204);box-sizing: border-box !important;overflow-wrap: break-word !important;">public</span> <span style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-weight: 700;color: rgb(204, 153, 204);box-sizing: border-box !important;overflow-wrap: break-word !important;">class</span> <span style="max-width: 100%;color: rgb(102, 153, 204);font-weight: 700;box-sizing: border-box !important;overflow-wrap: break-word !important;">Main</span> </span>{<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-weight: 700;color: rgb(204, 153, 204);box-sizing: border-box !important;overflow-wrap: break-word !important;">public</span> <span style="max-width: 100%;font-weight: 700;color: rgb(204, 153, 204);box-sizing: border-box !important;overflow-wrap: break-word !important;">static</span> <span style="max-width: 100%;font-weight: 700;color: rgb(204, 153, 204);box-sizing: border-box !important;overflow-wrap: break-word !important;">void</span> <span style="max-width: 100%;color: rgb(102, 153, 204);font-weight: 700;box-sizing: border-box !important;overflow-wrap: break-word !important;">main</span><span style="max-width: 100%;color: rgb(249, 145, 87);box-sizing: border-box !important;overflow-wrap: break-word !important;">(String[] args)</span> </span>{<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> SpringApplication.run(Main.class, args);<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> }<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">}<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></code> </section></pre> <h4 style="margin-top: 2rem;margin-bottom: 0.5rem;font-weight: 700;font-size: 15px;max-width: 100%;letter-spacing: 0.544px;white-space: normal;color: rgb(255, 140, 0);line-height: 1.35;text-align: start;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: Menlo, Monaco, 'Source Code Pro', Consolas, Inconsolata, 'Ubuntu Mono', 'DejaVu Sans Mono', 'Courier New', 'Droid Sans Mono', 'Hiragino Sans GB', 微软雅黑, monospace !important;"><strong><span style="font-size: 18px;color: rgb(0, 0, 0);">日志埋点</span></strong></h4> <h6 style="margin-top: 2rem;margin-bottom: 0.5rem;font-weight: 700;max-width: 100%;letter-spacing: 0.544px;white-space: normal;color: rgb(255, 140, 0);line-height: 1.35;text-align: start;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: Menlo, Monaco, 'Source Code Pro', Consolas, Inconsolata, 'Ubuntu Mono', 'DejaVu Sans Mono', 'Courier New', 'Droid Sans Mono', 'Hiragino Sans GB', 微软雅黑, monospace !important;"><span style="color: rgb(0, 0, 0);">1. 普通的记录日志</span></h6> <ul class="list-paddingleft-2" style="padding-left: 20px;max-width: 100%;letter-spacing: 0.544px;white-space: normal;list-style-position: initial;list-style-image: initial;color: rgb(80, 97, 109);font-family: Helvetica, Arial, sans-serif;font-size: 15px;text-align: start;margin-top: 2px !important;overflow-wrap: break-word !important;list-style-type: square !important;"> <li style="max-width: 100%;margin-top: 2px !important;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="max-width: 100%;min-height: 1em;font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;box-sizing: border-box !important;overflow-wrap: break-word !important;"> pefix:是拼接在 bizNo 上作为 log 的一个标识。避免 bizNo 都为整数 ID 的时候和其他的业务中的 ID 重复。比如订单 ID、用户 ID 等 </section></li> <li style="max-width: 100%;margin-top: 2px !important;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="max-width: 100%;min-height: 1em;font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;box-sizing: border-box !important;overflow-wrap: break-word !important;"> bizNo:就是业务的 ID,比如订单 ID,我们查询的时候可以根据 bizNo 查询和它相关的操作日志 </section></li> <li style="max-width: 100%;margin-top: 2px !important;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="max-width: 100%;min-height: 1em;font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;box-sizing: border-box !important;overflow-wrap: break-word !important;"> success:方法调用成功后把 success 记录在日志的内容中 </section></li> <li style="max-width: 100%;margin-top: 2px !important;box-sizing: border-box !important;overflow-wrap: break-word !important;"><p style="max-width: 100%;min-height: 1em;font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;box-sizing: border-box !important;overflow-wrap: break-word !important;">SpEL 表达式:其中用双大括号包围起来的(例如:{{#order.purchaseName}})#order.purchaseName 是 SpEL 表达式。Spring 中支持的它都支持的。比如调用静态方法,三目表达式。SpEL 可以使用方法中的任何参数</p></li> </ul> <pre style="padding: 0.5em;max-width: 100%;letter-spacing: 0.544px;font-family: Courier, 'Courier New', monospace;background: rgb(45, 45, 45);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);overflow-x: auto;color: rgb(204, 204, 204);font-size: 15px;text-align: start;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <section style="max-width: 100%;font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <code style="max-width: 100%;overflow-wrap: normal;font-family: Courier, 'Courier New', monospace;display: inline;overflow: initial;line-height: inherit;border-width: 0px;border-style: initial;border-color: initial;font-size: 13.5px;box-sizing: border-box !important;"> <span style="max-width: 100%;color: rgb(249, 145, 87);box-sizing: border-box !important;overflow-wrap: break-word !important;">@LogRecordAnnotation(success = "{{#order.purchaseName}}下了一个订单,购买商品「{{#order.productName}}」,下单结果:{{#_ret}}",<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> prefix = LogRecordType.ORDER, bizNo = "{{#order.orderNo}}")</span><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><span style="max-width: 100%;font-weight: 700;color: rgb(204, 153, 204);box-sizing: border-box !important;overflow-wrap: break-word !important;">public</span> <span style="max-width: 100%;font-weight: 700;color: rgb(204, 153, 204);box-sizing: border-box !important;overflow-wrap: break-word !important;">boolean</span> <span style="max-width: 100%;color: rgb(102, 153, 204);font-weight: 700;box-sizing: border-box !important;overflow-wrap: break-word !important;">createOrder</span><span style="max-width: 100%;color: rgb(249, 145, 87);box-sizing: border-box !important;overflow-wrap: break-word !important;">(Order order)</span> </span>{<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> log.info(<span style="max-width: 100%;color: rgb(153, 204, 153);box-sizing: border-box !important;overflow-wrap: break-word !important;">"【创建订单】orderNo={}"</span>, order.getOrderNo());<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="max-width: 100%;color: rgb(153, 153, 153);box-sizing: border-box !important;overflow-wrap: break-word !important;">// db insert order</span><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> <span style="max-width: 100%;font-weight: 700;color: rgb(204, 153, 204);box-sizing: border-box !important;overflow-wrap: break-word !important;">return</span> <span style="max-width: 100%;font-weight: 700;color: rgb(204, 153, 204);box-sizing: border-box !important;overflow-wrap: break-word !important;">true</span>;<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"> }<br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"><br style="max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;"></code> </section></pre> <section style="margin-top: 15px;margin-bottom: 15px;max-width: 100%;letter-spacing: 0.544px;white-space: normal;min-height: 1em;color: rgb(80, 97, 109);font-size: 15px;text-align: start;font-family: 'Helvetica Neue', Helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', Arial, sans-serif;box-sizing: border-box !important;overflow-wrap: break-word !important;"> 此时会打印操作日志 “张三下了一个订单, 购买商品「超值优惠红烧肉套餐」, 下单结果: true” </section> <h6 style="margin-top: 2rem;margin-bottom: 0.5rem;font-weight: 700;max-width: 100%;letter-spacing: 0.544px;white-space: normal;color: rgb(255, 140, 0);line-height: 1.35;text-align: start;box-sizing: border-box !important;overflow-wrap: break-word !important;font-family: Menlo, Monaco, 'Source Code Pro', Consolas, Inconsolata, 'Ubuntu Mono', 'DejaVu Sans Mono', 'Courier New', 'Droid Sans Mono', 'Hiragino Sans GB', 微软雅黑, monospace !important;"><span style="color: rgb(0, 0,
作者:小侯
职位描述 Java开发工程师全职(青海)10人 经验要求:3-5年 工作地点: 青海/西宁/城东区 中庄路23号中国工商银行西宁市 城东区 八一东路24号院 工行青海省分行 学历要求:全日制本科/专科 联系人:长沙湘凯信息科技有限责任公司HR小侯 联系电话:13935782298 岗位职责: 从事工行软件系统开发,驻场银行科技中心,长期稳定。 任职资格: 1.有较丰富的软件开发经验,熟悉Oracle数据库,有银行软件开发经验者优先。擅长任意一种语言,包括但不限于C/C++/Java/Python/JavaScript; 2.熟悉常用数据结构与算法; 3.掌握数据库基本理论及基础的应用实践; 4.熟悉Java语言,熟练掌握Spring、SpringMVC、Mybatis、SpringBoot、RESTful等框架,熟练使用MySQL、PostgreSQL等数据库;熟悉Git、Maven等相关工具;了解html、css、js 前端的知识; 5.有良好的计划、沟通、组织协调能力、语言表达能力﹔积极主动、良好的团队合作精神。 薪资待遇: 1、基本6-11K每月,年终奖丰厚; 2、五险一金,各种福利丰厚; 3、银行科技中心驻场,工作环境优越; 4、定期调整薪资,晋升空间大。
作者:小侯
职位描述 Java开发工程师全职(青海)10人 经验要求:3-5年 工作地点: 青海/西宁/城东区 中庄路23号中国工商银行西宁市 城东区 八一东路24号院 工行青海省分行 学历要求:全日制本科/专科 联系人:长沙湘凯信息科技有限责任公司HR小侯 联系电话:13935782298 岗位职责: 从事工行软件系统开发,驻场银行科技中心,长期稳定。 任职资格: 1.有较丰富的软件开发经验,熟悉Oracle数据库,有银行软件开发经验者优先。擅长任意一种语言,包括但不限于C/C++/Java/Python/JavaScript; 2.熟悉常用数据结构与算法; 3.掌握数据库基本理论及基础的应用实践; 4.熟悉Java语言,熟练掌握Spring、SpringMVC、Mybatis、SpringBoot、RESTful等框架,熟练使用MySQL、PostgreSQL等数据库;熟悉Git、Maven等相关工具;了解html、css、js 前端的知识; 5.有良好的计划、沟通、组织协调能力、语言表达能力﹔积极主动、良好的团队合作精神。 薪资待遇: 1、基本6-11K每月,年终奖丰厚; 2、五险一金,各种福利丰厚; 3、银行科技中心驻场,工作环境优越; 4、定期调整薪资,晋升空间大。
作者:微信小助手
<p data-tool="mdnice编辑器" style="margin: 0px;padding: 8px 0px;clear: both;line-height: 26px;"><span style="font-size: 15px;">很多业务场景,如:秒杀商品、微博热搜排行、或者一些活动数据,都是通过跑任务方式,将DB数据批量、集中预热到缓存中,缓存数据有着近乎相同的</span><code style="margin: 0px 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;"><span style="font-size: 15px;">过期时间</span></code><span style="font-size: 15px;">。</span></p> <p data-tool="mdnice编辑器" style="margin: 0px;padding: 8px 0px;clear: both;line-height: 26px;"><span style="font-size: 15px;">当过这批数据过期时,</span><code style="margin: 0px 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;"><span style="font-size: 15px;">会一起过期</span></code><span style="font-size: 15px;">,此时,对这批数据的所有请求,都会出现</span><code style="margin: 0px 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;"><span style="font-size: 15px;">缓存失效</span></code><span style="font-size: 15px;">,从而将压力转嫁到DB,DB的请求量激增,压力变大,响应开始变慢。</span></p> <p data-tool="mdnice编辑器" style="margin: 0px;padding: 8px 0px;clear: both;line-height: 26px;"><strong style="margin: 0px;padding: 0px;">那么有没有解呢?</strong></p> <p data-tool="mdnice编辑器" style="margin: 0px;padding: 8px 0px;clear: both;line-height: 26px;"><span style="font-size: 15px;">当然有了。</span></p> <p data-tool="mdnice编辑器" style="margin: 0px;padding: 8px 0px;clear: both;line-height: 26px;"><span style="font-size: 15px;">我们可以从</span><code style="margin: 0px 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;"><span style="font-size: 15px;">缓存的过期时间入口</span></code><span style="font-size: 15px;">,将原来的固定过期时间,调整为</span><code style="margin: 0px 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;"><span style="font-size: 15px;">过期时间=基础时间+随机时间</span></code><span style="font-size: 15px;">,让缓存慢慢过期,避免瞬间全部过期,对DB产生过大压力。</span></p> <h3 data-tool="mdnice编辑器" style="margin: 30px 0px 15px;padding: 0px;font-weight: bold;font-size: 20px;"><span style="color: rgb(255, 104, 39);font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">2、缓存穿透</span></h3> <p data-tool="mdnice编辑器" style="margin: 0px;padding: 8px 0px;clear: both;line-height: 26px;"><span style="font-size: 15px;">不是所有的请求都能查到数据,不论是从缓存中还是DB中。</span></p> <p data-tool="mdnice编辑器" style="margin: 0px;padding: 8px 0px;clear: both;line-height: 26px;"><span style="font-size: 15px;">假如黑客攻击了一个论坛,用了一堆肉鸡访问一个不存的</span><code style="margin: 0px 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;"><span style="font-size: 15px;">帖子id</span></code><span style="font-size: 15px;">。按照常规思路,每次都会先查缓存,缓存中没有,接着又查DB,同样也没有,此时不会预热到Cache中,导致每次查询,都会</span><code style="margin: 0px 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;"><span style="font-size: 15px;">cache miss</span></code><span style="font-size: 15px;">。</span></p> <p data-tool="mdnice编辑器" style="margin: 0px;padding: 8px 0px;clear: both;line-height: 26px;"><span style="font-size: 15px;">由于DB的吞吐性能较差,会严重影响系统的性能,甚至影响正常用户的访问。</span></p> <p data-tool="mdnice编辑器" style="margin: 0px;padding: 8px 0px;clear: both;line-height: 26px;"><span style="font-size: 15px;"><strong style="margin: 0px;padding: 0px;">解决方案:</strong></span></p> <ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin: 8px 0px;padding: 0px 0px 0px 25px;box-sizing: border-box;width: 557.438px;"> <li style="margin: 0px;padding: 0px;clear: both;"> <section style="margin: 5px 0px;padding: 0px;line-height: 26px;color: rgb(1, 1, 1);"> <span style="font-size: 15px;">方案一:查存DB 时,如果数据不存在,预热一个</span> <code style="margin: 0px 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;"><span style="font-size: 15px;">特殊空值</span></code> <span style="font-size: 15px;">到缓存中。这样,后续查询都会命中缓存,但是要对特殊值,解析处理。</span> </section></li> <li style="margin: 0px;padding: 0px;clear: both;"> <section style="margin: 5px 0px;padding: 0px;line-height: 26px;color: rgb(1, 1, 1);"> <span style="font-size: 15px;">方案二:构造一个</span> <code style="margin: 0px 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;"><span style="font-size: 15px;">BloomFilter</span></code> <span style="font-size: 15px;">过滤器,初始化全量数据,当接到请求时,在</span> <code style="margin: 0px 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;"><span style="font-size: 15px;">BloomFilter</span></code> <span style="font-size: 15px;">中判断这个key是否存在,如果不存在,直接返回即可,无需再查询</span> <code style="margin: 0px 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;"><span style="font-size: 15px;">缓存和DB</span></code> </section></li> </ul> <h3 data-tool="mdnice编辑器" style="margin: 30px 0px 15px;padding: 0px;font-weight: bold;font-size: 20px;"><span style="color: rgb(255, 104, 39);font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">3、缓存雪崩</span></h3> <p data-tool="mdnice编辑器" style="margin: 0px;padding: 8px 0px;clear: both;line-height: 26px;"><span style="font-size: 15px;">缓存雪崩是指部分缓存节点不可用,进而导致整个缓存体系甚至服务系统不可用的情况。</span></p> <p data-tool="mdnice编辑器" style="margin: 0px;padding: 8px 0px;clear: both;line-height: 26px;"><span style="font-size: 15px;">分布式缓存设计一般选择</span><code style="margin: 0px 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;"><span style="font-size: 15px;">一致性Hash</span></code><span style="font-size: 15px;">,当有部分节点异常时,采用 </span><code style="margin: 0px 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;"><span style="font-size: 15px;">rehash</span></code><span style="font-size: 15px;"> 策略,即把异常节点请求平均分散到其他缓存节点。但是,当较大的流量洪峰到来时,如果大流量 key 比较集中,正好在某 1~2 个缓存节点,很容易将这些缓存节点的内存、网卡过载,缓存节点异常 Crash,然后这些异常节点下线,这些大流量 key 请求又被 rehash 到其他缓存节点,进而导致其他缓存节点也被过载 Crash,缓存异常持续扩散,最终导致整个缓存体系异常,无法对外提供服务。</span></p> <p data-tool="mdnice编辑器" style="margin: 0px;padding: 8px 0px;clear: both;line-height: 26px;"><span style="font-size: 15px;"><strong style="margin: 0px;padding: 0px;">解决方案:</strong></span></p> <ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin: 8px 0px;padding: 0px 0px 0px 25px;box-sizing: border-box;width: 557.438px;"> <li style="margin: 0px;padding: 0px;clear: both;font-size: 15px;"> <section style="margin: 5px 0px;padding: 0px;line-height: 26px;color: rgb(1, 1, 1);"> <span style="font-size: 15px;">方案一:增加实时监控,及时预警。通过机器替换、各种故障自动转移策略,快速恢复缓存对外的服务能力</span> </section></li> <li style="margin: 0px;padding: 0px;clear: both;font-size: 15px;"> <section style="margin: 5px 0px;padding: 0px;line-height: 26px;color: rgb(1, 1, 1);"> <span style="font-size: 15px;">方案二:缓存增加多个副本,当缓存异常时,再读取其他缓存副本。为了保证副本的可用性,尽量将多个缓存副本部署在不同机架上,降低风险。</span> </section></li> </ul> <h3 data-tool="mdnice编辑器" style="margin: 30px 0px 15px;padding: 0px;font-weight: bold;font-size: 20px;"><span style="color: rgb(255, 104, 39);font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">4、缓存热点</span></h3> <p data-tool="mdnice编辑器" style="margin: 0px;padding: 8px 0px;clear: both;line-height: 26px;"><span style="font-size: 15px;">对于突发事件,大量用户同时去访问热点信息,这个突发热点信息所在的缓存节点就很容易出现过载和卡顿现象,甚至 Crash,我们称之为缓存热点。</span></p> <p data-tool="mdnice编辑器" style="margin: 0px;padding: 8px 0px;clear: both;line-height: 26px;"><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.9355432780847146" data-s="300,640" src="/upload/c75384d885334650bfbebecb9d05a94a.png" data-type="png" data-w="543" style="width: 449px;height: 420px;"></p> <p><br></p> <p data-tool="mdnice编辑器" style="margin: 0px;padding: 8px 0px;clear: both;line-height: 26px;"><span style="font-size: 15px;">这个在新浪微博经常遇到,某大V明星出轨、结婚、离婚,瞬间引发数百千万的吃瓜群众围观,访问同一个key,流量集中打在一个缓存节点机器,很容易打爆网卡、带宽、CPU的上限,最终导致缓存不可用。<br></span></p> <p data-tool="mdnice编辑器" style="margin: 0px;padding: 8px 0px;clear: both;line-height: 26px;"><span style="font-size: 15px;"><strong style="margin: 0px;padding: 0px;">解决方案:</strong></span></p> <ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin: 8px 0px;padding: 0px 0px 0px 25px;box-sizing: border-box;width: 557.438px;"> <li style="margin: 0px;padding: 0px;clear: both;"> <section style="margin: 5px 0px;padding: 0px;line-height: 26px;color: rgb(1, 1, 1);"> <span style="font-size: 15px;">首先能先找到这个</span> <code style="margin: 0px 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;"><span style="font-size: 15px;">热key</span></code> <span style="font-size: 15px;">来,比如通过</span> <code style="margin: 0px 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;"><span style="font-size: 15px;">Spark</span></code> <span style="font-size: 15px;">实时流分析,及时发现新的热点key。</span> </section></li> <li style="margin: 0px;padding: 0px;clear: both;"> <section style="margin: 5px 0px;padding: 0px;line-height: 26px;color: rgb(1, 1, 1);"> <span style="font-size: 15px;">将集中化流量打散,避免一个缓存节点过载。由于只有一个key,我们可以在key的后面拼上</span> <code style="margin: 0px 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;"><span style="font-size: 15px;">有序编号</span></code> <span style="font-size: 15px;">,比如</span> <code style="margin: 0px 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;"><span style="font-size: 15px;">key#01</span></code> <span style="font-size: 15px;">、</span> <code style="margin: 0px 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;"><span style="font-size: 15px;">key#02</span></code> <span style="font-size: 15px;">。。。</span> <code style="margin: 0px 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;"><span style="font-size: 15px;">key#10</span></code> <span style="font-size: 15px;">多个副本,这些加工后的key位于多个缓存节点上。</span> </section></li> <li style="margin: 0px;padding: 0px;clear: both;font-size: 15px;"> <section style="margin: 5px 0px;padding: 0px;line-height: 26px;color: rgb(1, 1, 1);"> <span style="font-size: 15px;">每次请求时,客户端随机访问一个即可</span> </section></li> </ul> <blockquote data-tool="mdnice编辑器" style="margin: 20px 0px;padding: 10px 10px 10px 20px;border-left: 3px solid rgba(0, 0, 0, 0.4);color: rgb(106, 115, 125);line-height: 1.6;font-size: 0.9em;border-top: none;border-right: none;border-bottom: none;overflow: auto;background: rgba(0, 0, 0, 0.05);"> <p>可以设计一个缓存服务治理管理后台,实时监控缓存的SLA,并打通分布式配置中心,对于一些<code style="margin: 0px 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">hot key</code>可以快速、动态扩容。</p> </blockquote> <h3 data-tool="mdnice编辑器" style="margin: 30px 0px 15px;padding: 0px;font-weight: bold;font-size: 20px;"><span style="color: rgb(255, 104, 39);font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">5、缓存大Key</span></h3> <p data-tool="mdnice编辑器" style="margin: 0px;padding: 8px 0px;clear: both;line-height: 26px;"><span style="font-size: 15px;">当访问缓存时,如果key对应的value过大,读写、加载很容易超时,容易引发网络拥堵。另外缓存的字段较多时,每个字段的变更都会引发缓存数据的变更,频繁的读写,导致慢查询。如果大key过期被缓存淘汰失效,预热数据要花费较多的时间,也会导致慢查询。</span></p> <p data-tool="mdnice编辑器" style="margin: 0px;padding: 8px 0px;clear: both;line-height: 26px;"><span style="font-size: 15px;">所以我们在设计缓存的时候,要注意</span><code style="margin: 0px 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;"><span style="font-size: 15px;">缓存的粒度</span></code><span style="font-size: 15px;">,既不能过大,如果过大很容易导致网络拥堵;也不能过小,如果太小,查询频率会很高,每次请求都要查询多次。</span></p> <p data-tool="mdnice编辑器" style="margin: 0px;padding: 8px 0px;clear: both;line-height: 26px;"><span style="font-size: 15px;"><strong style="margin: 0px;padding: 0px;">解决方案:</strong></span></p> <ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin: 8px 0px;padding: 0px 0px 0px 25px;box-sizing: border-box;width: 557.438px;"> <li style="margin: 0px;padding: 0px;clear: both;font-size: 15px;"> <section style="margin: 5px 0px;padding: 0px;line-height: 26px;color: rgb(1, 1, 1);"> <span style="font-size: 15px;">方案一:设置一个阈值,当value的长度超过阈值时,对内容启动压缩,降低kv的大小</span> </section></li> <li style="margin: 0px;padding: 0px;clear: both;"> <section style="margin: 5px 0px;padding: 0px;line-height: 26px;color: rgb(1, 1, 1);"> <span style="font-size: 15px;">方案二:评估</span> <code style="margin: 0px 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;"><span style="font-size: 15px;">大key</span></code> <span style="font-size: 15px;">所占的比例,由于很多框架采用</span> <code style="margin: 0px 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;"><span style="font-size: 15px;">池化技术</span></code> <span style="font-size: 15px;">,如:Memcache,可以预先分配大对象空间。真正业务请求时,直接拿来即用。</span> </section></li> <li style="margin: 0px;padding: 0px;clear: both;font-size: 15px;"> <section style="margin: 5px 0px;padding: 0px;line-height: 26px;color: rgb(1, 1, 1);"> <span style="font-size: 15px;">方案三:颗粒划分,将大key拆分为多个小key,独立维护,成本会降低不少</span> </section></li> <li style="margin: 0px;padding: 0px;clear: both;font-size: 15px;"> <section style="margin: 5px 0px;padding: 0px;line-height: 26px;color: rgb(1, 1, 1);"> <span style="font-size: 15px;">方案四:大key要设置合理的过期时间,尽量不淘汰那些大key</span> </section></li> </ul> <h3 data-tool="mdnice编辑器" style="margin: 30px 0px 15px;padding: 0px;font-weight: bold;font-size: 20px;"><span style="color: rgb(255, 104, 39);font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">6、缓存数据一致性</span></h3> <p data-tool="mdnice编辑器" style="margin: 0px;padding: 8px 0px;clear: both;line-height: 26px;"><span style="font-size: 15px;">缓存是用来加速的,一般不会持久化储存。所以,一份数据通常会存在</span><code style="margin: 0px 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;"><span style="font-size: 15px;">DB</span></code><span style="font-size: 15px;">和</span><code style="margin: 0px 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;"><span style="font-size: 15px;">缓存</span></code><span style="font-size: 15px;">中,由此会带来一个问题,如何保证这两者的数据一致性。另外,缓存热点问题会引入多个副本备份,也可能会发生不一致现象。</span></p> <p data-tool="mdnice编辑器" style="margin: 0px;padding: 8px 0px;clear: both;line-height: 26px;"><span style="font-size: 15px;"><br></span></p> <p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="0.5249169435215947" data-s="300,640" src="/upload/7908b706642c12e4de0515a2592c0bd8.png" data-type="png" data-w="1204" style=""></p> <p data-tool="mdnice编辑器" style="margin: 0px;padding: 8px 0px;clear: both;line-height: 26px;"><span style="font-size: 15px;"><strong style="margin: 0px;padding: 0px;">解决方案:</strong></span><br></p> <ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin: 8px 0px;padding: 0px 0px 0px 25px;box-sizing: border-box;width: 557.438px;"> <li style="margin: 0px;padding: 0px;clear: both;font-size: 15px;"> <section style="margin: 5px 0px;padding: 0px;line-height: 26px;color: rgb(1, 1, 1);"> <span style="font-size: 15px;">方案一:当缓存更新失败后,进行重试,如果重试失败,将失败的key写入MQ消息队列,通过异步任务补偿缓存,保证数据的一致性。</span> </section></li> <li style="margin: 0px;padding: 0px;clear: both;font-size: 15px;"> <section style="margin: 5px 0px;padding: 0px;line-height: 26px;color: rgb(1, 1, 1);"> <span style="font-size: 15px;">方案二:设置一个较短的过期时间,通过自修复的方式,在缓存过期后,缓存重新加载最新的数据</span> </section></li> </ul> <h3 data-tool="mdnice编辑器" style="margin: 30px 0px 15px;padding: 0px;font-weight: bold;font-size: 20px;"><span style="color: rgb(255, 104, 39);font-size: 15px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;">7、数据并发竞争预热</span></h3> <p data-tool="mdnice编辑器" style="margin: 0px;padding: 8px 0px;clear: both;line-height: 26px;"><span style="font-size: 15px;">互联网系统典型的特点就是流量大,一旦缓存中的数据过期、或因某些原因被删除等,导致缓存中的数据为空,大量的并发线程请求(查询同一个key)就会一起并发查询</span><code style="margin: 0px 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;"><span style="font-size: 15px;">数据库</span></code><span style="font-size: 15px;">,数据库的压力陡然增加。</span></p> <p data-tool="mdnice编辑器" style="margin: 0px;padding: 8px 0px;clear: both;line-height: 26px;"><span style="font-size: 15px;"><br></span></p> <img class="rich_pages js_insertlocalimg" data-ratio="0.763302752293578" data-s="300,640" src="/upload/d269541f516450c278d67b299649208c.jpg" data-type="jpeg" data-w="1090" style="text-align: center;white-space: normal;"> <p data-tool="mdnice编辑器" style="margin: 0px;padding: 8px 0px;clear: both;line-height: 26px;"><span style="font-size: 15px;">如果请求量非常大,全部压在数据库,可能把数据库压垮,进而导致整个系统的服务不可用。</span></p> <p data-tool="mdnice编辑器" style="margin: 0px;padding: 8px 0px;clear: both;line-height: 26px;"><span style="font-size: 15px;"><strong style="margin: 0px;padding: 0px;">解决方案:</strong></span></p> <ul data-tool="mdnice编辑器" class="list-paddingleft-2" style="margin: 8px 0px;padding: 0px 0px 0px 25px;box-sizing: border-box;width: 557.438px;"> <li style="margin: 0px;padding: 0px;clear: both;"> <section style="margin: 5px 0px;padding: 0px;line-height: 26px;color: rgb(1, 1, 1);"> <span style="font-size: 15px;">方案一:引入一把</span> <code style="margin: 0px 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;"><span style="font-size: 15px;">全局锁</span></code> <span style="font-size: 15px;">,当缓存未命中时,先尝试获取全局锁,如果拿到锁,才有资格去查询</span> <code style="margin: 0px 2px;padding: 2px 4px;font-size: 14px;overflow-wrap: break-word;border-radius: 4px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;"><span style="font-size: 15px;">DB</span></code> <span style="font-size: 15px;">,并将数据预热到缓存中。虽然,client端发起的请求非常多,但是由于拿不到锁,只能处于等待状态,当缓存中的数据预热成功后,再从缓存中获取</span> </section></li> </ul>
作者:微信小助手
<h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="font-family: STHeitiSC-Light;color: rgb(14, 136, 235);font-weight: bolder;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(14, 136, 235);">前言</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">四月份的时候,有位好朋友去美团面试。他说,被问到Redis与MySQL双写一致性如何保证?这道题其实就是在问缓存和数据库在双写场景下,一致性是如何保证的?本文将跟大家一起来探讨如何回答这个问题。</p> <p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.5797180043383948" data-s="300,640" src="/upload/d6373ed3b9f2eee61ae57870e4b3c5fb.png" data-type="png" data-w="1844" style=""></p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 公众号: <strong style="font-weight: border;color: #0e88eb;">捡田螺的小男孩</strong> </section></li> </ul> <h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-family: STHeitiSC-Light;color: rgb(14, 136, 235);font-weight: bolder;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(14, 136, 235);">谈谈一致性</span></h2> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.5282392026578073" src="/upload/f18bd9ea71c5362671219e0e5845331.png" data-type="png" data-w="602" style="border-radius: 0px 0px 5px 5px;display: block;margin: 20px auto;width: 85%;height: 100%;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">一致性就是数据保持一致,在分布式系统中,可以理解为多个节点中数据的值是一致的。</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <strong style="font-weight: border;color: #0e88eb;">强一致性</strong>:这种一致性级别是最符合用户直觉的,它要求系统写入什么,读出来的也会是什么,用户体验好,但实现起来往往对系统的性能影响大 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <strong style="font-weight: border;color: #0e88eb;">弱一致性</strong>:这种一致性级别约束了系统在写入成功后,不承诺立即可以读到写入的值,也不承诺多久之后数据能够达到一致,但会尽可能地保证到某个时间级别(比如秒级别)后,数据能够达到一致状态 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <strong style="font-weight: border;color: #0e88eb;">最终一致性</strong>:最终一致性是弱一致性的一个特例,系统会保证在一定时间内,能够达到一个数据一致的状态。这里之所以将最终一致性单独提出来,是因为它是弱一致性中非常推崇的一种一致性模型,也是业界在大型分布式系统的数据一致性上比较推崇的模型 </section></li> </ul> <h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-family: STHeitiSC-Light;color: rgb(14, 136, 235);font-weight: bolder;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(14, 136, 235);">三个经典的缓存模式</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">缓存可以提升性能、缓解数据库压力,但是使用缓存也会导致数据<strong style="font-weight: border;color: #0e88eb;">不一致性</strong>的问题。一般我们是如何使用缓存呢?有三种经典的缓存使用模式:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> Cache-Aside Pattern </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> Read-Through/Write-through </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> Write-behind </section></li> </ul> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;color: rgb(14, 136, 235);"><span style="display: none;"></span>Cache-Aside Pattern<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">Cache-Aside Pattern,即<strong style="font-weight: border;color: #0e88eb;">旁路缓存模式</strong>,它的提出是为了尽可能地解决缓存与数据库的数据不一致问题。</p> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;"><span style="display: none;"></span>Cache-Aside读流程<span style="display: none;"></span></h4> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><strong style="font-weight: border;color: #0e88eb;">Cache-Aside Pattern</strong>的读请求流程如下:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="1.1961904761904762" src="/upload/e3a23e9930b4fe913fa3a285d1994133.png" data-type="png" data-w="525" style="border-radius: 0px 0px 5px 5px;display: block;margin: 20px auto;width: 85%;height: 100%;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> <figcaption style="margin-top: 5px;text-align: center;color: rgb(136, 136, 136);font-size: 12px;"> Cache-Aside读请求 </figcaption> </figure> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 读的时候,先读缓存,缓存命中的话,直接返回数据 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 缓存没有命中的话,就去读数据库,从数据库取出数据,放入缓存后,同时返回响应。 </section></li> </ol> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;"><span style="display: none;"></span>Cache-Aside 写流程<span style="display: none;"></span></h4> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><strong style="font-weight: border;color: #0e88eb;">Cache-Aside Pattern</strong>的写请求流程如下:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="1.2309941520467835" src="/upload/899646d6bec1b16a4be89f8cfebe4c60.png" data-type="png" data-w="342" style="border-radius: 0px 0px 5px 5px;display: block;margin: 20px auto;width: 85%;height: 100%;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> <figcaption style="margin-top: 5px;text-align: center;color: rgb(136, 136, 136);font-size: 12px;"> Cache-Aside写请求 </figcaption> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">更新的时候,先<strong style="font-weight: border;color: #0e88eb;">更新数据库,然后再删除缓存</strong>。</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;color: rgb(14, 136, 235);"><span style="display: none;"></span>Read-Through/Write-Through(读写穿透)<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><strong style="font-weight: border;color: #0e88eb;">Read/Write-Through</strong>模式中,服务端把缓存作为主要数据存储。应用程序跟数据库缓存交互,都是通过<strong style="font-weight: border;color: #0e88eb;">抽象缓存层</strong>完成的。</p> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;"><span style="display: none;"></span>Read-Through<span style="display: none;"></span></h4> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><strong style="font-weight: border;color: #0e88eb;">Read-Through</strong>的简要流程如下</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.2773019271948608" src="/upload/df72f53433b10b2512302e83a99431c7.png" data-type="png" data-w="934" style="border-radius: 0px 0px 5px 5px;display: block;margin: 20px auto;width: 85%;height: 100%;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> <figcaption style="margin-top: 5px;text-align: center;color: rgb(136, 136, 136);font-size: 12px;"> Read-Through简要流程 </figcaption> </figure> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 从缓存读取数据,读到直接返回 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 如果读取不到的话,从数据库加载,写入缓存后,再返回响应。 </section></li> </ol> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">这个简要流程是不是跟<strong style="font-weight: border;color: #0e88eb;">Cache-Aside</strong>很像呢?其实<strong style="font-weight: border;color: #0e88eb;">Read-Through</strong>就是多了一层<strong style="font-weight: border;color: #0e88eb;">Cache-Provider</strong>而已,流程如下:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.6466616654163541" src="/upload/ca2823ee4984f862cf05ee99ee7ad18c.png" data-type="png" data-w="1333" style="border-radius: 0px 0px 5px 5px;display: block;margin: 20px auto;width: 85%;height: 100%;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> <figcaption style="margin-top: 5px;text-align: center;color: rgb(136, 136, 136);font-size: 12px;"> Read-Through流程 </figcaption> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">Read-Through实际只是在<strong style="font-weight: border;color: #0e88eb;">Cache-Aside</strong>之上进行了一层封装,它会让程序代码变得更简洁,同时也减少数据源上的负载。</p> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;"><span style="display: none;"></span>Write-Through<span style="display: none;"></span></h4> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><strong style="font-weight: border;color: #0e88eb;">Write-Through</strong>模式下,当发生写请求时,也是由<strong style="font-weight: border;color: #0e88eb;">缓存抽象层</strong>完成数据源和缓存数据的更新,流程如下:<img data-ratio="0.44329896907216493" src="/upload/79ac6c163f0674daecf0fea8d5daef3b.png" data-type="png" data-w="1358" style="border-radius: 0px 0px 5px 5px;display: block;margin: 20px auto;width: 85%;height: 100%;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"></p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;color: rgb(14, 136, 235);"><span style="display: none;"></span>Write-behind (异步缓存写入)<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><strong style="font-weight: border;color: #0e88eb;">Write-behind</strong> 跟Read-Through/Write-Through有相似的地方,都是由<strong style="font-weight: border;color: #0e88eb;">Cache Provider</strong>来负责缓存和数据库的读写。它们又有个很大的不同:<strong style="font-weight: border;color: #0e88eb;">Read/Write-Through</strong>是同步更新缓存和数据的,<strong style="font-weight: border;color: #0e88eb;">Write-Behind</strong>则是只更新缓存,不直接更新数据库,通过<strong style="font-weight: border;color: #0e88eb;">批量异步</strong>的方式来更新数据库。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.5528191834089436" src="/upload/8e0563cd6ff1f7ddcb84c1c9490fdb1b.png" data-type="png" data-w="1543" style="border-radius: 0px 0px 5px 5px;display: block;margin: 20px auto;width: 85%;height: 100%;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> <figcaption style="margin-top: 5px;text-align: center;color: rgb(136, 136, 136);font-size: 12px;"> Write behind流程 </figcaption> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">这种方式下,缓存和数据库的一致性不强,<strong style="font-weight: border;color: #0e88eb;">对一致性要求高的系统要谨慎使用</strong>。但是它适合频繁写的场景,MySQL的<strong style="font-weight: border;color: #0e88eb;">InnoDB Buffer Pool机制</strong>就使用到这种模式。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-family: STHeitiSC-Light;color: rgb(14, 136, 235);font-weight: bolder;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(14, 136, 235);">操作缓存的时候,到底是删除缓存呢,还是更新缓存?</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">日常开发中,我们一般使用的就是<strong style="font-weight: border;color: #0e88eb;">Cache-Aside</strong>模式。有些小伙伴可能会问, <strong style="font-weight: border;color: #0e88eb;">Cache-Aside</strong>在写入请求的时候,为什么是<strong style="font-weight: border;color: #0e88eb;">删除缓存而不是更新缓存</strong>呢?</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="1.2309941520467835" src="/upload/fe2d973cd54cae0bbd28d426f862b7e3.png" data-type="png" data-w="342" style="border-radius: 0px 0px 5px 5px;display: block;margin: 20px auto;width: 85%;height: 100%;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> <figcaption style="margin-top: 5px;text-align: center;color: rgb(136, 136, 136);font-size: 12px;"> Cache-Aside写入流程 </figcaption> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">我们在操作缓存的时候,到底应该删除缓存还是更新缓存呢?我们先来看个例子:</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.8166849615806806" src="/upload/b4d27a3150ba63e8693344c640d72763.png" data-type="png" data-w="911" style="border-radius: 0px 0px 5px 5px;display: block;margin: 20px auto;width: 85%;height: 100%;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> </figure> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 线程A先发起一个写操作,第一步先更新数据库 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 线程B再发起一个写操作,第二步更新了数据库 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 由于网络等原因,线程B先更新了缓存 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 线程A更新缓存。 </section></li> </ol> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">这时候,缓存保存的是A的数据(老数据),数据库保存的是B的数据(新数据),数据<strong style="font-weight: border;color: #0e88eb;">不一致</strong>了,脏数据出现啦。如果是<strong style="font-weight: border;color: #0e88eb;">删除缓存取代更新缓存</strong>则不会出现这个脏数据问题。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><strong style="font-weight: border;color: #0e88eb;">更新缓存相对于删除缓存</strong>,还有两点劣势:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 如果你写入的缓存值,是经过复杂计算才得到的话。更新缓存频率高的话,就浪费性能啦。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 在写数据库场景多,读数据场景少的情况下,数据很多时候还没被读取到,又被更新了,这也浪费了性能呢(实际上,写多的场景,用缓存也不是很划算的,哈哈) </section></li> </ul> <h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-family: STHeitiSC-Light;color: rgb(14, 136, 235);font-weight: bolder;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(14, 136, 235);">双写的情况下,先操作数据库还是先操作缓存?</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;"><code style="font-size: 14px;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(30, 107, 184);background-color: rgba(27, 31, 35, 0.05);font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;word-break: break-all;">Cache-Aside</code>缓存模式中,有些小伙伴还是会有疑问,在写请求过来的时候,为什么是<strong style="font-weight: border;color: #0e88eb;">先操作数据库呢</strong>?为什么<strong style="font-weight: border;color: #0e88eb;">不先操作缓存</strong>呢?</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">假设有A、B两个请求,请求A做更新操作,请求B做查询读取操作。<img data-ratio="0.8444444444444444" src="/upload/cbe32d25e66e4c3a5a4000e82d859594.png" data-type="png" data-w="1080" style="border-radius: 0px 0px 5px 5px;display: block;margin: 20px auto;width: 85%;height: 100%;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"></p> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 线程A发起一个写操作,第一步del cache </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 此时线程B发起一个读操作,cache miss </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 线程B继续读DB,读出来一个老数据 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 然后线程B把老数据设置入cache </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 线程A写入DB最新的数据 </section></li> </ol> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">酱紫就有问题啦,<strong style="font-weight: border;color: #0e88eb;">缓存和数据库的数据不一致了。缓存保存的是老数据,数据库保存的是新数据</strong>。因此,Cache-Aside缓存模式,选择了先操作数据库而不是先操作缓存。</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 个别小伙伴可能会问,先操作数据库再操作缓存,不一样也会导致数据不一致嘛?它俩又不是原子性操作的。这个是 <strong style="font-weight: border;color: #0e88eb;">会的</strong>,但是这种方式,一般因为删除缓存失败等原因,才会导致脏数据,这个概率就很低。小伙伴们可以画下操作流程图,自己先分析下哈。接下来我们再来分析这种 <strong style="font-weight: border;color: #0e88eb;">删除缓存失败</strong>的情况, <strong style="font-weight: border;color: #0e88eb;">如何保证一致性</strong>。 </section></li> </ul> <h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-family: STHeitiSC-Light;color: rgb(14, 136, 235);font-weight: bolder;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(14, 136, 235);">数据库和缓存数据保持强一致,可以嘛?</span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">实际上,没办法做到数据库与缓存<strong style="font-weight: border;color: #0e88eb;">绝对的一致性</strong>。</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 加锁可以嘛?并发写期间加锁,任何读操作不写入缓存? </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 缓存及数据库封装CAS乐观锁,更新缓存时通过lua脚本? </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 分布式事务,3PC?TCC? </section></li> </ul> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">其实,这是由<strong style="font-weight: border;color: #0e88eb;">CAP理论</strong>决定的。缓存系统适用的场景就是非强一致性的场景,它属于CAP中的AP。<strong style="font-weight: border;color: #0e88eb;">个人觉得,追求绝对一致性的业务场景,不适合引入缓存</strong>。</p> <blockquote data-tool="mdnice编辑器" style="border-width: initial;border-style: none;border-color: initial;font-size: 0.9em;overflow: auto;margin-bottom: 20px;margin-top: 20px;padding-top: 10px;padding-right: 10px;padding-bottom: 10px;line-height: 1.8;border-radius: 0px 0px 10px 10px;color: rgb(14, 136, 235);background: rgb(255, 255, 255);box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> <span style="font-size: 4em;font-family: Arial, serif;line-height: 1em;font-weight: 700;">★</span> <p style="padding-top: 8px;padding-bottom: 8px;letter-spacing: 0.2em;word-spacing: 0.1em;line-height: 26px;font-size: 15px;display: inline;">CAP理论,指的是在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼。</p> <span style="float: right;font-size: 3em;line-height: 1em;">”</span> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">但是,通过一些方案优化处理,是可以<strong style="font-weight: border;color: #0e88eb;">保证弱一致性,最终一致性</strong>的。</p> <h2 data-tool="mdnice编辑器" style="font-weight: bold;font-size: 22px;margin-top: 20px;margin-right: 10px;"><span style="display: none;"></span><span style="font-family: STHeitiSC-Light;color: rgb(14, 136, 235);font-weight: bolder;display: inline-block;padding-left: 10px;border-left: 5px solid rgb(14, 136, 235);">3种方案保证数据库与缓存的一致性</span></h2> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;color: rgb(14, 136, 235);"><span style="display: none;"></span>缓存延时双删<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">有些小伙伴可能会说,并不一定要先操作数据库呀,采用<strong style="font-weight: border;color: #0e88eb;">缓存延时双删</strong>策略,就可以保证数据的一致性啦。什么是延时双删呢?</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="1.6" src="/upload/577174c7167ab076595aa8db4bde738a.png" data-type="png" data-w="370" style="border-radius: 0px 0px 5px 5px;display: block;margin: 20px auto;width: 85%;height: 100%;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> <figcaption style="margin-top: 5px;text-align: center;color: rgb(136, 136, 136);font-size: 12px;"> 延时双删流程 </figcaption> </figure> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 先删除缓存 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 再更新数据库 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 休眠一会(比如1秒),再次删除缓存。 </section></li> </ol> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">这个休眠一会,一般多久呢?都是1秒?</p> <blockquote data-tool="mdnice编辑器" style="border-width: initial;border-style: none;border-color: initial;font-size: 0.9em;overflow: auto;margin-bottom: 20px;margin-top: 20px;padding-top: 10px;padding-right: 10px;padding-bottom: 10px;line-height: 1.8;border-radius: 0px 0px 10px 10px;color: rgb(14, 136, 235);background: rgb(255, 255, 255);box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> <span style="font-size: 4em;font-family: Arial, serif;line-height: 1em;font-weight: 700;">★</span> <p style="padding-top: 8px;padding-bottom: 8px;letter-spacing: 0.2em;word-spacing: 0.1em;line-height: 26px;font-size: 15px;display: inline;">这个休眠时间 = 读业务逻辑数据的耗时 + 几百毫秒。为了确保读请求结束,写请求可以删除读请求可能带来的缓存脏数据。</p> <span style="float: right;font-size: 3em;line-height: 1em;">”</span> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">这种方案还算可以,只有休眠那一会(比如就那1秒),可能有脏数据,一般业务也会接受的。但是如果<strong style="font-weight: border;color: #0e88eb;">第二次删除缓存失败</strong>呢?缓存和数据库的数据还是可能不一致,对吧?给Key设置一个自然的expire过期时间,让它自动过期怎样?那业务要接受<strong style="font-weight: border;color: #0e88eb;">过期时间</strong>内,数据的不一致咯?还是有其他更佳方案呢?</p> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;color: rgb(14, 136, 235);"><span style="display: none;"></span>删除缓存重试机制<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">不管是<strong style="font-weight: border;color: #0e88eb;">延时双删</strong>还是<strong style="font-weight: border;color: #0e88eb;">Cache-Aside的先操作数据库再删除缓存</strong>,都可能会存在第二步的删除缓存失败,导致的数据不一致问题。可以使用这个方案优化:删除失败就多删除几次呀,保证删除缓存成功就可以了呀~ 所以可以引入<strong style="font-weight: border;color: #0e88eb;">删除缓存重试机制</strong></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.551660516605166" src="/upload/1a2a3f4eae6a317244acde387c9496e0.png" data-type="png" data-w="1084" style="border-radius: 0px 0px 5px 5px;display: block;margin: 20px auto;width: 85%;height: 100%;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> <figcaption style="margin-top: 5px;text-align: center;color: rgb(136, 136, 136);font-size: 12px;"> 删除缓存重试流程 </figcaption> </figure> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 写请求更新数据库 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 缓存因为某些原因,删除失败 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 把删除失败的key放到消息队列 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 消费消息队列的消息,获取要删除的key </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 重试删除缓存操作 </section></li> </ol> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;color: rgb(14, 136, 235);"><span style="display: none;"></span>读取biglog异步删除缓存<span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">重试删除缓存机制还可以吧,就是会造成好多<strong style="font-weight: border;color: #0e88eb;">业务代码入侵</strong>。其实,还可以这样优化:通过数据库的<strong style="font-weight: border;color: #0e88eb;">binlog来异步淘汰key</strong>。</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img data-ratio="0.8288288288288288" src="/upload/ac19bf000d41a9c858090a07d668f5a3.png" data-type="png" data-w="1110" style="border-radius: 0px 0px 5px 5px;display: block;margin: 20px auto;width: 85%;height: 100%;object-fit: contain;box-shadow: rgb(132, 161, 168) 0px 10px 15px;"> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;margin: 10px;line-height: 1.75;letter-spacing: 0.2em;font-size: 15px;word-spacing: 0.1em;">以mysql为例吧</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 可以使用阿里的canal将binlog日志采集发送到MQ队列里面 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> 然后通过ACK机制确认处理这条更新消息,删除缓存,保证数据缓存一致性 </section> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <br> </section> <section> 【重要提醒】读者朋友们好,因为微信更改了推送规则,推送不再按照时间线显示,如果不想错过我的文章,请把我的公众号设置「 <strong>星标</strong>」哦,经常点赞,评论也可以防止失联~以及支持鼓励我持续更新 <br> </section> <section> <br> </section> <section class="mp_profile_iframe_wrp"> <mpprofile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-id="Mzg3NzU5NTIwNg==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/PoF8jo1PmpxpY5pHeUUaicHSaqaylBb25Rbib0ocE4ZmXWdicS9tbGvFc9qvbiaNDibKKvuFYqlUdSW6VicXhoLNHfMQ/0?wx_fmt=png" data-nickname="捡田螺的小男孩" data-alias="" data-signature="专注后端技术栈,热爱分享,热爱交朋友,热爱工作总结。毕业于华南理工大学,软件工程专业~" data-from="0"></mpprofile> </section></li> </ul> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;"> <span style="color: rgb(51, 51, 51);font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 17px;text-align: justify;"></span> </section> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);font-size: 15px;text-align: right;"> <span style="font-size: 15px;"><span style="font-size: 15px;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;text-align: right;color: rgb(255, 0, 0);background-color: rgb(255, 255, 255);">跪求大家帮忙给点个</span><strong style="font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;text-align: right;white-space: normal;color: rgb(255, 0, 0);font-size: 10px;background-color: rgb(255, 255, 255);">赞</strong><span style="font-size: 15px;font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;text-align: right;color: rgb(255, 0, 0);background-color: rgb(255, 255, 255);">、</span><strong style="font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;letter-spacing: 0.544px;text-align: right;white-space: normal;color: rgb(255, 0, 0);font-size: 10px;background-color: rgb(255, 255, 255);">在看,感谢!</strong></span> </section>
作者:じ☆ve不哭
# 关闭centos 防火墙 systemctl stop firewalld systemctl status firewalld # chkconfig --list | grep firewalld systemctl disable firewalld 1、安装docker ``` yum install -y -q yum-utils curl -fsSL https://get.docker.com | bash -s docker --mirror aliyun\ ``` 2、启动docker ``` sudo systemctl start docker ``` 3、开始飞桨 1.准备docker ``` mkdir projects #(我采用的该方法)如果您希望在CPU环境下使用docker,使用docker而不是nvidia-docker创建docker sudo docker run --name ppocr -v $PWD:/paddle --network=host -it paddlepaddle/paddle:latest-dev-cuda10.1-cudnn7-gcc82 /bin/bash # 如果使用CUDA10,请运行以下命令创建容器,设置docker容器共享内存shm-size为64G,建议设置32G以上 sudo nvidia-docker run --name ppocr -v $PWD:/paddle --shm-size=64G --network=host -it paddlepaddle/paddle:latest-dev-cuda10.1-cudnn7-gcc82 /bin/bash 您也可以访问[DockerHub](https://hub.docker.com/r/paddlepaddle/paddle/tags/)获取与您机器适配的镜像。 # ctrl+P+Q可退出docker 容器,重新进入docker 容器使用如下命令 sudo docker container exec -it ppocr /bin/bash ``` 2. 安装PaddlePaddle 2.0 ``` pip3 install --upgrade pip # (我采用的该方法)如果您的机器是CPU,请运行以下命令安装 python3 -m pip install paddlepaddle==2.0.0 -i https://mirror.baidu.com/pypi/simple # 如果您的机器安装的是CUDA9或CUDA10,请运行以下命令安装 python3 -m pip install paddlepaddle-gpu==2.0.0 -i https://mirror.baidu.com/pypi/simple 更多的版本需求,请参照[安装文档](https://www.paddlepaddle.org.cn/install/quick)中的说明进行操作。 ``` 3. 克隆PaddleOCR repo代码 ``` 【推荐】git clone https://github.com/PaddlePaddle/PaddleOCR 如果因为网络问题无法pull成功,也可选择使用码云上的托管: git clone https://gitee.com/paddlepaddle/PaddleOCR 注:码云托管代码可能无法实时同步本github项目更新,存在3~5天延时,请优先使用推荐方式。 ``` 4. 安装第三方库 ``` cd PaddleOCR pip3 install -r requirements.txt ``` **提示scikit-image-0.17.2无法安装,将版本改为0.15.0** ## 基于PaddleHub Serving的服务部署 PaddleOCR提供2种服务部署方式: - 基于PaddleHub Serving的部署:代码路径为"`./deploy/hubserving`",按照本教程使用; - 基于PaddleServing的部署:代码路径为"`./deploy/pdserving`",使用方法参考[文档](../../deploy/pdserving/README_CN.md)。 # 基于PaddleHub Serving的服务部署 hubserving服务部署目录下包括检测、识别、2阶段串联三种服务包,请根据需求选择相应的服务包进行安装和启动。目录结构如下: ``` deploy/hubserving/ └─ ocr_cls 分类模块服务包 └─ ocr_det 检测模块服务包 └─ ocr_rec 识别模块服务包 └─ ocr_system 检测+识别串联服务包 ``` 每个服务包下包含3个文件。以2阶段串联服务包为例,目录如下: ``` deploy/hubserving/ocr_system/ └─ __init__.py 空文件,必选 └─ config.json 配置文件,可选,使用配置启动服务时作为参数传入 └─ module.py 主模块,必选,包含服务的完整逻辑 └─ params.py 参数文件,必选,包含模型路径、前后处理参数等参数 ``` ## 快速启动服务 以下步骤以检测+识别2阶段串联服务为例,如果只需要检测服务或识别服务,替换相应文件路径即可。 ### 1. 准备环境 ```shell # 安装paddlehub pip3 install paddlehub==1.8.3 --upgrade -i https://pypi.tuna.tsinghua.edu.cn/simple ``` ### 2. 下载推理模型 安装服务模块前,需要准备推理模型并放到正确路径。默认使用的是v2.0版的超轻量模型,默认模型路径为: ``` 检测模型:./inference/ch_ppocr_mobile_v2.0_det_infer/ 识别模型:./inference/ch_ppocr_mobile_v2.0_rec_infer/ 方向分类器:./inference/ch_ppocr_mobile_v2.0_cls_infer/ ``` **模型路径可在`params.py`中查看和修改。** 更多模型可以从PaddleOCR提供的[模型库](../../doc/doc_ch/models_list.md)下载,也可以替换成自己训练转换好的模型。 * 如果需要使用多语言模型,请同时修改 `ocr_rec/params.py` 中 `cfg.rec_model_dir` 和 `cfg.rec_char_dict_path` 参数,多语言模型和字典文件对应关系可以参考[文档](../../doc/doc_ch/multi_languages.md#预测部署) ### 3. 安装服务模块 PaddleOCR提供3种服务模块,根据需要安装所需模块。 * 在Linux环境下,安装示例如下: ```shell # 安装检测服务模块: hub install deploy/hubserving/ocr_det/ # 或,安装分类服务模块: hub install deploy/hubserving/ocr_cls/ # 或,安装识别服务模块: hub install deploy/hubserving/ocr_rec/ # 或,安装检测+识别串联服务模块: hub install deploy/hubserving/ocr_system/ ``` * 在Windows环境下(文件夹的分隔符为`\`),安装示例如下: ```shell # 安装检测服务模块: hub install deploy\hubserving\ocr_det\ # 或,安装分类服务模块: hub install deploy\hubserving\ocr_cls\ # 或,安装识别服务模块: hub install deploy\hubserving\ocr_rec\ # 或,安装检测+识别串联服务模块: hub install deploy\hubserving\ocr_system\ ``` ### 4. 启动服务 #### 方式1. 命令行命令启动(仅支持CPU) **启动命令:** ```shell $ hub serving start --modules [Module1==Version1, Module2==Version2, ...] \ --port XXXX \ --use_multiprocess \ --workers \ ``` **参数:** |参数|用途| |-|-| |--modules/-m|PaddleHub Serving预安装模型,以多个Module==Version键值对的形式列出<br>*`当不指定Version时,默认选择最新版本`*| |--port/-p|服务端口,默认为8866| |--use_multiprocess|是否启用并发方式,默认为单进程方式,推荐多核CPU机器使用此方式<br>*`Windows操作系统只支持单进程方式`*| |--workers|在并发方式下指定的并发任务数,默认为`2*cpu_count-1`,其中`cpu_count`为CPU核数| 如启动串联服务: ```hub serving start -m ocr_system``` 停止服务:```hub serving stop --port/-p 8866``` 意思是 ``` hub serving stop --port 8866 或者 hub serving stop -p 8866 ``` 这样就完成了一个服务化API的部署,使用默认端口号8866。 #### 请求接口 接口请求:(将图片流base64后发送到接口即可) http://10.10.3.157:8866/predict/ocr_system Content-Type:application/json 参数:{"images":[base64(img)] #### 方式2. 配置文件启动(支持CPU、GPU) **启动命令:** ```hub serving start -c config.json``` 其中,`config.json`格式如下: ```python { "modules_info": { "ocr_system": { "init_args": { "version": "1.0.0", "use_gpu": true }, "predict_args": { } } }, "port": 8868, "use_multiprocess": false, "workers": 2 } ``` - `init_args`中的可配参数与`module.py`中的`_initialize`函数接口一致。其中,**当`use_gpu`为`true`时,表示使用GPU启动服务**。 - `predict_args`中的可配参数与`module.py`中的`predict`函数接口一致。 **注意:** - 使用配置文件启动服务时,其他参数会被忽略。 - 如果使用GPU预测(即,`use_gpu`置为`true`),则需要在启动服务之前,设置CUDA_VISIBLE_DEVICES环境变量,如:```export CUDA_VISIBLE_DEVICES=0```,否则不用设置。 - **`use_gpu`不可与`use_multiprocess`同时为`true`**。 如,使用GPU 3号卡启动串联服务: ```shell export CUDA_VISIBLE_DEVICES=3 hub serving start -c deploy/hubserving/ocr_system/config.json ``` ## 发送预测请求 配置好服务端,可使用以下命令发送预测请求,获取预测结果: ```python tools/test_hubserving.py server_url image_path``` 需要给脚本传递2个参数: - **server_url**:服务地址,格式为 `http://[ip_address]:[port]/predict/[module_name]` 例如,如果使用配置文件启动分类,检测、识别,检测+分类+识别3阶段服务,那么发送请求的url将分别是: `http://127.0.0.1:8865/predict/ocr_det` `http://127.0.0.1:8866/predict/ocr_cls` `http://127.0.0.1:8867/predict/ocr_rec` `http://127.0.0.1:8868/predict/ocr_system` - **image_path**:测试图像路径,可以是单张图片路径,也可以是图像集合目录路径 访问示例: ```python tools/test_hubserving.py http://127.0.0.1:8868/predict/ocr_system ./doc/imgs/``` ## 返回结果格式说明 返回结果为列表(list),列表中的每一项为词典(dict),词典一共可能包含3种字段,信息如下: |字段名称|数据类型|意义| |----|----|----| |angle|str|文本角度| |text|str|文本内容| |confidence|float| 文本识别置信度或文本角度分类置信度| |text_region|list|文本位置坐标| 不同模块返回的字段不同,如,文本识别服务模块返回结果不含`text_region`字段,具体信息如下: | 字段名/模块名 | ocr_det | ocr_cls | ocr_rec | ocr_system | | ---- | ---- | ---- | ---- | ---- | |angle| | √ | | √ | |text| | |√|√| |confidence| |√ |√|√| |text_region| √| | |√ | **说明:** 如果需要增加、删除、修改返回字段,可在相应模块的`module.py`文件中进行修改,完整流程参考下一节自定义修改服务模块。 ## 自定义修改服务模块 如果需要修改服务逻辑,你一般需要操作以下步骤(以修改`ocr_system`为例): - 1、 停止服务 ```hub serving stop --port/-p XXXX``` - 2、 到相应的`module.py`和`params.py`等文件中根据实际需求修改代码。 例如,如果需要替换部署服务所用模型,则需要到`params.py`中修改模型路径参数`det_model_dir`和`rec_model_dir`,如果需要关闭文本方向分类器,则将参数`use_angle_cls`置为`False`,当然,同时可能还需要修改其他相关参数,请根据实际情况修改调试。 **强烈建议修改后先直接运行`module.py`调试,能正确运行预测后再启动服务测试。** - 3、 卸载旧服务包 ```hub uninstall ocr_system``` - 4、 安装修改后的新服务包 ```hub install deploy/hubserving/ocr_system/``` - 5、重新启动服务 ```hub serving start -m ocr_system```
作者:微信小助手
<ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: square;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&chksm=fa496f8ecd3ee698f4954c00efb80fe955ec9198fff3ef4011e331aa37f55a6a17bc8c0335a8&scene=21&token=899450012&lang=zh_CN#wechat_redirect" style="color: rgb(30, 107, 184);font-weight: bold;border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">1、前言与初衷</a> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&chksm=fa496f8ecd3ee698f4954c00efb80fe955ec9198fff3ef4011e331aa37f55a6a17bc8c0335a8&scene=21&token=899450012&lang=zh_CN#wechat_redirect" style="color: rgb(30, 107, 184);font-weight: bold;border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">2、什么是DevOps?</a> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&chksm=fa496f8ecd3ee698f4954c00efb80fe955ec9198fff3ef4011e331aa37f55a6a17bc8c0335a8&scene=21&token=899450012&lang=zh_CN#wechat_redirect" style="color: rgb(30, 107, 184);font-weight: bold;border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">3 涉及软件环境搭建内容</a> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&chksm=fa496f8ecd3ee698f4954c00efb80fe955ec9198fff3ef4011e331aa37f55a6a17bc8c0335a8&scene=21&token=899450012&lang=zh_CN#wechat_redirect" style="color: rgb(30, 107, 184);font-weight: bold;border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">4、需要准备的工作有哪些</a> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&chksm=fa496f8ecd3ee698f4954c00efb80fe955ec9198fff3ef4011e331aa37f55a6a17bc8c0335a8&scene=21&token=899450012&lang=zh_CN#wechat_redirect" style="color: rgb(30, 107, 184);font-weight: bold;border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">5、非多台机器免密远程登录&Jenkins部署详解</a> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&chksm=fa496f8ecd3ee698f4954c00efb80fe955ec9198fff3ef4011e331aa37f55a6a17bc8c0335a8&scene=21&token=899450012&lang=zh_CN#wechat_redirect" style="color: rgb(30, 107, 184);font-weight: bold;border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">5、多台机器免密远程登录&Jenkins部署流程详解</a> </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> <a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&chksm=fa496f8ecd3ee698f4954c00efb80fe955ec9198fff3ef4011e331aa37f55a6a17bc8c0335a8&scene=21&token=899450012&lang=zh_CN#wechat_redirect" style="color: rgb(30, 107, 184);font-weight: bold;border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="1">7、总结&建议&学习<span class="js_jump_icon h5_image_link" data-positionback="static" style="inset: auto;margin: 0px;"><img data-ratio="0.6905158069883528" src="/upload/cd628936751364d89d47aa6783692c5c.jpg" data-type="jpeg" data-w="1202" style="margin: 0px;"></span></a> </section></li> </ul> <hr data-tool="mdnice编辑器" style="height: 1px;margin-top: 10px;margin-bottom: 10px;border-right: none;border-bottom: none;border-left: none;border-top-style: solid;border-top-color: black;"> <h2 data-tool="mdnice编辑器" style="font-size: 22px;text-align: center;font-weight: bold;line-height: 1.1em;padding-top: 12px;padding-bottom: 12px;margin: 70px 30px 30px;border-width: 1px;border-style: solid;border-color: rgb(0, 0, 0);"><span style="float: left;display: block;width: 90%;border-top: 1px solid #000;height: 1px;line-height: 1px;margin-left: -5px;margin-top: -17px;"> </span><span style="display: block;width: 3px;margin-left: 5%;height: 3px;line-height: 3px;overflow: hidden;background-color: rgb(0, 0, 0);box-shadow: rgb(0, 0, 0) 3px 0px, rgb(0, 0, 0) 0px 3px, rgb(0, 0, 0) -3px 0px, rgb(0, 0, 0) 0px -3px;"></span><span style="display: block;-webkit-box-reflect: below 0em -webkit-gradient(linear,left top,left bottom, from(rgba(0,0,0,0)),to(rgba(255,255,255,0.1)));"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&scene=21#wechat_redirect" style="color: rgb(30, 107, 184);border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">1、前言与初衷</a></span><span style="display: block;width: 3px;margin-left: 95%;height: 3px;line-height: 3px;overflow: hidden;background-color: rgb(0, 0, 0);box-shadow: rgb(0, 0, 0) 3px 0px, rgb(0, 0, 0) 0px 3px, rgb(0, 0, 0) -3px 0px, rgb(0, 0, 0) 0px -3px;"></span><span style="float: right;display: block;width: 90%;border-bottom: 1px solid #000;height: 1px;line-height: 1px;margin-right: -5px;margin-top: 16px;"> </span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">本文章会涉及Docker常见命令基础知识点结合不同场景实操一起使用。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">本文章会涉及结合工作过程中部署不同环境服务器的项目案例场景为初心进行实际细讲。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">本文章主要讲述Docker、Jenkins、GitLab、Git、JDK、SpringBoot、Maven等技术结合实现自动化运维部署(DevOps)应用工程,适合SpringCloud部署。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">初衷想法:在学习过程中遇到比较有趣的问题、然而花了点心血和时间去整理,然而进行梳理出来一份文章比较完整有知识体系的DevOps自动化构建与部署工程文章,技术知识内容比较多,而且文章内容较长,然而分了几个章程来讲述</p> <h2 data-tool="mdnice编辑器" style="font-size: 22px;text-align: center;font-weight: bold;line-height: 1.1em;padding-top: 12px;padding-bottom: 12px;margin: 70px 30px 30px;border-width: 1px;border-style: solid;border-color: rgb(0, 0, 0);"><span style="float: left;display: block;width: 90%;border-top: 1px solid #000;height: 1px;line-height: 1px;margin-left: -5px;margin-top: -17px;"> </span><span style="display: block;width: 3px;margin-left: 5%;height: 3px;line-height: 3px;overflow: hidden;background-color: rgb(0, 0, 0);box-shadow: rgb(0, 0, 0) 3px 0px, rgb(0, 0, 0) 0px 3px, rgb(0, 0, 0) -3px 0px, rgb(0, 0, 0) 0px -3px;"></span><span style="display: block;-webkit-box-reflect: below 0em -webkit-gradient(linear,left top,left bottom, from(rgba(0,0,0,0)),to(rgba(255,255,255,0.1)));"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&scene=21#wechat_redirect" style="color: rgb(30, 107, 184);border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">2、什么是DevOps?</a></span><span style="display: block;width: 3px;margin-left: 95%;height: 3px;line-height: 3px;overflow: hidden;background-color: rgb(0, 0, 0);box-shadow: rgb(0, 0, 0) 3px 0px, rgb(0, 0, 0) 0px 3px, rgb(0, 0, 0) -3px 0px, rgb(0, 0, 0) 0px -3px;"></span><span style="float: right;display: block;width: 90%;border-bottom: 1px solid #000;height: 1px;line-height: 1px;margin-right: -5px;margin-top: 16px;"> </span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">DevOps(Development和Operations的组合词)是一组过程、方法与系统的统称,用于促进开发(应用程序/软件工程)、技术运营和质量保障(QA)部门之间的沟通、协作与整合,它是一种重视“软件开发人员(Dev)”和“IT运维技术人员(Ops)”之间沟通合作的文化、运动或惯例。透过自动化“软件交付”和“架构变更”的流程,来使得构建、测试、发布软件能够更加地快捷、频繁和可靠。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">它的出现是由于软件行业日益清晰地认识到:为了按时交付软件产品和服务,开发和运营工作必须紧密合作。</p> <h2 data-tool="mdnice编辑器" style="font-size: 22px;text-align: center;font-weight: bold;line-height: 1.1em;padding-top: 12px;padding-bottom: 12px;margin: 70px 30px 30px;border-width: 1px;border-style: solid;border-color: rgb(0, 0, 0);"><span style="float: left;display: block;width: 90%;border-top: 1px solid #000;height: 1px;line-height: 1px;margin-left: -5px;margin-top: -17px;"> </span><span style="display: block;width: 3px;margin-left: 5%;height: 3px;line-height: 3px;overflow: hidden;background-color: rgb(0, 0, 0);box-shadow: rgb(0, 0, 0) 3px 0px, rgb(0, 0, 0) 0px 3px, rgb(0, 0, 0) -3px 0px, rgb(0, 0, 0) 0px -3px;"></span><span style="display: block;-webkit-box-reflect: below 0em -webkit-gradient(linear,left top,left bottom, from(rgba(0,0,0,0)),to(rgba(255,255,255,0.1)));"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&scene=21#wechat_redirect" style="color: rgb(30, 107, 184);border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">3 涉及软件环境搭建内容</a></span><span style="display: block;width: 3px;margin-left: 95%;height: 3px;line-height: 3px;overflow: hidden;background-color: rgb(0, 0, 0);box-shadow: rgb(0, 0, 0) 3px 0px, rgb(0, 0, 0) 0px 3px, rgb(0, 0, 0) -3px 0px, rgb(0, 0, 0) 0px -3px;"></span><span style="float: right;display: block;width: 90%;border-bottom: 1px solid #000;height: 1px;line-height: 1px;margin-right: -5px;margin-top: 16px;"> </span></h2> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: square;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 如何在Centos7安装JDK1.8-u121详解 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 如何在Centos7安装Maven3.6.1详解 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 如何在Centos7安装Git详解 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 如何在CentOS7与Git配置免密码登陆详解 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 如何在Docker安装GitLab详解 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 如何在Docker创建NetWork网络详解 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 如何在Docker安装Registry私服详解 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 如何在Docker安装Jenkins详解 </section></li> </ul> <blockquote data-tool="mdnice编辑器" style="border-top: none;border-bottom: none;font-size: 0.9em;overflow: auto;color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;margin-bottom: 20px;margin-top: 20px;border-left-color: rgba(0, 0, 0, 0.65);border-right: 1px solid rgba(0, 0, 0, 0.65);background: rgb(249, 249, 249);"> <p style="padding-top: 8px;padding-bottom: 8px;font-size: 14px;color: black;line-height: 26px;">见附录</p> </blockquote> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">特别说明</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <a target="_blank" href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&chksm=fa496f8ecd3ee698f4954c00efb80fe955ec9198fff3ef4011e331aa37f55a6a17bc8c0335a8&scene=21&token=899450012&lang=zh_CN#wechat_redirect" textvalue="你已选中了添加链接的内容" tab="innerlink" data-linktype="1"><span class="js_jump_icon h5_image_link" data-positionback="static" style="inset: auto;margin: 10px 0px 0px;"><img data-ratio="0.16622340425531915" src="/upload/6e2fb22b6328b8a937ebc80046eb19d5.png" data-type="png" data-w="752" style="display: block;margin: 0px;box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;"></span></a> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 12px;"> 图片 </figcaption> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">1、如何使用Maven结合Docker把SpringBoot应用编译成可用的镜像进行部署。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">2、其中JDK和Maven是传统方式进行安装,由于本人Centos操作系统是有其他软件依赖它们,有时候传统方式安装软件会更好,这里不过多的阐述。有些软件在Docker安装过程与使用过程并没传统方式的简单,比如:Jenkins。</p> <h2 data-tool="mdnice编辑器" style="font-size: 22px;text-align: center;font-weight: bold;line-height: 1.1em;padding-top: 12px;padding-bottom: 12px;margin: 70px 30px 30px;border-width: 1px;border-style: solid;border-color: rgb(0, 0, 0);"><span style="float: left;display: block;width: 90%;border-top: 1px solid #000;height: 1px;line-height: 1px;margin-left: -5px;margin-top: -17px;"> </span><span style="display: block;width: 3px;margin-left: 5%;height: 3px;line-height: 3px;overflow: hidden;background-color: rgb(0, 0, 0);box-shadow: rgb(0, 0, 0) 3px 0px, rgb(0, 0, 0) 0px 3px, rgb(0, 0, 0) -3px 0px, rgb(0, 0, 0) 0px -3px;"></span><span style="display: block;-webkit-box-reflect: below 0em -webkit-gradient(linear,left top,left bottom, from(rgba(0,0,0,0)),to(rgba(255,255,255,0.1)));"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be9d1fdef8857d9&scene=21#wechat_redirect" style="color: rgb(30, 107, 184);border-bottom: 1px solid rgb(30, 107, 184);" data-linktype="2">4、需要准备的工作有哪些</a></span><span style="display: block;width: 3px;margin-left: 95%;height: 3px;line-height: 3px;overflow: hidden;background-color: rgb(0, 0, 0);box-shadow: rgb(0, 0, 0) 3px 0px, rgb(0, 0, 0) 0px 3px, rgb(0, 0, 0) -3px 0px, rgb(0, 0, 0) 0px -3px;"></span><span style="float: right;display: block;width: 90%;border-bottom: 1px solid #000;height: 1px;line-height: 1px;margin-right: -5px;margin-top: 16px;"> </span></h2> <h4 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 18px;"><span style="display: none;"></span><a href="https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247487551&idx=1&sn=18f64ba49f3f0f9d8be
作者:微信小助手
<p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">ELK 是一个开源的实时日志分析平台,它主要由 Elasticsearch、Logstash 和 Kiabana 三部分组成。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><strong style="letter-spacing: 0px;color: rgb(171, 25, 66);"><span style="letter-spacing: 0px;font-size: 15px;">Logstash</span></strong></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Logstash 主要用于收集服务器日志,它是一个开源数据收集引擎,具有实时管道功能。Logstash 可以动态地将来自不同数据源的数据统一起来,并将数据标准化到您所选择的目的地。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Logstash 收集数据的过程主要分为以下三个部分:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: square;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 输入:数据(包含但不限于日志)往往都是以不同的形式、格式存储在不同的系统中,而 Logstash 支持从多种数据源中收集数据(File、Syslog、MySQL、消息中间件等等)。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 过滤器:实时解析和转换数据,识别已命名的字段以构建结构,并将它们转换成通用格式。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 输出:Elasticsearch 并非存储的唯一选择,Logstash 提供很多输出选择。 </section></li> </ul> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span><span style="font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 14px;letter-spacing: 0px;text-align: left;"><strong style="color: rgb(171, 25, 66);white-space: normal;"><span style="font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0px;font-size: 15px;text-align: left;">Elasticsearch</span></strong></span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Elasticsearch (ES)是一个分布式的 Restful 风格的搜索和数据分析引擎,它具有以下特点:</p> <ul data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;list-style-type: square;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 查询:允许执行和合并多种类型的搜索 — 结构化、非结构化、地理位置、度量指标 — 搜索方式随心而变。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 分析:Elasticsearch 聚合让您能够从大处着眼,探索数据的趋势和模式。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 速度:很快,可以做到亿万级的数据,毫秒级返回。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 可扩展性:可以在笔记本电脑上运行,也可以在承载了 PB 级数据的成百上千台服务器上运行。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 弹性:运行在一个分布式的环境中,从设计之初就考虑到了这一点。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 灵活性:具备多个案例场景。支持数字、文本、地理位置、结构化、非结构化,所有的数据类型都欢迎。 </section></li> </ul> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span><span style="font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 14px;letter-spacing: 0px;text-align: left;"><strong style="color: rgb(171, 25, 66);white-space: normal;"><span style="font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0px;font-size: 15px;text-align: left;">Kibana</span></strong></span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">Kibana 可以使海量数据通俗易懂。它很简单,基于浏览器的界面便于您快速创建和分享动态数据仪表板来追踪 Elasticsearch 的实时数据变化。其搭建过程也十分简单,您可以分分钟完成 Kibana 的安装并开始探索 Elasticsearch 的索引数据 — 没有代码、不需要额外的基础设施。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">对于以上三个组件在 《ELK 协议栈介绍及体系结构》 一文中有具体介绍,这里不再赘述。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在 ELK 中,三大组件的大概工作流程如下图所示,由 Logstash 从各个服务中采集日志并存放至 Elasticsearch 中,然后再由 Kiabana 从 Elasticsearch 中查询日志并展示给终端用户。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><strong>图 1. ELK 的大致工作流程</strong></p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img src="/upload/e43556df4c98a5af6ff39a1e3d6c3f91.png" data-cropx1="0" data-cropx2="1080" data-cropy1="0" data-cropy2="427.7419354838709" data-ratio="0.3962962962962963" src="https://mmbiz.qpic.cn/mmbiz_jpg/eZzl4LXykQxVJ0WmQ3rKgbiaTSOe37EWh0Myx6keSINrNJe0474RnAICoSiaFpPTRl36icsJibHSYVjOpDXNY5hqyg/640?wx_fmt=jpeg" data-type="jpeg" data-w="1080" style="display: block;margin-right: auto;margin-left: auto;box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;height: 221px;width: 558px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 12px;"> 图片 </figcaption> </figure> <h2 data-tool="mdnice编辑器" style="font-size: 22px;text-align: center;font-weight: bold;line-height: 1.1em;padding-top: 12px;padding-bottom: 12px;margin: 70px 30px 30px;border-width: 1px;border-style: solid;border-color: rgb(0, 0, 0);"><span style="float: left;display: block;width: 90%;border-top: 1px solid #000;height: 1px;line-height: 1px;margin-left: -5px;margin-top: -17px;"> </span><span style="display: block;width: 3px;margin-left: 5%;height: 3px;line-height: 3px;overflow: hidden;background-color: rgb(0, 0, 0);box-shadow: rgb(0, 0, 0) 3px 0px, rgb(0, 0, 0) 0px 3px, rgb(0, 0, 0) -3px 0px, rgb(0, 0, 0) 0px -3px;"></span><span style="font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 14px;letter-spacing: 0px;text-align: left;"><strong style="color: rgb(171, 25, 66);white-space: normal;"><span style="font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0px;font-size: 15px;text-align: left;">ELK 实现方案</span></strong></span><span style="display: block;width: 3px;margin-left: 95%;height: 3px;line-height: 3px;overflow: hidden;background-color: rgb(0, 0, 0);box-shadow: rgb(0, 0, 0) 3px 0px, rgb(0, 0, 0) 0px 3px, rgb(0, 0, 0) -3px 0px, rgb(0, 0, 0) 0px -3px;"></span><span style="float: right;display: block;width: 90%;border-bottom: 1px solid #000;height: 1px;line-height: 1px;margin-right: -5px;margin-top: 16px;"> </span></h2> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">通常情况下我们的服务都部署在不同的服务器上,那么如何从多台服务器上收集日志信息就是一个关键点了。本篇文章中提供的解决方案如下图所示:</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">图 2. 本文提供的 ELK 实现方案</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img src="/upload/fe61d79bbf18eda8f39c3ce4bc54d88f.png" data-cropx1="0" data-cropx2="1080" data-cropy1="0" data-cropy2="462.58064516129036" data-ratio="0.42777777777777776" src="https://mmbiz.qpic.cn/mmbiz_jpg/eZzl4LXykQxVJ0WmQ3rKgbiaTSOe37EWhFCOEhcYGpu3DmvibJGnsSakm3MF0TNdOjeWtOvYhIUeajAQWyibd58gA/640?wx_fmt=jpeg" data-type="jpeg" data-w="1080" style="display: block;box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;inset: auto;height: 239px;width: 558px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 12px;"> 图片 </figcaption> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">如上图所示,整个 ELK 的运行流程如下:</p> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 在微服务(产生日志的服务)上部署一个 Logstash,作为 Shipper 角色,主要负责对所在机器上的服务产生的日志文件进行数据采集,并将消息推送到 Redis 消息队列。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 另用一台服务器部署一个 Indexer 角色的 Logstash,主要负责从 Redis 消息队列中读取数据,并在 Logstash 管道中经过 Filter 的解析和处理后输出到 Elasticsearch 集群中存储。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> Elasticsearch 主副节点之间数据同步。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 单独一台服务器部署 Kibana 读取 Elasticsearch 中的日志数据并展示在 Web 页面。 </section></li> </ol> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">通过这张图,相信您已经大致清楚了我们将要搭建的 ELK 平台的工作流程,以及所需组件。下面就让我们一起开始搭建起来吧。</p> <p data-tool="mdnice编辑器" style="color: rgb(0, 0, 0);font-size: 16px;text-align: justify;letter-spacing: 0.544px;background-color: rgb(255, 255, 255);"><span style="font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 14px;letter-spacing: 0px;text-align: left;"><strong style="color: rgb(171, 25, 66);white-space: normal;"><span style="font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0px;font-size: 15px;text-align: left;">ELK 平台搭建</span></strong></span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><span style="background-color: rgb(0, 0, 0);font-size: 22px;font-weight: bold;text-align: center;letter-spacing: 0px;"><br></span></p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;"><span style="background-color: rgb(0, 0, 0);font-size: 22px;font-weight: bold;text-align: center;letter-spacing: 0px;"></span><span style="font-weight: bold;letter-spacing: 0px;">本节主要介绍搭建 ELK 日志平台,包括安装 Indexer 角色的 Logstash,Elasticsearc</span><span style="font-weight: bold;letter-spacing: 0px;">h 以及 Kibana 三个组件。</span><span style="font-weight: bold;letter-spacing: 0px;">完成本小节,您需要做如下准备:</span></p> <ol data-tool="mdnice编辑器" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;" class="list-paddingleft-2"> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 一台 Ubuntu 机器或虚拟机,作为入门教程,此处省略了 Elasticsearch 集群的搭建,且将 Logstash(Indexer)、Elasticsearch 以及 Kibana 安装在同一机器上。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> 在 Ubuntu 上安装 JDK,注意 Logstash 要求 JDK 在 1.7 版本以上。 </section></li> <li> <section style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;color: rgb(1, 1, 1);"> Logstash、Elasticsearch、Kibana 安装包,您可以在 此页面 下载。 </section></li> </ol> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span><span style="font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 14px;letter-spacing: 0px;text-align: left;"><strong style="color: rgb(171, 25, 66);white-space: normal;"><span style="font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0px;font-size: 15px;text-align: left;">安装 Logstash</span></strong></span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">解压压缩包:</p> <pre data-tool="mdnice编辑器" style="box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;margin-right: auto;margin-left: auto;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/hzVGicX27IG3g2ZGN2gJDUaPeEgL6N9CStAkFPwnTgiaSccZlibdK5q7axrM2Kn0BT96LGf0FRy54LKMtG1qcd7039qncvxJt0w/640?wx_fmt=svg&random=0.0054461041113003095") 10px 10px / 40px no-repeat rgb(30, 30, 30);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #DCDCDC;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #1E1E1E;border-radius: 5px;">tar -xzvf logstash-<span style="color: #B8D7A3;line-height: 26px;">7.3</span><span style="color: #B8D7A3;line-height: 26px;">.0</span>.tar.gz<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">显示更多简单用例测试,进入到解压目录,并启动一个将控制台输入输出到控制台的管道。</p> <pre data-tool="mdnice编辑器" style="box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;margin-right: auto;margin-left: auto;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/hzVGicX27IG3g2ZGN2gJDUaPeEgL6N9CStAkFPwnTgiaSccZlibdK5q7axrM2Kn0BT96LGf0FRy54LKMtG1qcd7039qncvxJt0w/640?wx_fmt=svg&random=0.8092089296011502") 10px 10px / 40px no-repeat rgb(30, 30, 30);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #DCDCDC;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #1E1E1E;border-radius: 5px;">cd logstash-<span style="color: #B8D7A3;line-height: 26px;">7.3</span><span style="color: #B8D7A3;line-height: 26px;">.0</span><br>elk<span style="color: #9B9B9B;line-height: 26px;">@elk</span>:~/elk/logstash-<span style="color: #B8D7A3;line-height: 26px;">7.3</span><span style="color: #B8D7A3;line-height: 26px;">.0</span>$ bin/logstash -e <span style="color: #D69D85;line-height: 26px;">'input { stdin {} } output { { stdout {} } }'</span><br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">显示更多看到如下日志就意味着 Logstash 启动成功。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">图 3. Logstash 启动成功日志</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img src="/upload/3436e8a66cc4c1f74477fb50af92ec31.png" data-cropx1="0" data-cropx2="1024" data-cropy1="0" data-cropy2="651.4695340501792" data-ratio="0.6357421875" src="https://mmbiz.qpic.cn/mmbiz_jpg/eZzl4LXykQxVJ0WmQ3rKgbiaTSOe37EWhz3YTZ5PAum8CVVJrZIvnibWu8V0XRS2F2Wdg3j2bW8xm7mtW2xNDoXA/640?wx_fmt=jpeg" data-type="jpeg" data-w="1024" style="display: block;box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;inset: auto;height: 355px;width: 558px;"> <figcaption style="margin-top: 5px;text-align: center;color: #888;font-size: 12px;"> 图片 </figcaption> </figure> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在控制台输入 Hello Logstash ,看到如下效果代表 Logstash 安装成功。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">清单 1. 验证 Logstash 是否启动成功Hello Logstash</p> <pre data-tool="mdnice编辑器" style="box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;margin-right: auto;margin-left: auto;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/hzVGicX27IG3g2ZGN2gJDUaPeEgL6N9CStAkFPwnTgiaSccZlibdK5q7axrM2Kn0BT96LGf0FRy54LKMtG1qcd7039qncvxJt0w/640?wx_fmt=svg&random=0.19375358660324205") 10px 10px / 40px no-repeat rgb(30, 30, 30);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #DCDCDC;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #1E1E1E;border-radius: 5px;">{<br> <span style="color: #9CDCFE;line-height: 26px;">"@timestamp"</span> =&gt; 2019-08-10T16:<span style="color: #B8D7A3;line-height: 26px;">11</span>:<span style="color: #B8D7A3;line-height: 26px;">10.040</span>Z,<br> <span style="color: #9CDCFE;line-height: 26px;">"host"</span> =&gt; <span style="color: #9CDCFE;line-height: 26px;">"elk"</span>,<br> <span style="color: #9CDCFE;line-height: 26px;">"@version"</span> =&gt; <span style="color: #9CDCFE;line-height: 26px;">"1"</span>,<br> <span style="color: #9CDCFE;line-height: 26px;">"message"</span> =&gt; <span style="color: #9CDCFE;line-height: 26px;">"Hello Logstash"</span><br>}<br></code></pre> <h3 data-tool="mdnice编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;font-size: 20px;"><span style="display: none;"></span><span style="font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 14px;letter-spacing: 0px;text-align: left;"><strong style="color: rgb(171, 25, 66);white-space: normal;"><span style="font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0px;font-size: 15px;text-align: left;">安装 Elasticsearch</span></strong></span><span style="display: none;"></span></h3> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">解压安装包:</p> <pre data-tool="mdnice编辑器" style="box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;margin-right: auto;margin-left: auto;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/hzVGicX27IG3g2ZGN2gJDUaPeEgL6N9CStAkFPwnTgiaSccZlibdK5q7axrM2Kn0BT96LGf0FRy54LKMtG1qcd7039qncvxJt0w/640?wx_fmt=svg&random=0.02710415941085964") 10px 10px / 40px no-repeat rgb(30, 30, 30);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #DCDCDC;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #1E1E1E;border-radius: 5px;">tar -xzvf elasticsearch-<span style="color: #B8D7A3;line-height: 26px;">7.3</span><span style="color: #B8D7A3;line-height: 26px;">.0</span>-linux-x86_64.tar.gz<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">启动 Elasticsearch:</p> <pre data-tool="mdnice编辑器" style="box-shadow: rgba(170, 170, 170, 0.48) 0px 0px 6px 0px;border-radius: 4px;margin-top: 10px;margin-right: auto;margin-left: auto;"><span style="display: block;background: url("https://mmbiz.qpic.cn/mmbiz_svg/hzVGicX27IG3g2ZGN2gJDUaPeEgL6N9CStAkFPwnTgiaSccZlibdK5q7axrM2Kn0BT96LGf0FRy54LKMtG1qcd7039qncvxJt0w/640?wx_fmt=svg&random=0.2268962922905915") 10px 10px / 40px no-repeat rgb(30, 30, 30);height: 30px;width: 100%;margin-bottom: -7px;border-radius: 5px;"></span><code style="overflow-x: auto;padding: 16px;color: #DCDCDC;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;font-size: 12px;-webkit-overflow-scrolling: touch;padding-top: 15px;background: #1E1E1E;border-radius: 5px;">cd elasticsearch-<span style="color: #B8D7A3;line-height: 26px;">7.3</span><span style="color: #B8D7A3;line-height: 26px;">.0</span>/<br>bin/elasticsearch<br></code></pre> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">在启动 Elasticsearch 的过程中我遇到了两个问题在这里列举一下,方便大家排查。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">问题一 :内存过小,如果您的机器内存小于 Elasticsearch 设置的值,就会报下图所示的错误。解决方案是,修改 elasticsearch-7.3.0/config/jvm.options 文件中的如下配置为适合自己机器的内存大小,若修改后还是报这个错误,可重新连接服务器再试一次。</p> <p data-tool="mdnice编辑器" style="padding-top: 8px;padding-bottom: 8px;line-height: 26px;">图 4. 内存过小导致 Elasticsearch 启动报错</p> <figure data-tool="mdnice编辑器" style="margin-top: 10px;margin-bottom: 10px;display: flex;flex-direction: column;justify-content: center;align-items: center;"> <img src="/upload/f534f20cf6d1b2cc1347aab1b2229614.png" data-cropx1="0" data-cropx2="1024" data-cropy1="0" data-cropy2="420.2437275985663" data-ratio="0.4091796875" src="https://mmbiz.qpic.cn/mmbiz_jpg/eZzl4LXykQxVJ0WmQ3rKgbiaTSOe37EWhicEFd6szDibPsMibA2b1ic05TGwmdQKDzFL0D5LicA
作者:微信小助手
<p data-lake-id="f47942fff38c686ed626a530b6d84219" style="text-align: center;font-size: 15px;color: rgb(64, 64, 64);line-height: 1.74;letter-spacing: 0.008em;outline-style: none;overflow-wrap: break-word;" data-mpa-powered-by="yiban.io"><span data-mce-style="font-size: 10px" style="font-size: 13px;color: rgb(136, 136, 136);">点击上方蓝色“</span><span style="color: rgb(24, 144, 255);font-size: 13px;" data-mce-style="font-size: 10px">后端面试那些事儿</span><span data-mce-style="font-size: 10px" style="font-size: 13px;color: rgb(136, 136, 136);">”,选择“设为星标”</span></p> <p data-lake-id="eca2a1864e13b3e1b84aafe9cb4abdff" style="text-align: center;font-size: 15px;color: rgb(64, 64, 64);line-height: 1.74;letter-spacing: 0.008em;outline-style: none;overflow-wrap: break-word;"><span style="color: rgb(140, 140, 140);font-size: 13px;" data-mce-style="font-size: 10px">学最好的别人,做最好的自己</span></p> <p data-lake-id="b4f10076adf2228ecaccd921f627ed69" style="text-align: right;font-size: 15px;color: rgb(64, 64, 64);line-height: 1.74;letter-spacing: 0.008em;outline-style: none;overflow-wrap: break-word;"><span style="color: rgb(140, 140, 140);font-size: 13px;" data-mce-style="font-size: 10px">来源:https://zhenbianshu.github.io/</span></p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">Redis 作为一个非常成功的数据库,提供了非常丰富的数据类型和命令,使用这些,我们可以轻易而高效地完成很多缓存操作,可是总有一些比较特殊问题或需求需要解决,这时候可能就需要我们自己定制自己的 Redis 数据结构和命令。</p> <h3 style="margin-top: 1.2em;margin-bottom: 1em;color: rgb(53, 179, 120);font-weight: bold;font-size: 20px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">Redis命令问题</h3> <h4 style="margin-top: 30px;margin-bottom: 15px;color: black;font-weight: bold;font-size: 18px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">“线程安全”问题</h4> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">我们都知道 Redis 是单线程的,可是它怎么会有线程安全问题呢?</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">我们正常理解的线程安全问题是指<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">单进程多线程</code>模型内部多个线程<code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">操作进程内共享内存</code>导致的数据资源充突。而 Redis 的线程安全问题的产生,并不是来自于 Redis 服务器内部。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">Redis 作为数据服务器,就相当于多个客户端的共享内存,多个客户端就相当于同一进程下的多个线程,如果多个客户端之间没有良好的数据同步策略,就会产生类似线程安全的问题。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">典型场景是:</p> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 15px;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> Redis 内存储了一个用户的状态: <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">user5277=idle</code>; </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 客户端连接 A 读取了用户状态,获取到用户的空闲状态 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">status = get("user5277")</code>; </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 客户端连接 B 也同样读取了用户状态; </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 客户端连接 A 给用户安排了一个任务,并将 Redis 内用户状态置为忙碌 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">set("user5277", "busy")</code>; </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 客户端连接 B 同样设置用户为忙碌状态。 </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 可是此时用户却被同时分配了两个任务。 </section></li> </ul> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">导致这个问题的原因就是虽然 Redis 是单线程的,能保证命令的序列化,但由于其执行效率很高,多个客户端的命令之间不做好请求同步,同样会造成命令的顺序错乱。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">当然这个问题也很好解决,给用户状态加锁就行了,使同一时间内只能有一个客户端操作用户状态。不过加锁我们就需要考虑锁粒度、死锁等问题了,无疑添加了程序的复杂性,不利于维护。</p> <h4 style="margin-top: 30px;margin-bottom: 15px;color: black;font-weight: bold;font-size: 18px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"> <mpcpc js_editor_cpcad="" class="js_cpc_area cpc_iframe" src="/cgi-bin/readtemplate?t=tmpl/cpc_tmpl#1625928530584" data-category_id_list="1|11|16|17|22|24|26|27|28|29|3|31|32|35|36|37|39|41|42|43|45|46|47|48|49|5|50|51|52|53|54|55|6|7|8" data-id="1625928530584"></mpcpc></h4> <h4 style="margin-top: 30px;margin-bottom: 15px;color: black;font-weight: bold;font-size: 18px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">效率问题</h4> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">Redis 作为一个极其高效的内存数据服务器,其命令执行速度极快,之前看过阿里云 Redis 的一个压测结果,执行效率可以达到 10W写QPS, 60W读QPS,那么,它的效率问题又来自何处呢?</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">答案是网络,做 Web 的都知道,效率优化要从网络做起,服务端又是优化代码,又是优化数据库,不如网络连接的一次优化,而网络优化最有效的就是减少请求数。我们要知道执行一次内存访问的耗时约是 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">100ns</code>,而不同机房之间来回一次约需要 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">500000ns</code>,其中的差距可想而知。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">Redis在单机内效率超高,但工业化部署总不会把服务器和 Redis 放在同一台机器上,如果触碰到效率瓶颈的话,那就是网络。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">典型场景就是我们从 Redis 里读出一条数据,再使用这条数据做键,读取另外一条数据。这样来来回回,便有两次网络往返。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">导致这种问题的原因就是 Redis 的普通命令没有服务端计算的能力,无法在服务器进行复合命令操作,虽然有 Redis 也提供了 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">pipeline</code> 的特性,但它需要多个命令的请求和响应之间没有依赖关系。想简化多个相互依赖的命令就只能将数据拉回客户端,由客户端处理后再请求 Redis。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">综上,我们要更高效更方便的使用 Redis 就需要自己“定制”一些命令了。</p> <h3 style="margin-top: 1.2em;margin-bottom: 1em;color: rgb(53, 179, 120);font-weight: bold;font-size: 20px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">内嵌Lua的执行</h3> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">万幸 Redis 内嵌了 Lua 执行环境,支持 Lua 脚本的执行,通过执行 Lua 脚本,我们可以把多个命令复合为一个 Lua 脚本,通过 Lua 脚本来实现上文中提到的 Redis 命令的次序性和 Redis 服务端计算。</p> <h4 style="margin-top: 30px;margin-bottom: 15px;color: black;font-weight: bold;font-size: 18px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">Lua</h4> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">Lua 是一个简洁、轻量、可扩展的脚本语言,它的特性有:</p> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 15px;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 轻量:源码包只有核心库,编译后体积很小。 </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 高效:由 ANSI C 写的,启动快、运行快。 </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 内嵌:可内嵌到各种编程语言或系统中运行,提升静态语言的灵活性。如 OpenResty 就是将 Lua 嵌入到 nginx 中执行。 </section></li> </ul> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">而且完全不需要担心语法问题,Lua 的语法很简单,分分钟使用不成问题。</p> <h4 style="margin-top: 30px;margin-bottom: 15px;color: black;font-weight: bold;font-size: 18px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"> <mpcpc js_editor_cpcad="" class="js_cpc_area cpc_iframe" src="/cgi-bin/readtemplate?t=tmpl/cpc_tmpl#1625928538014" data-category_id_list="1|11|16|17|22|24|26|27|28|29|3|31|32|35|36|37|39|41|42|43|45|46|47|48|49|5|50|51|52|53|54|55|6|7|8" data-id="1625928538014"></mpcpc></h4> <h4 style="margin-top: 30px;margin-bottom: 15px;color: black;font-weight: bold;font-size: 18px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">执行步骤</h4> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">Redis 在 2.6 版本后,启动时会创建 Lua 环境、载入 Lua 库、定义 Redis 全局表格、存储 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">redis.pcall</code> 等 Redis 命令,以准备 Lua 脚本的执行。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">一个典型的 Lua 脚本执行步骤如下:</p> <ol style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 15px;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 检查脚本是否执行过,没执行过使用脚本的 sha1 校验和生成一个 Lua 函数; </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 为函数绑定超时、错误处理勾子; </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 创建一个伪客户端,通过这个伪客户端执行 Lua 中的 Redis 命令; </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 处理伪客户端的返回值,最终返回给客户端; </section></li> </ol> <p style="text-align: center;"><img class="rich_pages" data-backh="652" data-backw="579" data-galleryid="" data-ratio="1.1268518518518518" data-s="300,640" src="/upload/b62bae1c0f861cb9df7574a6ada6cca0.png" data-type="png" data-w="1080" style="width: 100%;height: auto;"></p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">虽然 Lua 脚本使用的是伪客户端,但 Redis 处理它会跟普通客户端一样,也会将执行的 Redis 命令进行 rdb aof 主从复制等操作。</p> <h4 style="margin-top: 30px;margin-bottom: 15px;color: black;font-weight: bold;font-size: 18px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">使用</h4> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">Lua 脚本的使用可以通过 Redis 的 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">EVAL</code> 和 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">EVALSHA</code> 命令。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">EVAL</code> 适用于单次执行 Lua 脚本,执行脚本前会由脚本内容生成 sha1 校验和,在函数表内查询函数是否已定义,如未定义执行成功后 Redis 会在全局表里缓存这个脚本的校验和为函数名,后续再次执行此命令就不会再创建新的函数了。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">而要使用 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">EVALSHA</code> 命令,就得先使用 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">SCRIPT LOAD</code> 命令先将函数加载到 Redis,Redis 会返回此函数的 sha1 校验和, 后续就可以直接使用这个校验和来执行命令了。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">以下是使用上述命令的例子:</p> <pre style="font-size: 15px;font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace;margin-top: 10px;margin-bottom: 10px;overflow: auto;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;color: rgb(89, 89, 89);letter-spacing: 0.75px;text-align: left;background-color: rgb(255, 255, 255);"><code style="font-size: 12px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;display: -webkit-box;overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);background: rgb(40, 44, 52);border-radius: 5px;">127.0.0.1:6379> EVAL <span style="color: rgb(152, 195, 121);line-height: 26px;">"return 'hello'"</span> 0 0<br><span style="color: rgb(152, 195, 121);line-height: 26px;">"hello"</span><br><br>127.0.0.1:6379> SCRIPT LOAD <span style="color: rgb(152, 195, 121);line-height: 26px;">"return redis.pcall('GET', ARGV[1])"</span><br><span style="color: rgb(152, 195, 121);line-height: 26px;">"20b602dcc1bb4ba8fca6b74ab364c05c58161a0a"</span><br><br>127.0.0.1:6379> EVALSHA 20b602dcc1bb4ba8fca6b74ab364c05c58161a0a 0 <span style="color: rgb(230, 192, 123);line-height: 26px;">test</span><br><span style="color: rgb(152, 195, 121);line-height: 26px;">"zbs"</span><br></code></pre> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">EVAL 命令的原型是 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">EVAL script numkeys key [key ...] arg [arg ...]</code>,在 Lua 函数内部可以使用 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">KEYS[N]</code> 和 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ARGV[N]</code> 引用键和参数,需要注意 KEYS 和 ARGV 的参数序号都是从 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">1</code> 开始的。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">还需要注意在 Lua 脚本中,Redis 返回为空时,结果是 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">false</code>,而 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">不是 nil</code>;</p> <h3 style="margin-top: 1.2em;margin-bottom: 1em;color: rgb(53, 179, 120);font-weight: bold;font-size: 20px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">Lua 脚本实例</h3> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">下面写几个 Lua 脚本的实例,用来介绍语法的,仅供参考。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">Redis 里 hashSet A 的 字段 B 的值是 C,取出 Redis 里键为 C 的值。</p> <pre style="font-size: 15px;font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace;margin-top: 10px;margin-bottom: 10px;overflow: auto;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;color: rgb(89, 89, 89);letter-spacing: 0.75px;text-align: left;background-color: rgb(255, 255, 255);"><code style="font-size: 12px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;display: -webkit-box;overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);background: rgb(40, 44, 52);border-radius: 5px;">// 使用: EVAL script 2 A B<br><br><span style="color: rgb(230, 192, 123);line-height: 26px;">local</span> tmpKey = redis.call(<span style="color: rgb(152, 195, 121);line-height: 26px;">'HGET'</span>, KEYS[1], KEYS[2]);<br><span style="color: rgb(230, 192, 123);line-height: 26px;">return</span> redis.call(<span style="color: rgb(152, 195, 121);line-height: 26px;">'GET'</span>, tmpKey);<br></code></pre> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">一次 lpop 出多个值,直到值为 n,或 list 为空(pipeline 也可轻易实现);</p> <pre style="font-size: 15px;font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace;margin-top: 10px;margin-bottom: 10px;overflow: auto;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;color: rgb(89, 89, 89);letter-spacing: 0.75px;text-align: left;background-color: rgb(255, 255, 255);"><code style="font-size: 12px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;display: -webkit-box;overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);background: rgb(40, 44, 52);border-radius: 5px;">// 使用: EVAL script 2 list count<br><br><span style="color: rgb(230, 192, 123);line-height: 26px;">local</span> list = {};<br><span style="color: rgb(230, 192, 123);line-height: 26px;">local</span> item = <span style="color: rgb(86, 182, 194);line-height: 26px;">false</span>;<br><span style="color: rgb(230, 192, 123);line-height: 26px;">local</span> num = tonumber(KEYS[2]);<br><span style="color: rgb(198, 120, 221);line-height: 26px;">while</span> (num > 0)<br><span style="color: rgb(198, 120, 221);line-height: 26px;">do</span><br> item = redis.call(<span style="color: rgb(152, 195, 121);line-height: 26px;">'LPOP'</span>, KEYS[1]);<br> <span style="color: rgb(198, 120, 221);line-height: 26px;">if</span> item == <span style="color: rgb(86, 182, 194);line-height: 26px;">false</span> <span style="color: rgb(198, 120, 221);line-height: 26px;">then</span><br> <span style="color: rgb(230, 192, 123);line-height: 26px;">break</span>;<br> end;<br> table.insert(list, item);<br> num = num - 1;<br>end;<br><span style="color: rgb(230, 192, 123);line-height: 26px;">return</span> list;<br></code></pre> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">获取 zset 内 score 最多的 n 个元素 对应 hashset 中的详细信息;</p> <pre style="font-size: 15px;font-family: SFMono-Regular, Consolas, "Liberation Mono", Menlo, Courier, monospace;margin-top: 10px;margin-bottom: 10px;overflow: auto;border-radius: 5px;box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px;color: rgb(89, 89, 89);letter-spacing: 0.75px;text-align: left;background-color: rgb(255, 255, 255);"><code style="font-size: 12px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;display: -webkit-box;overflow-x: auto;padding: 15px 16px 16px;color: rgb(171, 178, 191);background: rgb(40, 44, 52);border-radius: 5px;"><span style="color: rgb(230, 192, 123);line-height: 26px;">local</span> elements = redis.call(<span style="color: rgb(152, 195, 121);line-height: 26px;">'ZRANK'</span>, KEYS[1], 0, KEY[2]);<br><span style="color: rgb(230, 192, 123);line-height: 26px;">local</span> detail = {};<br><br><span style="color: rgb(198, 120, 221);line-height: 26px;">for</span> index,ele <span style="color: rgb(198, 120, 221);line-height: 26px;">in</span> elements <span style="color: rgb(198, 120, 221);line-height: 26px;">do</span><br> <span style="color: rgb(230, 192, 123);line-height: 26px;">local</span> info = redis.call(<span style="color: rgb(152, 195, 121);line-height: 26px;">'HGETALL'</span>, ele);<br> table.insert(detail, info);<br>end;<br><br><span style="color: rgb(230, 192, 123);line-height: 26px;">return</span> detail;<br></code></pre> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">基本使用语法就是如此,更多应用就看各个具体场景了。</p> <h3 style="margin-top: 1.2em;margin-bottom: 1em;color: rgb(53, 179, 120);font-weight: bold;font-size: 20px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">一些思考</h3> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">实现之外,还要一些东西要思考:</p> <h4 style="margin-top: 30px;margin-bottom: 15px;color: black;font-weight: bold;font-size: 18px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">使用场景</h4> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">首先来总结一下 Redis 中 Lua 的使用场景:</p> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 15px;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 可以使用 Lua 脚本实现原子性操作,避免不同客户端访问 Redis 服务器造成的数据冲突。 </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 在前后多次请求的结果有依赖时,可以使用 Lua 脚本把多个请求整合为一个请求。 </section></li> </ul> <h4 style="margin-top: 30px;margin-bottom: 15px;color: black;font-weight: bold;font-size: 18px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">注意点</h4> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">使用 Lua 脚本,我们还需要注意:</p> <ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;font-size: 15px;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 要保证安全性,在 Lua 脚本中不要使用全局变量,以免污染 Lua 环境,虽然使用全局变量全报错,Lua 脚本停止执行,但还是在定义变量时添加 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">local</code> 关键字。 </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 要注意 Lua 脚本的时间复杂度,Redis 的单线程同样会阻塞在 Lua 脚本的执行中。 </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 使用 Lua 脚本实现原子操作时,要注意如果 Lua 脚本报错,之前的命令同样无法回滚。 </section></li> <li> <section style="margin-top: 10px;margin-bottom: 10px;line-height: 26px;color: rgb(1, 1, 1);"> 一次发出多个 Redis 请求,但请求前后无依赖时,使用 <code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">pipeline</code>,比 Lua 脚本方便。 </section></li> </ul> <h3 style="margin-top: 1.2em;margin-bottom: 1em;color: rgb(53, 179, 120);font-weight: bold;font-size: 20px;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">小结</h3> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">最近工作有了较大的变动,从业务到技术栈都跟原来完全不同了,所有代码和业务都脱离了自己掌控的感觉真的很不爽,工作中全是“开局一个搜索引擎,语法全靠查”,每天还要熬到很晚熟悉新的东西,有点小累,果然换工作就是找罪受啊。</p> <p style="margin: 1em 4px;font-size: 16px;padding-top: 8px;padding-bottom: 8px;line-height: 26px;color: black;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, "PingFang SC", Cambria, Cochin, Georgia, Times, "Times New Roman", serif;letter-spacing: 0.75px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">不过走出舒适区后的充实感也在提醒自己正在不停进步,倒也挺有成就感的。</p> <p data-lake-id="b4f10076adf2228ecaccd921f627ed69" style="text-align: center;font-size: 15px;color: rgb(64, 64, 64);line-height: 1.74;letter-spacing: 0.008em;outline-style: none;overflow-wrap: break-word;"><br></p> <section data-mpa-template="t" mpa-from-tpl="t"> <section mpa-from-tpl="t" style="margin-top: 10px;margin-bottom: 10px;"> <p style="margin-right: auto;margin-left: auto;width: 231.1875px;"><img data-ratio="0.16666666666666666" src="/upload/89aa6c9fe16e0dfcf56dad1a9f9078ff.png" data-type="gif" data-w="300" style="width: auto;"></p> </section> </section> <p data-lake-id="098b923ca3e65e99528d2c30d6cf4c26" style="text-align: center;font-size: 15px;color: rgb(64, 64, 64);line-height: 1.74;letter-spacing: 0.008em;outline-style: none;overflow-wrap: break-word;"><br></p> <section data-recommend-type="list-title" data-recommend-tid="6" data-mpa-template="t" style="width: 100%;display: flex;justify-content: center;align-items: center;" data-mid="" data-from="yb-recommend"> <section style="width: 100%;padding: 14px;background: rgb(255, 255, 255);border-radius: 3px;border-width: 1px;border-style: solid;border-color: rgb(232, 232, 235);" data-mid=""> <section style="width: 100%;display: flex;justify-content: center;align-items: center;align-items: flex-end;" data-mid=""> <section data-mid="" style="height: 28px;padding: 4px 22px;font-size: 14px;font-weight: 500;color: rgb(19, 52, 86);line-height: 20px;background-image: url("https://mmbiz.qpic.cn/mmbiz_png/sUbvrqLicbpzB81mjeBxPuxnYdalGxNnJo30L2Hq3WwGficcq8w5YJkLeXnsNHocN53k55TfN5mBpCdicGRyfDg1g/640?wx_fmt=png");background-repeat: no-repeat;background-size: 100% 100%;margin-bottom: -14px;z-index: 10;"> <p data-mid="">往期推荐</p> </section> </section> <section style="width: 100%;border-width: 1px;border-style: solid;border-color: rgb(198, 226, 255);padding: 17px 16px 9px;" data-mid=""> <section data-mpa-template="t" data-recommend-article-type="list-title" data-recomment-template-id="6" data-recommend-article-id="2247495617_1" data-recommend-article-time="1625893260" data-recommend-article-cover="http://mmbiz.qpic.cn/mmbiz_jpg/HmHDU48icAtZXOWEtuzs2ynI48cxZy6WOCSjFsPlDggMicCnA6BzG90tE6eXiab4rSCOR0HEVOQI1MbsNmJOXTZ6Q/0?wx_fmt=jpeg" data-recommend-article-title="为什么 StringBuilder 不是线程安全的?" data-recommend-article-content-url="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247495617&idx=1&sn=7f7240d1c35fd1c4c90eb92b43ddb882&chksm=97b471d9a0c3f8cf95cdb3ead63d6db382b67ef3f5f21c2cbe23df1570b0229d2f153b898cef#rd"> <a href="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247495617&idx=1&sn=7f7240d1c35fd1c4c90eb92b43ddb882&chksm=97b471d9a0c3f8cf95cdb3ead63d6db382b67ef3f5f21c2cbe23df1570b0229d2f153b898cef&scene=21#wechat_redirect" data-linktype="2"> <section data-recommend-title="t" data-recommend-content="t" style="width: 100%;display: flex;justify-content: center;align-items: center;flex-wrap: nowrap;border-bottom: 1px dashed #c6e2ff;padding: 6px;font-size: 13px;font-weight: 400;color: #2c5f95;line-height: 18px;" data-mid=""> <p style="white-space: nowrap;overflow: hidden;text-overflow: ellipsis;max-width: 100%;" data-mid="">为什么 StringBuilder 不是线程安全的?</p> </section></a> </section> <section data-mpa-template="t" data-recommend-article-type="list-title" data-recomment-template-id="6" data-recommend-article-id="2247495617_2" data-recommend-article-time="1625893260" data-recommend-article-cover="http://mmbiz.qpic.cn/mmbiz_jpg/R3InYSAIZkFlFPC5m4TXiaxU7jlSQiaAmmZBokwbRr4KiaHyvW7sZ54vNucn1ZKqliapc45MbHWPTKPoH4UIWKOdCw/0?wx_fmt=jpeg" data-recommend-article-title="程序员写代码崩溃,路过的暖心美团骑手:我帮你看看!" data-recommend-article-content-url="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247495617&idx=2&sn=7064d4821fa16c70d668db56be99e543&chksm=97b471d9a0c3f8cffc9e72348ae25742caa627902d836d01f2d24671dbc6779294a7e8b4a8b2#rd"> <a href="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247495617&idx=2&sn=7064d4821fa16c70d668db56be99e543&chksm=97b471d9a0c3f8cffc9e72348ae25742caa627902d836d01f2d24671dbc6779294a7e8b4a8b2&scene=21#wechat_redirect" data-linktype="2"> <section data-recommend-title="t" data-recommend-content="t" style="width: 100%;display: flex;justify-content: center;align-items: center;flex-wrap: nowrap;border-bottom: 1px dashed #c6e2ff;padding: 6px;font-size: 13px;font-weight: 400;color: #2c5f95;line-height: 18px;" data-mid=""> <p style="white-space: nowrap;overflow: hidden;text-overflow: ellipsis;max-width: 100%;" data-mid="">程序员写代码崩溃,路过的暖心美团骑手:我帮你看看!</p> </section></a> </section> <section data-mpa-template="t" data-recommend-article-type="list-title" data-recomment-template-id="6" data-recommend-article-id="2247495617_3" data-recommend-article-time="1625893260" data-recommend-article-cover="http://mmbiz.qpic.cn/mmbiz_jpg/Rhiaqbb2uvaLfDNBc0BGGzodNWibtFj119BiaGd6NY69cd5bmH4wS1KIF6xxhScHBk3H6ExLP0HdeMzqZxwibrDcvg/0?wx_fmt=jpeg" data-recommend-article-title="搜番神器!一张图就可以告诉你所有!" data-recommend-article-content-url="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247495617&idx=3&sn=56e2acdb9c434066168ebc2b7797eb55&chksm=97b471d9a0c3f8cfdc9dff4635baf40548638c9446f2997c90824cef682cb54408d4d68c5838#rd"> <a href="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247495617&idx=3&sn=56e2acdb9c434066168ebc2b7797eb55&chksm=97b471d9a0c3f8cfdc9dff4635baf40548638c9446f2997c90824cef682cb54408d4d68c5838&scene=21#wechat_redirect" data-linktype="2"> <section data-recommend-title="t" data-recommend-content="t" style="width: 100%;display: flex;justify-content: center;align-items: center;flex-wrap: nowrap;border-bottom: 1px dashed #c6e2ff;padding: 6px;font-size: 13px;font-weight: 400;color: #2c5f95;line-height: 18px;" data-mid=""> <p style="white-space: nowrap;overflow: hidden;text-overflow: ellipsis;max-width: 100%;" data-mid="">搜番神器!一张图就可以告诉你所有!</p> </section></a> </section> <section data-mpa-template="t" data-recommend-article-type="list-title" data-recomment-template-id="6" data-recommend-article-id="2247495601_1" data-recommend-article-time="1625806860" data-recommend-article-cover="http://mmbiz.qpic.cn/mmbiz_jpg/HmHDU48icAtZZb5PCImFKatZjBmLyzUucRT8oh60Lq5qXQ5h9J1GWOUdSjPpXghw35DpLz0hez333TGTrvqtWKA/0?wx_fmt=jpeg" data-recommend-article-title="Java 中 long 是不是原子操作?" data-recommend-article-content-url="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247495601&idx=1&sn=133b2399e236afd3725a10dd152322f4&chksm=97b471a9a0c3f8bf0ecd6f7823e0ad60e68e9a859861ffdf281572d13ca5390000b3feb6ede5#rd"> <a href="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247495601&idx=1&sn=133b2399e236afd3725a10dd152322f4&chksm=97b471a9a0c3f8bf0ecd6f7823e0ad60e68e9a859861ffdf281572d13ca5390000b3feb6ede5&scene=21#wechat_redirect" data-linktype="2"> <section data-recommend-title="t" data-recommend-content="t" style="width: 100%;display: flex;justify-content: center;align-items: center;flex-wrap: nowrap;border-bottom: 1px dashed #c6e2ff;padding: 6px;font-size: 13px;font-weight: 400;color: #2c5f95;line-height: 18px;" data-mid=""> <p style="white-space: nowrap;overflow: hidden;text-overflow: ellipsis;max-width: 100%;" data-mid="">Java 中 long 是不是原子操作?</p> </section></a> </section> <section data-mpa-template="t" data-recommend-article-type="list-title" data-recomment-template-id="6" data-recommend-article-id="2247495601_2" data-recommend-article-time="1625806860" data-recommend-article-cover="http://mmbiz.qpic.cn/mmbiz_jpg/R3InYSAIZkH22OQfNUgpLsZAhXUGGwpteqNjSj4icMLyLSFqFrxyrcXsZPZN6gc41dEvOCNdlt6pzAKLhjJcZKg/0?wx_fmt=jpeg" data-recommend-article-title="Spring发布新成员:Spring GraphQL!高调出场的GraphQL能火起来了吗?" data-recommend-article-content-url="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247495601&idx=2&sn=dc6a029448e1aa0d3523485f63b067b2&chksm=97b471a9a0c3f8bf426b50ee5d77c0712c8a2c924daf72ac2767a7c34a26665b8debccefcc78#rd"> <a href="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247495601&idx=2&sn=dc6a029448e1aa0d3523485f63b067b2&chksm=97b471a9a0c3f8bf426b50ee5d77c0712c8a2c924daf72ac2767a7c34a26665b8debccefcc78&scene=21#wechat_redirect" data-linktype="2"> <section data-recommend-title="t" data-recommend-content="t" style="width: 100%;display: flex;justify-content: center;align-items: center;flex-wrap: nowrap;border-bottom: 1px dashed #c6e2ff;padding: 6px;font-size: 13px;font-weight: 400;color: #2c5f95;line-height: 18px;" data-mid=""> <p style="white-space: nowrap;overflow: hidden;text-overflow: ellipsis;max-width: 100%;" data-mid="">Spring发布新成员:Spring GraphQL!高调出场的GraphQL能火起来了吗?</p> </section></a> </section> <section data-mpa-template="t" data-recommend-article-type="list-title" data-recomment-template-id="6" data-recommend-article-id="2247495601_3" data-recommend-article-time="1625806860" data-recommend-article-cover="http://mmbiz.qpic.cn/mmbiz_jpg/Rhiaqbb2uvaKtRJnCKR3be0jqerWKIntiaugB3PfEicDrVoRbOpjlZw1K99zVicbxeicN7fYkJBtrP9D3OzIpbobkpA/0?wx_fmt=jpeg" data-recommend-article-title="大福利!苹果 Apple Music 会员!免费领取!" data-recommend-article-content-url="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247495601&idx=3&sn=b9a6073e0458406bd43e0034f15c891c&chksm=97b471a9a0c3f8bffc9e9a92efa1c52b2813eb429cc63c941f29dec4552e7de0476f13ed7c30#rd"> <a href="http://mp.weixin.qq.com/s?__biz=MzIxMzQzNzMwMw==&mid=2247495601&idx=3&sn=b9a6073e0458406bd43e0034f15c891c&chksm=97b471a9a0c3f8bffc9e9a92efa1c52b2813eb429cc63c941f29dec4552e7de0476f13ed7c30&scene=21#wechat_redirect" data-linktype="2"> <section data-recommend-title="t" data-recommend-content="t" style="width: 100%;display: flex;justify-content: center;align-items: center;flex-wrap: nowrap;border-bottom: 1px dashed #c6e2ff;padding: 6px;font-size: 13px;font-weight: 400;color: #2c5f95;line-height: 18px;border-bottom:none !important;" data-mid=""> <p style="white-space: nowrap;overflow: hidden;text-overflow: ellipsis;max-width: 100%;" data-mid="">大福利!苹果 Apple Music 会员!免费领取!</p> </section></a> </section> </section> </section> </section> <p data-lake-id="098b923ca3e65e99528d2c30d6cf4c26" style="text-align: center;font-size: 15px;color: rgb(64, 64, 64);line-height: 1.74;letter-spacing: 0.008em;outline-style: none;overflow-wrap: break-word;"><br></p> <p data-lake-id="098b923ca3e65e99528d2c30d6cf4c26" style="text-align: center;font-size: 15px;color: rgb(64, 64, 64);line-height: 1.74;letter-spacing: 0.008em;outline-style: none;overflow-wrap: break-word;"><span style="color: rgb(140, 140, 140);"><br></span></p> <section data-mpa-template="t" mpa-from-tpl="t" style="white-space: normal;"> <p style="text-align: center;"><strong><span style="color: rgb(140, 140, 140);letter-spacing: 0.008em;font-size: 14px;">一起进大厂,每日学干货</span></strong></p> </section> <section style="margin-top: 5px;white-space: normal;text-align: center;font-size: 15px;color: rgb(64, 64, 64);line-height: 1.74;letter-spacing: 0.008em;outline-style: none;overflow-wrap: break-word;"> <span style="color: rgb(0, 0, 0);"><strong><span style="font-size: 14px;">关注我回复【</span></strong></span> <span style="color: rgb(255, 76, 65);"><strong><span style="font-size: 14px;">加群</span></strong></span> <span style="color: rgb(0, 0, 0);"><strong><span style="color: rgb(0, 0, 0);font-size: 14px;">】,加入Java技术交流群</span></strong></span> </section> <section data-mpa-template="t" mpa-from-tpl="t"> <p style="text-align: center;"><img data-ratio="0.5982532751091703" src="/upload/4e21037b66f60f7d73d060863de4b4b5.png" data-type="gif" data-w="458" data-width="100%" style="color: rgb(62, 62, 62);font-size: 16px;vertical-align: middle;width: 62.0938px;"></p> <section class="mp_profile_iframe_wrp"> <mpprofile class="js_uneditable custom_select_card mp_profile_iframe" data-pluginname="mpprofile" data-id="MzIxMzQzNzMwMw==" data-headimg="http://mmbiz.qpic.cn/mmbiz_png/HmHDU48icAtYvlypOY9VaGVXQ639L63Iq6zHiclgibG0CAhgrJ2JLRibKbeCgVIx7WXcicbMW6AJL1Hos9AoJTqtVfA/0?wx_fmt=png" data-nickname="后端面试那些事" data-alias="" data-signature="专注分享后端干货!面向大厂,一起进步!" data-from="0"></mpprofile> </section> <p style="text-align: center;"><br></p> </section> <p style="text-align: center;"><br></p> <section data-mpa-template="t" mpa-from-tpl="t"> <section data-role="paragraph" mpa-from-tpl="t" style="white-space: normal;border-width: 0px;border-style: none;border-color: initial;"> <section style="box-sizing: border-box;font-size: 16px;"> <section powered-by="xiumi.us" style="margin-top: 0.5em;box-sizing: border-box;"> <section style="padding: 0.5em;border-width: 1px;border-style: solid;border-color: rgb(249, 110, 87);box-shadow: rgb(226, 226, 226) 0px 16px 1px -13px;box-sizing: border-box;"> <section powered-by="xiumi.us" style="text-align: left;box-sizing: border-box;"> <section style="text-align: justify;color: rgb(64, 84, 115);box-sizing: border-box;"> <p style="box-sizing: border-box;"><img data-ratio="0.33611111111111114" src="/upload/a53e4e397528931045198e4f89d7ba0.png" data-type="png" data-w="1080"></p> <p style="box-sizing: border-box;">点击“阅读原文”,领取 2021 年<strong>最新免费技术资料大全</strong></p> </section> </section> </section> </section> <section powered-by="xiumi.us" style="font-size: 32px;color: rgb(249, 110, 87);box-sizing: border-box;text-align: left;"> ↓↓↓ </section> </section> </section> </section>