电子物流中的EDI 应用

背景

EDI 全称是Electronic data interchange, 即电子数据交换。在传统企业里,很多流程上的操作或者通信一般是由纸质媒介完成的,比如说采购订单、发票、订单同步之类的。但由于纸质媒介一切传播全靠人手,就会带来很多不可避免的缺点,比如说操作及同步信息慢、人力及物力资源消耗大等等。EDI 的出现就是为了解决纸质交互带来的缺点。它可以极大地提高业务效率、更快地同步各种状态及信息、减少纠错流程、接近实时地访问信息,最重要的是它可以省钱。有研究表明平均来说使用EDI 的成本相比纸质交换来说只需要纸质的三分之一开销。而且可以节省更多的时间(有研究是节省61%),缩短订单的周期,将流程自动化等等。

估计能看到这文章的人多为互联网从业者,可能很难想象在这个“信息化”的时代里还有那么多没有被“信息化”的地方。但神奇的是,在完全没有信息化的情况下,人们也靠人力实现了一切甚至直到这一刻还有那么多的传统行业在过着完全不一样的日常流程。

当你看到这第一段的时候,你可能会想:这不就是 “接口(API)” 做的事吗?EDI 成立之初其实做的就是类似我们现在用API 做的事。只是年代不同。EDI 最早在1960 年代就出现了。而我们更为熟悉一些的像XML 是1996 年才出现的,而JSON 是2013 年才有。现代互联网的历史还真就这么短。

话说回来,EDI 与API 的不同之处,在我的理解里,相比我们现在的API, EDI 的概念其实更像是通用的API. 我们API 的“标准” 更多的是大家用相同的技术,但各家有各家的定义。而EDI 是大家用相同的技术,并且大家用相同的结构。因为EDI 文件中并不包含解释。所以每一份EDI 里面用的字段,表达的内容都是一样的。你也可以理解为是一个没有包含字段名只有各个字段值的API.

一开始EDI 起源于军事物流。但随着时间的发展,各行各业慢慢都用上了这个东西,但又因为不同行业的需求不同,所以现今EDI 有多个流通的标准。比如说船舶舱单的用的EDI 856,发票用的EDI 810,美国最多使用的ANSI X12,全球其他地方使用比较多的UN/EDIFACT 等等。不同的行业、不同的情景都会有一个对应的标准可以用。

原理

那么说了那么多,如何使用EDI 呢?很简单,分三步:

  1. 准备需要传输的数据
  2. 把数据转换成EDI 格式
  3. 关上冰箱门(bushi

EDI 根据使用场景的不同,一般EDI 是一个文本文件。里面按照某种标准将对应的信息转化成EDI 格式保存。至于传输它,你可以用FTP/SFTP/FTPS, AS1/AS2/AS4, OFTP/OFTP2 甚至是用电子邮件发送这个EDI 文件都行。只要是能从一个地方传输一个文本文件到另外一个地方的都行。甚至有人是做成了API 的也有(格式用的EDI)。总之大部分情况下EDI 都是直接的端对端传输。但也有少量的VAN(增值网络)。

另外一端取到文件后,如果是自动化的可以用程序进行下一步流程。如果是要给人看的则可以用EDI 翻译噐或者用解析器去打开它们。

比如说我之前做的一个项目用的EDI 315,315主要用在船运中跟踪物流信息状态及运输/集装箱的一些事件详情等。它的内容如下:

  1. ISA*00* *00* *ZZ*OECGROUP *ZZ*AAA *201120*1304*U*00401*000259937*0*P*>
  2. GS*QO*OECGROUP*AAA*20201120*1304*259937*X*004010
  3. ST*315*0001
  4. B4***I*20201030*0000*CNYTN*DFSU*773057*L*4500*CNYTN*UN*6
  5. N9*BM*OERT210702J01222
  6. N9*BN*MEDUZ7825111
  7. N9*EQ*DFSU7730576
  8. N9*SN*FJ10356721
  9. N9*SCA*OERT
  10. Q2*NONE********045W***L*MAERSK ALGOL
  11. R4*5*UN*CNYTN*YANTIAN PT*CN
  12. DTM*140*20201030*0000*LT
  13. R4*R*UN*CNYTN*YANTIAN PT*CN
  14. DTM*140*20201030*0000*LT
  15. R4*L*UN*CNYTN*YANTIAN PT*CN
  16. DTM*140*20201110*1040*LT
  17. R4*D*UN*USSAV*SAVANNAH*US
  18. DTM*139*20201213*0000*LT
  19. R4*E*UN*USSAV*SAVANNAH*US
  20. DTM*139*20201215*1500*LT
  21. SE*19*0001
  22. GE*1*259937
  23. IEA*1*000259937

这就是一个完整的EDI 文件。看着可能觉得像乱码,但其实只是它没有字段名解释而已。

所有的EDI 文件都是由三个块组成的:

  • Element:元素,一行里面的内容就是一个个不同的元素。
  • Segments:段,段可以理解为一些同类型的元素,类似于组的概念。对元素进行分类。像上面的例子中一行就是一个组。
  • Transaction Sets:事务集,也可以叫EDI 信息或者EDI 事务。当信息以段的形式收集好后它们就会组成集。

之所以要规定这些标准,就是要用一个经由双方认可的标准去传输信息可以用更少量的内容去表达更多的事。

下面我只解释上面例子中的一段,大概了解一下意思知道原理就行。更多详细的各段的代表的意思可以看这个文档了解:315 Status Details (Ocean)

上面这个文档只是我在网上找的,用的是同个标准但细节可能会和我这个例子不完全一样,因为同个标准下,不同的公司可能用到的字段不完全相同,比如有些字段它们公司不需要可能就给省略了。但意思是一个意思。

  1. R4*5*UN*CNYTN*YANTIAN PT*CN
  • "R4" - 是这节的头,标识了这是一行描述 "Port or Terminal" 的相关内容。

  • "*" - 星号在这份文件中就是个分隔符,没有实际意思。所以这段本质上是:[R4, 5, UN, CNYTN, YANTIAN PT, CN] 这么几节信息。

  • "5" - 这也是一个约定的值,当它是 "5" 时,代表这在描述 "Active Location".

  • "UN" - 代表下个值是采用的UNLOCODE, 即港口码头代码表,描述全球各个港口用的一个表。

  • "CNYTN" - 这是UNLOCODE 的当前这个港口的代号。可以在这里查询。即深圳的盐田港

  • "YANTIAN PT" - 这个是港口的名字。PT是Port, 即港口。

  • "CN" - 这是两位的ISO 国家代码,指中国

在有说明的情况下其实挺好理解的。但是没有说明就会是天书。。

实施

关于这节其实我一直在想需不需要写。因为在原理清楚了之后其实就没有很大必要写了,是个开发都能整个解析出来的。而且Github 上也挺多现成的类库的,不过因为标准太多了很大机率你还是得自已实现一个自已需要的。

关于传输的,如果是像电子邮件的可以直接用邮件服务拦截附件并发送内容到解析程序。如果是ftp 之类的文件服务可能得设立一个文件变动的监控程序或者是弄个定时器定时扫描。

关于解析内容的,下面我附个最简单的解析代码解析上面这个例子吧。解析这种东西看使用场景需要可以变得很复杂也可以很简单。一切跟着需求走。

  1. const UNUSED = undefined;
  2. const InterchangeControlHeader = [
  3. 'AuthorizationInformationQualifier',
  4. 'AuthorizationInformation',
  5. 'SecurityInformationQualifier',
  6. 'SecurityInformation',
  7. 'InterchangeSenderIDQualifier',
  8. 'InterchangeSenderID',
  9. 'InterchangeReceiverIDQualifier',
  10. 'InterchangeReceiverID',
  11. 'InterchangeDate',
  12. 'InterchangeTime',
  13. 'InterchangeControlStandardsIdentifier',
  14. 'InterchangeControlVersionNumber',
  15. 'InterchangeControlNumber',
  16. 'AcknowledgmentRequested',
  17. 'UsageIndicator',
  18. 'ComponentElementSeparator',
  19. ];
  20. const FunctionalGroupHeader = [
  21. 'FunctionalIdentifierCode',
  22. 'ApplicationSendersCode',
  23. 'ApplicationReceiversCode',
  24. 'Date',
  25. 'Time',
  26. 'GroupControlNumber',
  27. 'ResponsibleAgencyCode',
  28. 'VersionReleaseIndustryIdentifierCode',
  29. ];
  30. const TransactionSetHeader = [
  31. 'TransactionSetIdentifierCode',
  32. 'TransactionSetControlNumber',
  33. ];
  34. const BeginningSegmentForInquiryOrReply = [
  35. UNUSED,
  36. UNUSED,
  37. 'ShipmentStatusCode',
  38. 'Date',
  39. 'StatusTime',
  40. 'StatusLocation',
  41. 'EquipmentInitial',
  42. 'EquipmentNumber',
  43. 'EquipmentStatusCode',
  44. 'EquipmentType',
  45. 'LocationIdentifier',
  46. 'LocationQualifier',
  47. 'EquipmentNumberCheckDigit',
  48. ];
  49. const ReferenceIdentification = [
  50. 'ReferenceIdentificationQualifier',
  51. 'ReferenceIdentification',
  52. ];
  53. const StatusDetailsOcean = [
  54. 'VesselCode',
  55. UNUSED,
  56. UNUSED,
  57. UNUSED,
  58. UNUSED,
  59. UNUSED,
  60. UNUSED,
  61. 'CountryCode',
  62. 'VoyageNumber',
  63. UNUSED,
  64. UNUSED,
  65. 'VesselCodeQualifier',
  66. 'VesselName',
  67. ];
  68. const PortOrTerminal = [
  69. 'PortOrTerminalFunctionCode',
  70. 'LocationQualifier',
  71. 'LocationIdentifier',
  72. 'PortName',
  73. 'CountryCode',
  74. ];
  75. const DateTimeReference = ['DateTimeQualifier', 'Date', 'Time', 'TimeCode'];
  76. const TransactionSetTrailer = [
  77. 'NumberOfIncludedSegments',
  78. 'TransactionSetControlNumber',
  79. ];
  80. const FunctionalGroupTrailer = [
  81. 'NumberOfTransactionSetsIncluded',
  82. 'GroupControlNumber',
  83. ];
  84. const InterchangeControlTrailer = [
  85. 'NumberOfIncludedFunctionalGroups',
  86. 'InterchangeControlNumber',
  87. ];
  88. const segments = {
  89. ISA: 'InterchangeControlHeader',
  90. GS: 'FunctionalGroupHeader',
  91. ST: 'TransactionSetHeader',
  92. B4: 'BeginningSegmentForInquiryOrReply',
  93. N9: 'ReferenceIdentification',
  94. Q2: 'StatusDetailsOcean',
  95. R4: 'PortOrTerminal',
  96. DTM: 'DateTimeReference',
  97. SE: 'TransactionSetTrailer',
  98. GE: 'FunctionalGroupTrailer',
  99. IEA: 'InterchangeControlTrailer',
  100. };
  101. const segmentFields = {
  102. [segments.ISA]: InterchangeControlHeader,
  103. [segments.GS]: FunctionalGroupHeader,
  104. [segments.ST]: TransactionSetHeader,
  105. [segments.B4]: BeginningSegmentForInquiryOrReply,
  106. [segments.N9]: ReferenceIdentification,
  107. [segments.Q2]: StatusDetailsOcean,
  108. [segments.R4]: PortOrTerminal,
  109. [segments.DTM]: DateTimeReference,
  110. [segments.SE]: TransactionSetTrailer,
  111. [segments.GE]: FunctionalGroupTrailer,
  112. [segments.IEA]: InterchangeControlTrailer,
  113. };
  114. type Edi315 = {
  115. [key in keyof typeof segments]?:
  116. | Record<Partial<keyof typeof segmentFields>, string>
  117. | Record<Partial<keyof typeof segmentFields>, string>[];
  118. };
  119. const parse = function(
  120. data: string | string[],
  121. segmentSeparator = '\n',
  122. valueSeparator = '*',
  123. ): Edi315 {
  124. const result = {};
  125. const availableSegments = Object.keys(segments);
  126. const _data = Array.isArray(data) ? data : data.split(segmentSeparator);
  127. _data.map((line, index) => {
  128. if (!line.replace(valueSeparator, '').trim()) return;
  129. const lineData = line.split(valueSeparator);
  130. const segmentName = lineData[0];
  131. if (!availableSegments.includes(segmentName)) {
  132. console.error('Unknown segment:', line);
  133. return;
  134. }
  135. lineData.slice(1).map((item, idx, array) => {
  136. const fieldName = segmentFields[segments[segmentName]][idx];
  137. if (result[segmentName] === undefined) {
  138. if (['N9', 'R4', 'DTM'].includes(segmentName)) {
  139. result[segmentName] = [];
  140. } else {
  141. result[segmentName] = {};
  142. }
  143. }
  144. if (segmentFields[segments[segmentName]].length != array.length) {
  145. if (idx < 1) {
  146. console.error('Mismatch segment length:', line);
  147. }
  148. return;
  149. }
  150. if (fieldName !== UNUSED) {
  151. if (Array.isArray(result[segmentName])) {
  152. if (result[segmentName][index] === undefined) {
  153. result[segmentName][index] = {};
  154. }
  155. result[segmentName][index][fieldName] = item;
  156. } else {
  157. result[segmentName][fieldName] = item;
  158. }
  159. }
  160. });
  161. });
  162. Object.keys(segments).map(segmentName => {
  163. if (Array.isArray(result[segmentName])) {
  164. result[segmentName] = result[segmentName].filter(
  165. x => x as Record<string, string>,
  166. );
  167. }
  168. });
  169. return result;
  170. };
  171. export default parse;

这样当传入上面这个例子时你就可以得到如下结果:

  1. {
  2. ISA: {
  3. AuthorizationInformationQualifier: '00',
  4. AuthorizationInformation: ' ',
  5. SecurityInformationQualifier: '00',
  6. SecurityInformation: ' ',
  7. InterchangeSenderIDQualifier: 'ZZ',
  8. InterchangeSenderID: 'OECGROUP ',
  9. InterchangeReceiverIDQualifier: 'ZZ',
  10. InterchangeReceiverID: 'AAA ',
  11. InterchangeDate: '201120',
  12. InterchangeTime: '1304',
  13. InterchangeControlStandardsIdentifier: 'U',
  14. InterchangeControlVersionNumber: '00401',
  15. InterchangeControlNumber: '000259937',
  16. AcknowledgmentRequested: '0',
  17. UsageIndicator: 'P',
  18. ComponentElementSeparator: '>'
  19. },
  20. GS: {
  21. FunctionalIdentifierCode: 'QO',
  22. ApplicationSendersCode: 'OECGROUP',
  23. ApplicationReceiversCode: 'AAA',
  24. Date: '20201120',
  25. Time: '1304',
  26. GroupControlNumber: '259937',
  27. ResponsibleAgencyCode: 'X',
  28. VersionReleaseIndustryIdentifierCode: '004010'
  29. },
  30. ST: {
  31. TransactionSetIdentifierCode: '315',
  32. TransactionSetControlNumber: '0001'
  33. },
  34. B4: {
  35. ShipmentStatusCode: 'I',
  36. Date: '20201030',
  37. StatusTime: '0000',
  38. StatusLocation: 'CNYTN',
  39. EquipmentInitial: 'DFSU',
  40. EquipmentNumber: '773057',
  41. EquipmentStatusCode: 'L',
  42. EquipmentType: '4500',
  43. LocationIdentifier: 'CNYTN',
  44. LocationQualifier: 'UN',
  45. EquipmentNumberCheckDigit: '6'
  46. },
  47. N9: [
  48. {
  49. ReferenceIdentificationQualifier: 'BM',
  50. ReferenceIdentification: 'OERT210702J01222'
  51. },
  52. {
  53. ReferenceIdentificationQualifier: 'BN',
  54. ReferenceIdentification: 'MEDUZ7825111'
  55. },
  56. {
  57. ReferenceIdentificationQualifier: 'EQ',
  58. ReferenceIdentification: 'DFSU7730576'
  59. },
  60. {
  61. ReferenceIdentificationQualifier: 'SN',
  62. ReferenceIdentification: 'FJ10356721'
  63. },
  64. {
  65. ReferenceIdentificationQualifier: 'SCA',
  66. ReferenceIdentification: 'OERT'
  67. }
  68. ],
  69. Q2: {
  70. VesselCode: 'NONE',
  71. CountryCode: '',
  72. VoyageNumber: '045W',
  73. VesselCodeQualifier: 'L',
  74. VesselName: 'MAERSK ALGOL'
  75. },
  76. R4: [
  77. {
  78. PortOrTerminalFunctionCode: '5',
  79. LocationQualifier: 'UN',
  80. LocationIdentifier: 'CNYTN',
  81. PortName: 'YANTIAN PT',
  82. CountryCode: 'CN'
  83. },
  84. {
  85. PortOrTerminalFunctionCode: 'R',
  86. LocationQualifier: 'UN',
  87. LocationIdentifier: 'CNYTN',
  88. PortName: 'YANTIAN PT',
  89. CountryCode: 'CN'
  90. },
  91. {
  92. PortOrTerminalFunctionCode: 'L',
  93. LocationQualifier: 'UN',
  94. LocationIdentifier: 'CNYTN',
  95. PortName: 'YANTIAN PT',
  96. CountryCode: 'CN'
  97. },
  98. {
  99. PortOrTerminalFunctionCode: 'D',
  100. LocationQualifier: 'UN',
  101. LocationIdentifier: 'USSAV',
  102. PortName: 'SAVANNAH',
  103. CountryCode: 'US'
  104. },
  105. {
  106. PortOrTerminalFunctionCode: 'E',
  107. LocationQualifier: 'UN',
  108. LocationIdentifier: 'USSAV',
  109. PortName: 'SAVANNAH',
  110. CountryCode: 'US'
  111. }
  112. ],
  113. DTM: [
  114. {
  115. DateTimeQualifier: '140',
  116. Date: '20201030',
  117. Time: '0000',
  118. TimeCode: 'LT'
  119. },
  120. {
  121. DateTimeQualifier: '140',
  122. Date: '20201030',
  123. Time: '0000',
  124. TimeCode: 'LT'
  125. },
  126. {
  127. DateTimeQualifier: '140',
  128. Date: '20201110',
  129. Time: '1040',
  130. TimeCode: 'LT'
  131. },
  132. {
  133. DateTimeQualifier: '139',
  134. Date: '20201213',
  135. Time: '0000',
  136. TimeCode: 'LT'
  137. },
  138. {
  139. DateTimeQualifier: '139',
  140. Date: '20201215',
  141. Time: '1500',
  142. TimeCode: 'LT'
  143. }
  144. ],
  145. SE: {
  146. NumberOfIncludedSegments: '19',
  147. TransactionSetControlNumber: '0001'
  148. },
  149. GE: {
  150. NumberOfTransactionSetsIncluded: '1',
  151. GroupControlNumber: '259937'
  152. },
  153. IEA: {
  154. NumberOfIncludedFunctionalGroups: '1',
  155. InterchangeControlNumber: '000259937'
  156. }
  157. }

在这个例子中我并没有对缩写类的词汇或者字段进行拓展,保留了它们在文件中的样子,实际上使用的话你可以把它们拓展成人眼可直接阅读的原意可能会更好点,再有就是对不同的类型的字段进行类型转换也是不错的,比如说日期的转成日期格式,数字的转成数字格式。总之解析是个可以不断丰富的过程,但我这没有做很多。

补充阅读:

  1. 维基百科 - EDI
  2. 中国的港口大全
  3. 实时的港口监测信息

电子物流中的EDI 应用的更多相关文章

  1. 转:电子取证中AVI文件的文件雕复

    电子取证中AVI文件的文件雕复 收藏本文 分享 1引言在电子取证工作中,恢复数字设备中被删除的数据是极为重要的工作之一,恢复数据又分依赖系统元信息的传统数据恢复技术和不依赖系统元信息的文件雕刻.文件雕 ...

  2. 电子技术中的dB

    (所有内容来自网络: http://www.mscbsc.com/askpro/question13066) dB是功率增益的单位,表示一个相对值 分贝是用来表示 "功率"的数量对 ...

  3. Web开发从零单排之二:在自制电子请帖中添加留言板功能,SAE+PHP+MySql

    在上一篇博客中介绍怎样在SAE平台搭建一个html5的电子请帖网站,收到很多反馈,也有很多人送上婚礼的祝福,十分感谢! web开发从零学起,记录自己学习过程,各种前端大神们可以绕道不要围观啦 大婚将至 ...

  4. 【案例】电子生产中的排程问题如何解决?APS助力智能化排产

    共进电子是典型的消费类电子制造企业,以ODM业务为主,立足双O(OEM/ODM),发展自主品牌.其中,生产模式特点包括: 批量制造.多品种小批量.面向订单生产: 产品结构复杂,设计变更频繁:生产计划复 ...

  5. ERP中通过EDI导入资料的时候出现【Microsoft Office Excel不能访问文件‘C:\Windows\TEMP\433....’

    问题描述: ERP中导入单据的时候报错,Microsoft Office Excel不能访问文件'C:\Windows\TEMP\433....可能的原因有:·文件名称或路径不存在,文件正被其他程序使 ...

  6. 微信公众号 待发货-物流中-已收货 foreach break continue

    w <?php $warr = array(1,2,3); $w_break = 0; foreach($warr AS $w){ if($w==2)break; $w_break += $w; ...

  7. SDUTOJ 2711 4-2 电子时钟中的运算符重载

    watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvUl9NaXNheWE=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA ...

  8. PBOC2.0协议中电子存折/电子钱包中圈存交易流程

    通过圈存交易,持卡人可将其在银行相应账户上的资金划入电子存折或电子钱包中.这种交易必须在金融终端上联机进行并要求提交个人识别码(PIN)(无论电子存折还是电子钱包应用). 交易流程图如下: 1.1 发 ...

  9. MES系统在小批量电子行业生产管理中的应用

    小批量电子产品生产管理的主要问题 电子电器制造类企业,既有单件小批量生产,也有批量生产:有按库存生产,也有按订单生产,属于典型的离散制造行业.因产品的不同其生产工艺流程也不尽相同,生产设备的布置不是按 ...

随机推荐

  1. 使用BootstrapVue相关组件,构建Vue项目界面

    基于Vue的前端框架有很多,Element算一个,而BootstrapVue也可以非常不错的一个,毕竟Bootstrap也是CSS中的大佬级别的,它和Vue的整合,使得开发起来更加方便了.Bootst ...

  2. Spring Cache缓存技术,Cacheable、CachePut、CacheEvict、Caching、CacheConfig注解的使用

    前置知识: 在Spring Cache缓存中有两大组件CacheManager和Cache.在整个缓存中可以有多个CacheManager,他们负责管理他们里边的Cache.一个CacheManage ...

  3. 20个提高开发效率的JavaScript技巧

    减少代码行数和加快开发的技术! 我们在开发中,经常要写一些函数,如排序.搜索.寻找唯一的值.传递参数.交换值等,在这里我列出了我搜集的一些技术资源,可以像高手一样写出这些函数! JavaScript确 ...

  4. 听说你还不知道Spring是如何解决循环依赖问题的?

    Spring如何解决的循环依赖,是近两年流行起来的一道Java面试题. 其实笔者本人对这类框架源码题还是持一定的怀疑态度的. 如果笔者作为面试官,可能会问一些诸如"如果注入的属性为null, ...

  5. VBS脚本编程(3)——常用函数

    数据类型转换函数 1.Hex 函数 返回表示十六进制数字值的字符串. Hex(number) number 参数是任意有效的表达式. 说明 如果 number 参数不是整数,则在进行运算前将其四舍五入 ...

  6. Room-数据持久化存储(入门)

    @ 目录 一.简单使用 1.Entity 2.Dao 3.DataBase 4.使用 二.参数解析 1.Entity 2.Dao 3.查询方式 总结 # 前言 官方简介: Room 持久性库在 SQL ...

  7. 详解Redis主从复制原理

    文章首发于公众号 "蘑菇睡不着" 前言 Redis 的主从复制和 MySQL 差不多,主要起着 数据备份,读写分离等作用.所以说主从复制对 Redis 来说非常重要,而无论是面试还 ...

  8. 一致性hash原理 看这一篇就够了

    ​ 在了解一致性哈希算法之前,最好先了解一下缓存中的一个应用场景,了解了这个应用场景之后,再来理解一致性哈希算法,就容易多了,也更能体现出一致性哈希算法的优点,那么,我们先来描述一下这个经典的分布式缓 ...

  9. 一篇文章快速搞懂 Apache SkyWalking 的 OAL

    OAL简介 在流模式(Streaming mode)下,SkyWalking 提供了 观测分析语言(Observability Analysis Language,OAL) 来分析流入的数据. OAL ...

  10. Redis 雪崩、穿透、击穿、并发、缓存讲解以及解决方案

    1.缓存雪崩 数据未加载到缓存中,或者缓存同一时间大面积的失效,从而导致所有请求都去查数据库,导致数据库CPU和内存负载过高,甚至宕机. 比如一个雪崩的简单过程 1.redis集群大面积故障 2.缓存 ...