基于队列深度阈值绕过储存级存储器读取缓存的制作方法

专利2022-06-29  65


本公开涉及用于存储系统的方法、机器可读存储介质和存储系统。



背景技术:

在诸如服务器、存储阵列等计算设备中,处理资源可以以与其他类型的存储装置(例如,非易失性存储装置,诸如硬盘驱动器(hdd)、固态驱动器(ssd)等)相比低得多的延迟从缓存(cache)存储器中读取和写入到缓存存储器。然而,尽管这样的缓存存储器可以具有低得多的延迟,但是其通常使用在断电时不保留所存储的数据的(多个)易失性存储器设备来实施,并且通常具有与其他类型的存储装置(例如,非易失性存储装置,诸如hdd、ssd等)相比更高的成本(每容量单位)。



技术实现要素:

本公开在一方面提供一种用于存储系统的方法,所述方法包括:

利用所述存储系统的至少一个处理资源来确定在给定的时间段内从储存级存储器(scm)读取缓存中读取和写入到所述储存级存储器读取缓存的总数据量,所述存储系统包括主缓存、所述储存级存储器读取缓存和后端存储装置;

响应于基于所确定的总数据量而确定超过了所述储存级存储器读取缓存的数据速率阈值,来调整所述储存级存储器读取缓存的至少一个队列深度阈值;

响应于对所述储存级存储器读取缓存的输入/输出(io)请求,将所述储存级存储器读取缓存的队列深度与所述储存级存储器读取缓存的所述至少一个队列深度阈值之一进行比较;

基于所述输入/输出请求的类型和所述比较的结果,在以下操作之间进行选择:使用所述储存级存储器读取缓存来处理所述输入/输出请求、丢弃所述输入/输出请求、以及在不使用所述储存级存储器读取缓存的情况下处理所述输入/输出请求;以及

响应于所述选择来执行所选的处理或丢弃操作。

本公开在另一方面提供一种机器可读存储介质,所述至少一个非暂态机器可读存储介质包括指令,所述指令能够由存储系统的至少一个处理资源执行以进行以下操作:

确定在给定的时间段内从储存级存储器(scm)读取缓存中读取和写入到所述储存级存储器读取缓存的总数据量;

响应于基于所确定的总数据量而确定超过了所述储存级存储器读取缓存的数据速率阈值,来调整所述储存级存储器读取缓存的读取队列深度阈值和写入队列深度阈值中的至少一个;

响应于对所述储存级存储器读取缓存的输入/输出(io)请求:

当处理所述输入/输出请求包括写入所述储存级存储器读取缓存、并且所述储存级存储器读取缓存未完成的写入输入/输出的当前数量大于所述写入队列深度阈值时,丢弃所述输入/输出请求;以及

当所述输入/输出请求是对所请求数据的读取请求、并且所述储存级存储器读取缓存未完成的读取输入/输出的当前数量大于所述读取队列深度阈值时,绕过所述储存级存储器读取缓存并将所请求数据从所述存储系统的后端存储装置或从远程计算设备读取到所述存储系统的主缓存中。

本公开在又一方面提供一种存储系统,包括:

至少一个处理资源;

主缓存;

储存级存储器(scm)读取缓存;

后端存储装置;以及

至少一个非暂态机器可读存储介质,所述至少一个非暂态机器可读存储介质包括指令,所述指令能够由所述至少一个处理资源执行以进行以下操作:

执行后台进程,所述后台进程包括:

确定在给定时间段内传送到所述储存级存储器读取缓存和从所述储存级存储器读取缓存传送的数据量;以及

响应于基于所确定的数据量而确定超过了所述储存级存储器读取缓存的数据速率阈值来调整所述储存级存储器读取缓存的读取队列深度阈值和写入队列深度阈值中的至少一个;以及

执行输入/输出(io)进程,所述输入/输出进程包括:

响应于对所述储存级存储器读取缓存的输入/输出请求:

当所述输入/输出请求是将数据降级到所述储存级存储器读取缓存的请求、并且所述储存级存储器读取缓存的写入队列深度大于所述写入队列深度阈值时,丢弃所述输入/输出请求而不将所述数据写入所述储存级存储器读取缓存;并且

当所述输入/输出请求是对所请求数据的读取请求、并且所述储存级存储器读取缓存的读取队列深度大于所述读取队列深度阈值时,绕过所述储存级存储器读取缓存并将所请求数据从所述后端存储装置或从远程计算设备读取到所述主缓存中。

附图说明

以下具体实施方式参考附图,其中:

图1是用于基于队列深度阈值来选择是否使用储存级存储器(storageclassmemory,scm)读取缓存的示例计算系统的框图;

图2是包括将未完成(outstanding)io的数量与scm读取缓存的队列深度阈值进行比较的示例方法的流程图;

图3a是包括基于在某个时间段内向和从scm读取缓存传送的数据量来调整队列深度阈值的示例方法的流程图;

图3b是包括减小scm读取缓存的写入队列深度阈值和读取队列深度阈值之一的示例方法的流程图;

图3c是包括增大scm读取缓存的写入队列深度阈值和读取队列深度阈值之一的示例方法的流程图;

图4是包括选择是否使用scm读取缓存来处理输入/输出(io)请求的示例方法的流程图;并且

图5是用于调整多个scm缓存中的每一个的队列深度阈值的示例系统的框图。

具体实施方式

诸如服务器、存储阵列等计算设备可以包括由诸如(多个)动态随机存取存储器(dram)设备等易失性存储器实施的缓存存储器,其可以例如以双列直插式存储器模块(dimm)的形式提供。这样的计算设备还可以包括其他类型的存储装置,诸如即使在断电时也可以保留存储在其中的数据的(多个)非易失性存储设备。计算设备(诸如存储阵列)可以使用这样的(多个)非易失性存储设备(例如,hdd、ssd或其组合)等来实施计算设备(例如,存储阵列)的“后端存储装置”,以将数据永久存储在所述计算设备中。这样的(多个)存储设备可以使用各种通信协议,诸如与小型计算机系统接口(scsi)(例如,串行附接scsi(sas))、串行at附件(sata)或其他合适的(多种)协议有关的那些通信协议。

在这样的计算设备中,计算设备的(多个)处理资源可以以与实施计算设备的后端存储装置(例如,hdd、ssd等,或其组合)的(多个)存储设备相比低得多的延迟(即,快得多)从实施缓存存储器的(多个)存储器设备中读取数据和向所述(多个)存储器设备写入数据。但是,与后端存储装置相比,缓存存储器的容量相对较低,因此缓存存储器可能限于一次仅存储计算设备存储的数据的较小子集(例如,比计算设备存储的全部数据少得多)。在这样的示例中,在给定时间仅可以(以缓存存储器提供的低延迟)从缓存存储器中访问存储在计算设备的后端存储装置中的数据中的相对较小比例。

在一些示例中,使用非易失性存储装置作为逻辑上位于计算设备的主缓存与后端存储装置(例如,其他(多个)非易失性存储设备)之间的扩展读取缓存(或“中间”读取缓存)来扩展利用易失性存储器实施的缓存存储器(其在本文中称为计算设备的“主缓存”)的容量可能是有利的。即使这种扩展读取缓存可能具有比主缓存更高的延迟,但扩展读取缓存可以具有比计算设备的后端存储装置低得多的延迟,并且因此,当主缓存处存在缓存未命中(cachemiss)时,从扩展读取缓存(如果计算设备中存在的话)中读取所请求数据可以允许以与将所请求数据从计算设备的后端存储装置读取到主缓存中的情况相比低得多的延迟将所请求数据读取到主缓存中。

例如,当主缓存处存在缓存未命中时(即,当确定由读取请求所请求的数据不存在于主缓存中时),将所请求数据从后端存储装置的sashdd读取到主缓存中可能要花费一个或多个毫秒(ms)(或更长的时间,这取决于sashdd的当前利用率),由此向所请求的读取添加了至少一个或多个毫秒的延迟。将所请求数据从后端存储装置的sasssd读取到主缓存中可能要花费大约125微秒或更多微秒(或更长的时间,这取决于其当前利用率),这会由此向所请求的读取添加大量的附加延迟。类似地,将所请求数据从扩展读取缓存的sasssd读取到主缓存中(即,使用一个或多个sasssd来实施上述扩展读取缓存的情况)可能也要花费大约125微秒或更多微秒(或更长的时间,这取决于sashdd的当前利用率),从而向所请求的读取添加了大量的附加延迟。

相反,例如,当使用储存级存储器(scm)来实施扩展读取缓存时,在一些示例中,最快仅需十几微秒就可以将所请求数据从实施扩展读取缓存的scm读取到主缓存中,从而与sasssd相比显著减少将数据读取到主缓存中所添加的延迟(例如,约十分之一的延迟)。在本文描述的示例中,(至少部分地)实施扩展读取缓存的scm卡在本文中可以称为“scm读取缓存(scmreadcache)”。

然而,单个scm设备(在本文中可以称为scm“卡”)具有用于以较低延迟从scm卡读取数据和将数据写入到scm卡的有限速率(例如,本文中的“数据速率”或“带宽”)。例如,单个scm卡可以具有大约为2.4gb/秒的数据速率阈值,可以在所述阈值以下以较低延迟从scm卡读取数据或将数据写入到scm卡。在这样的示例中,当足够快地向scm卡发出足够的输入/输出(io)请求(或本文的“io”;例如,读取请求和/或写入请求)使得这些io请求正在尝试从scm卡中读取和/或向scm卡写入的累计数据量超过了数据速率阈值(例如,在一些示例中大约为2.4gb/秒)时,进一步发出的io请求经历的延迟可能与在超过该阈值之前发出的那些io请求相比高得多。例如,在超过数据速率阈值之后发出的io请求经历的延迟(例如,从scm读取到主缓存中的时间)可能接近或甚至大于上述的sasssd的延迟(例如,大约125微秒或更长)。

在这样的示例中,向scm卡发出io请求使得超过数据速率阈值可能会导致在超过阈值之后发出的那些io请求的延迟意外增加。在这样的示例中,一旦已经超过了scm卡的数据速率阈值,就从后端存储装置而不是从scm卡中进行读取,由此能够获得更低的延迟。

为了解决这些问题,本文描述的示例可以包括用于选择性地(或智能地)绕过scm读取缓存和选择性地(或智能地)将数据从主缓存降级到scm读取缓存的技术。例如,本文描述的示例可以监测在给定时间段内向和从scm读取缓存传送的数据量,并且使用该信息来自适应地调整scm读取缓存的一个或多个队列深度阈值,并丢弃或重定向将会超过适当的队列深度阈值的对scm读取缓存的io请求。以这种方式,本文描述的示例可以管理向和从scm读取缓存传送数据的速率,以避免或减少在超过数据速率阈值之后向scm读取缓存发出的io请求的数量,从而避免或减少与在未超过数据速率阈值的情况下发出的io请求相比在scm读取缓存处遭受显著更高延迟的io请求的数量。

例如,本文描述的示例可以确定在给定时间段内从scm读取缓存读取和写入到scm读取缓存的总数据量,并且响应于基于所确定的数据量而确定超过了scm读取缓存的数据速率阈值来调整scm读取缓存的至少一个队列深度阈值。此类示例还可以响应于对scm读取缓存的io请求来将scm读取缓存未完成的io的当前数量与scm读取缓存的(多个)队列深度阈值之一进行比较,并且基于io请求的类型和比较的结果,在以下操作之间进行选择:(1)使用scm读取缓存来处理io请求,(2)丢弃io请求,以及(3)在不使用scm读取缓存的情况下处理io请求。然后,这样的示例可以执行所选的处理动作或丢弃动作。

以这种方式,本文描述的示例可以减少向scm读取缓存发出的、由于超过了scm读取缓存的数据速率阈值而遭受显著更高的延迟的io请求的数量。在一些示例中,监测在给定时间段内从scm读取缓存读取和写入到scm读取缓存的实际数据量可以实现对是否超过了scm读取缓存的数据速率阈值的相对准确的跟踪。

图1是用于基于队列深度阈值来选择是否使用储存级存储器(scm)读取缓存150的示例计算系统102的框图。在图1所示的示例中,计算系统102(或“系统”或“存储系统”102)包括计算设备100,所述计算设备包括至少一个处理资源110和至少一个机器可读存储介质120,所述机器可读存储介质至少包括存储指令121(例如,用其编码),所述存储指令可由计算设备100的至少一个处理资源110执行以实施本文关于指令121所描述的功能。指令121可以至少包括可由至少一个处理资源110执行的指令122、124和126。计算设备100可以是如本文描述的任何合适类型的计算设备,诸如服务器、存储阵列等。

在图1所示的示例中,计算设备100可以包括存储器130,所述存储器可以由易失性存储器(例如,一个或多个易失性存储器设备,诸如(多个)dram设备、(多个)dimm等)来实施。存储器130可以实施计算设备100的主缓存140,以供处理资源110使用。在一些示例中,存储器130还可以存储如下所述由指令121使用的各种数据132、134、136和138。在一些示例中,数据132、134、136和138可以存储在实施主缓存140的(一个或多个)相同的存储器设备上。在一些示例中,数据132、134、136和138可以存储在与实施主缓存140的(一个或多个)存储器设备不同的(一个或多个)存储器设备上。

在图1所示的示例中,计算设备100可以包括后端存储装置160以永久地存储计算设备100的数据。后端存储装置160可以包括一个或多个存储设备,诸如存储设备162、164、166、168等。后端存储装置160的(多个)存储设备中的每一个可以是非易失性存储设备(诸如hdd、ssd等),并且后端存储装置160可以包括此类非易失性存储设备的任何组合。在一些示例中,计算设备100可以是包括用于存储数据的多个存储设备(例如,后端存储装置160的存储设备)的存储阵列。尽管在图1的示例中的后端存储装置160中图示了四个存储设备,但是在示例中,后端存储装置160可以包括任何合适数量的一个或多个存储设备(例如,四个以上,等)。

在图1所示的示例中,计算设备100可以包括由包括scm的scm设备(或scm芯片)实施的scm读取缓存150,以存储数据。在本文描述的示例中,计算设备100可以将scm读取缓存150用作用于扩展主缓存140的容量的扩展读取缓存以服务于读取请求。scm读取缓存150也可以在逻辑上(或在功能上)位于主缓存140与后端存储装置160之间,并且在本文中可以被称为“中间”读取缓存。在这样的示例中,scm读取缓存150可以在逻辑上(或在功能上)位于主缓存140与后端存储装置160之间,因为例如,数据可以从主缓存140被转储清除(flush)或降级到scm读取缓存150中,使得所述数据可以随后从scm读取缓存150被读回主缓存140中,以避免将数据从后端存储装置160读取到主缓存140中的较高延迟过程。

在本文描述的示例中,scm可以是一种非易失性存储器(nvm)技术,并且可以使用与nvmexpresstm(nvmetm)一致的协议进行通信。在本文描述的示例中,可以以多种不同形式中的任何一种来实施用于(至少部分地)实施scm读取缓存150的scm设备,所述不同形式如例如3dxpoint芯片(或设备)、3dxpointdimm、相变存储器(pcm)设备(诸如相变随机存取存储器(ram)设备)、磁性ram(mram)设备(诸如自旋转移力矩(stt)ram设备)、电阻式ram(rram)设备、忆阻器设备等、或其组合。在一些示例中,scm可以实施基于块的访问。在其他示例中,scm可以实施基于存储器的语义以进行数据访问。在这样的示例中,基于scm的dimm在本文中可以用于实施scm读取缓存。在一些示例中,后端存储装置160的一个或多个存储设备可以使用与nvmetm一致的协议进行通信。在本文描述的示例中,scm读取缓存150可以包括由具有比sasssd(其是一种nvm)更低延迟的scm设备实施的读取缓存。

现在将在此结合图1和图2描述示例,其中图2是包括将未完成io的数量与scm读取缓存150的队列深度阈值进行比较的示例方法200的流程图。尽管下文参考图1的计算设备100描述了方法200的执行,但可以利用适用于执行方法200的其他计算设备(例如,图5的计算设备500)。另外,方法200的实施方式不限于这种示例。尽管图2的流程图示出了执行某些功能的具体顺序,但方法200不限于该顺序。例如,流程图中连续示出的功能可以以不同顺序执行,可以同时或部分同时或以其组合的形式执行。

参考图1和图2,在方法200的205处,计算设备100的指令122可以(例如,当由处理资源110执行时)确定(例如,使用处理资源110)在给定时间段内从scm读取缓存150读取和写入到所述scm读取缓存的总数据量132。在一些示例中,指令122可以基于累计在给定时间段内完成的由对scm读取缓存150的io请求从scm读取缓存150读取或写入到所述scm读取缓存的数据量,来确定该总数据量132,如下文结合图3a更详细描述的。

在210处,指令124可以(例如,当由处理资源110执行时)响应于基于所确定的数据量132而确定超过了scm读取缓存150的数据速率阈值134,来调整scm读取缓存150的至少一个队列深度阈值138。例如,指令124可以在所确定的总数据量132大于数据速率阈值134时减小(多个)队列深度阈值中的至少一个,并且可以在所确定的总数据量132不大于(即,小于或等于)数据速率阈值134时增大(多个)队列深度阈值中的至少一个,如下文结合图3a至图3c更详细描述的。

在一些示例中,可以用相同的术语(例如,数据量)表达或表示数据速率阈值134和所确定的数据量132,并且在这样的示例中,指令124可以直接比较这些值以进行上述确定。在其他示例中,可以用与所确定的数据量132(例如,数据量)不同的术语(例如,数据速率/时间单位)来表达或表示数据速率阈值134,并且在这样的示例中,指令124可以在比较两个值之前首先将这两个值用等同的术语表示(例如,通过转换这些值中的一个或两个)。

在本文描述的示例中,在给定时间scm读取缓存的“队列深度”可以是在给定时间未完成(例如,向scm读取缓存发出的并且在给定时间尚未由scm读取缓存完成或尚未被报告为完成)的io请求(或io)的数量。在本文描述的示例中,scm读取缓存的“队列深度阈值”可以是与scm读取缓存的队列深度有关(例如,针对scm读取缓存的队列深度而设置)的可调阈值。在本文描述的一些示例中,队列深度可以例如是指读取队列深度或写入队列深度。在这样的示例中,在给定时间scm读取缓存的“读取队列深度”可以是向scm读取缓存发出的在给定时间未完成(即,向scm读取缓存发出的并且在给定时间尚未完成或尚未被报告为完成)的读取请求的总数。在本文描述的示例中,在给定时间scm读取缓存的“写入队列深度”可以是向scm读取缓存发出的在给定时间未完成(即,向scm读取缓存发出的并且在给定时间尚未完成或尚未被报告为完成)的写入请求(例如,降级请求)的总数。在这样的示例中,队列深度阈值可以例如是指读取队列深度阈值或写入队列深度阈值。在这样的示例中,scm读取缓存的“读取队列深度阈值”可以是与scm读取缓存的读取队列深度有关(例如,针对scm读取缓存的读取队列深度而设置)的可调阈值,并且scm读取缓存的“写入队列深度阈值”可以是与scm读取缓存的写入队列深度有关(例如,针对scm读取缓存的写入队列深度而设置)的可调阈值。

在一些示例中,指令121可以实施scm读取缓存150的单个队列深度阈值138,并且指令124可以调整单个队列深度阈值138。在其他示例中,指令121可以实施scm读取缓存150的多个队列深度阈值,诸如上述scm读取缓存150的读取队列深度阈值和写入队列深度阈值(例如,参见图5的读取队列深度阈值137和写入队列深度阈值139)。在这样的示例中,指令124可以一次选择性地调整这些阈值之一(如下文结合图3b和图3c更详细描述的)。在这样的示例中,指令124可以以分阶段的方式调整读取队列深度阈值和写入队列深度阈值。在其他示例中,指令124可以一致地调整读取队列深度阈值和写入队列深度阈值(例如,同时在相同方向上调整这两者以将它们保持在相同的值)。

在一些示例中,读取队列深度阈值可以是响应于从scm读取缓存150中读取所请求数据的读取请求180,而将scm读取缓存150的当前读取队列深度与之进行比较的阈值。在一些示例中,写入队列深度阈值可以是可以响应于将干净数据从主缓存140写入到scm读取缓存150的降级请求,而将scm读取缓存150的当前写入队列深度与之进行比较的阈值。在存在单个队列深度阈值的示例中,可以响应于上述读取请求或降级请求,将scm读取缓存150的当前队列深度(例如,读取队列深度、写入队列深度或这两者的组合)与单个队列深度阈值进行比较。在本文描述的示例中,scm读取缓存的“当前”队列深度(例如,读取队列深度或写入队列深度)(或scm读取缓存未完成的读取或写入(降级)io的“当前”数量)可以是响应于io请求而检查或确定scm读取缓存的队列深度的值时(在本文描述的示例中可以是“当前”时间)该队列深度的值(或未完成io的数量)。

在方法200的215处,指令126可以(例如,当由处理资源110执行时)接收对scm读取缓存150的io请求180(例如,从scm读取缓存150读取数据的读取请求或者将数据从主缓存140写入到scm读取缓存的降级请求),并且响应于io请求180而确定io请求180的类型(例如,读取请求或降级请求),并将scm读取缓存150未完成的io(例如,未完成的读取io或写入io)的当前数量136与scm读取缓存150的(多个)队列深度阈值138之一进行比较(如下文结合图4更详细描述的)。在一些示例中,未完成请求的数量136实际上可以包括两个值:(i)scm读取缓存150的未完成读取io的数量,以及(ii)scm读取缓存150的未完成写入io的数量(例如,参见图5的未完成读取io133和未完成写入io135)。在这样的示例中,响应于io请求180,指令126可以确定io请求180的类型,并且当io请求180是读取请求时,指令126可以将scm读取缓存150未完成的读取io的当前数量与scm读取缓存150的读取队列深度阈值进行比较(如下面结合图4更详细描述的)。在其他示例中,当io请求180是降级请求时,指令126可以将scm读取缓存150未完成的写入io的当前数量与scm读取缓存150的写入队列深度阈值进行比较(如下面结合图4更详细描述的)。

在本文描述的示例中,对scm读取缓存150的io请求(或io)的类型可以包括读取请求和降级请求。在一些示例中,读取请求可以是基于主机io请求从scm读取缓存中读取某些所请求数据的请求,并且降级请求可以是由包括scm读取缓存的计算设备生成的降级请求(即,由计算设备本身内部生成的请求、而不是主机io)。在这样的示例中,主机(host)可以是能够经由一个或多个计算机网络与计算设备进行通信的任何合适的计算设备。参考图5,例如,计算设备500(如下所述)可以经由一个或多个计算机网络从与计算设备500分离的主机计算设备接收读取请求(或读取io)582。在一些示例中,对某些所请求数据的这种主机读取请求582可以触发对scm读取缓存的读取请求180以读取(至少一些)所请求数据(例如,当读取请求582中所请求的数据不存在于主缓存140中但存在于scm读缓存中时)。对图1的计算设备100的scm读取缓存150的读取请求180(即,用于读取某些所请求数据)可以类似地由对来自与计算设备100分离的主机计算设备的所请求数据的读取请求来触发。相反,例如,当计算设备确定主缓存140太满并且决定将干净数据从主缓存140降级到scm读取缓存150时,本文描述的示例中的降级请求180可以由包括scm读取缓存150的计算设备(例如,计算设备100)内部生成。在本文描述的示例中,计算机网络可以包括例如局域网(lan)、虚拟局域网(vlan)、无线局域网(wlan)、虚拟专用网络(vpn)、因特网等、或其任何组合。

返回图2,在220处,指令126可以(例如,当由处理资源110执行时)基于io请求180的类型和比较的结果,在以下操作之间进行选择:(1)使用scm读取缓存150来处理io请求180,(2)丢弃io请求180(即,不执行io请求180),以及(3)在不使用scm读取缓存150的情况下处理io请求180。响应于所述选择,指令126可以执行所选的对io请求180的处理或丢弃(如下文结合图4更详细描述的)。

例如,基于(i)确定io请求180是将数据降级到scm读取缓存150的请求,并且(ii)比较的结果指示scm读取缓存150未完成的写入io的当前数量大于scm读取缓存150的写入队列深度阈值,指令126可以选择丢弃io请求180,并且作为响应,可以执行所述选择(在方法200的225处),这包括丢弃io请求180而不将(降级请求的)数据写入到scm读取缓存150(例如,并且不将数据写入到后端存储装置160或另一远程计算设备的存储装置)。

在本文描述的示例中,将数据降级到scm读取缓存150的请求是将干净数据从主缓存140降级到scm读取缓存150的请求。在本文描述的示例中,主缓存140中的“干净”数据是从后端存储装置160被带入主缓存140中以来尚未在主缓存140中进行更改的数据。当首先将数据读取到主缓存140中时,所述数据是从后端存储装置160中读取的。因此,如果该数据是主缓存140中的“干净”数据(例如,所述数据自被从后端存储装置160读取到主缓存140中以来未进行更改),则在降级请求时所述干净数据的副本仍存在于后端存储装置160中。相反,在本文描述的示例中,“脏”数据是不存在于后端存储装置160中的数据,因为例如,所述数据在主缓存140中时(在所述数据被从后端存储装置160带入之后)被修改并且尚未以更改的状态被转储清除回到后端存储装置160。

在这样的示例中,丢弃对脏数据的转储清除请求可能存在丢失数据(例如,对数据的更改)的风险。但是,在本文描述的示例中,降级请求涉及将干净数据从主缓存140降级到scm读取缓存150,并且如上所述,干净数据已经存在于后端存储装置160中,因此丢弃降级请求将不太可能导致任何数据丢失。

在本文描述的示例中,当计算设备100已经确定主缓存140太满并且需要释放主缓存140中的空间(例如,通过使最近最少使用的数据降级等)时,可以接收降级请求。在这样的示例中,可以将干净数据从主缓存140降级到scm读取缓存150,在那里可以保存所述干净数据以便稍后以比在该稍后时间从后端存储装置160中读取所述干净数据的情况下更低的延迟从scm读取缓存150中读取。虽然降级到scm读取缓存150可以节省稍后重新读取数据的时间,但潜在的未来节省可能不值得写入到scm读取缓存150以执行降级的scm读取缓存150上的附加负荷。例如,当scm读取缓存150最近正在以数据速率阈值134、高于所述数据速率阈值、或接近所述数据速率阈值处理数据时,处理降级请求的附加负荷可能超过数据速率阈值134并且导致例如以比在未超过数据速率阈值134的情况下高得多的延迟执行稍后的io。如此,在一些情况下可能优选丢弃降级请求。

如此,在本文描述的示例中,指令124可以基于从scm读取缓存150读取和写入到scm读取缓存的数据量(如上文参考指令122所描述的)来调整scm读取缓存150的写入队列深度阈值。在这样的示例中,指令126可以选择在scm读取缓存150的当前写入队列深度大于scm读取缓存150的当前写入队列深度阈值时丢弃降级请求,以避免使scm读取缓存150过载并避免潜在地引起稍后的io的高得多的延迟。在这样的示例中,当丢弃降级请求时,可以从主缓存140中释放将要从主缓存140降级的干净数据,而不将所述干净数据写入到其他地方(例如,scm读取缓存150或后端存储装置160)。

返回图2的220,当满足不同的标准时,指令126可以做出替代性选择。例如,基于(i)确定io请求180是对所请求数据的读取请求,并且(ii)比较的结果指示scm读取缓存150未完成的读取io的当前数量大于scm读取缓存150的读取队列深度阈值,指令126可以选择在不使用scm读取缓存150的情况下处理io请求180。作为响应,指令126可以执行所述选择,这包括绕过scm读取缓存150并将所请求数据从其他存储装置(例如,直接从后端存储装置160或者从本文的远程计算设备或“节点”的存储装置)读取到主缓存140中。在其他示例中,指令126进行进一步的确定(例如,基于将从其中读取所请求数据的其他存储装置的位置和利用率),并且还部分地基于这些进一步的确定来决定是否绕过scm读取缓存150。

在这样的示例中,尽管从scm读取缓存150进行的读取可能具有比从后端存储装置160进行的读取更低的延迟(如上所述),但是当scm读取缓存150所经历的数据速率超过数据速率阈值134时,则对scm读取缓存的随后io可能会经历显著更高的延迟,如上所述。如此,当scm读取缓存150的当前队列深度136超过scm读取缓存150的当前队列深度阈值138(例如,读取队列深度阈值)时,指令126可以绕过scm读取缓存150并直接从后端存储装置160进行读取,其中该队列深度阈值138是基于scm读取缓存150所经历的数据速率来调整的,如上所述。在这样的示例中,控制队列深度阈值并基于该阈值选择性地绕过scm读取缓存150可以有利地将scm读取缓存150所经历的数据速率保持低于数据速率阈值134。

返回图2的220,当满足不同的标准时,指令126可以做出另一选择。例如,基于(i)确定io请求180是将数据降级到scm读取缓存150的请求,并且(ii)比较的结果指示scm读取缓存150未完成的写入io的当前数量不大于scm读取缓存150的写入队列深度阈值,指令126可以选择使用scm读取缓存150来处理io请求180,并且作为响应将数据降级(例如,写入)到scm读取缓存150。

在一些示例中,当满足不同的标准时,指令126可以做出另一选择。例如,基于(i)确定io请求180是从scm读取缓存150中读取所请求数据的读取请求,并且(ii)比较的结果指示scm读取缓存150未完成的读取io的当前数量不大于scm读取缓存150的读取队列深度阈值,指令126可以选择使用scm读取缓存150来处理io请求180,并且作为响应将所请求数据从scm读取缓存150读取到主缓存140中。

现在将在下文结合图3a、图3b、图3c和图4的示例更详细地描述上文结合图2的方法200所描述的示例。图3a是包括基于在某个时间段内向和从scm读取缓存传送的数据量来调整队列深度阈值的示例方法300a的流程图。图3b是包括减小scm读取缓存的写入队列深度阈值和读取队列深度阈值之一的示例方法300b的流程图。图3c是包括增大scm读取缓存的写入队列深度阈值和读取队列深度阈值之一的示例方法300c的流程图。图4是包括选择是否使用scm读取缓存来处理输入/输出(io)请求的示例方法400的流程图。

尽管下文参考图1的计算设备100描述了对方法300a、300b、300c和400的执行,但可以利用适于执行这些方法的其他计算设备(例如,图5的计算设备500)。另外,这些方法的实施方式不限制于这种示例。尽管图3a至图4的流程图各自示出了执行某些功能的具体顺序,但这些方法不限于该顺序。例如,流程图中连续示出的功能可以以不同顺序执行,可以同时或部分同时或以其组合的形式执行。

参考图1和图2,如上所述,指令122可以确定在给定时间段内从scm读取缓存150读取和写入到所述scm读取缓存的总数据量132(在方法200的205处),并且指令124可以响应于基于所确定的数据量132而确定超过了scm读取缓存150的数据速率阈值134,来调整scm读取缓存150的至少一个队列深度阈值138(在方法200的210处)。在一些示例中,这些功能可以通过在一些示例中至少部分地由指令122和124(例如,参见图5的用于实施后台进程的后台指令522)实施的后台进程(或线程)来周期性地执行。

下文可以结合图3a至图3c来更详细地描述上文结合图2的205和210所描述的功能。参考图3a,方法300a可以在305处开始,其中,指令122可以确定某个时间段是否已经过去。例如,如上所述,后台进程可以周期性地执行确定总数据量132并且选择性地调整(多个)队列深度阈值。

在图3a的示例中,可以在多个连续时间段中的每个时间段结束时执行所述确定和选择性调整。这样的时间段每个可以是固定的时间量,如例如大约100毫秒。在这样的示例中,在每个时间段期间(即,在所述时间段还没有过去时),指令122可以例如通过在完成对scm读取缓存150的每个io之后累计由已完成io所读取或写入的数据量,来累计在所述时间段期间从scm读取缓存150读取和写入到所述scm读取缓存的总数据量132。例如,如果当前时间段还没有过去(305处为“否”),则方法300a可以前进到310,在310,指令122可以确定对scm读取缓存150的任何未完成io请求是否已经完成。如果是这样(310处为“是”),则指令122可以基于已完成io请求的大小(即,由io读取或写入的数据量)来增大在当前时间段内从scm读取缓存150读取和写入到所述scm读取缓存的总数据量。例如,在315处,对于对scm读取缓存150的每个已完成io,指令122可以将由对scm读取缓存150的最近完成的(多个)io请求所读取和/或写入的数据量添加到正为当前时间段计算的总和中。在315处适当增大总数据量132之后,指令122可以返回到305(例如,以确定当前时间段是否已经过去)。如果在310处对scm读取缓存150的任何未完成io都没有完成(310处为“否”),则指令122可以返回到305(例如,以确定当前时间段是否已经过去)。

在305处,当当前时间段已经过去时(例如,在当前100ms时间段结束时)(305处为“是”),方法300a可以前进到320,在320,指令122可以例如通过检查在已过去的时间段(例如,最近的100ms时间段)内累计的总数据量来确定在该已过去的时间段内从scm读取缓存150读取和写入到所述scm读取缓存的总数据量132,如上所述。在320处,指令122可以进一步基于所述时间段内的累计总数据量132来确定在所述时间段内是否超过了scm读取缓存150的数据速率阈值134(如上文结合图1和图2所描述的),并且基于所述确定,指令124可以调整scm读取缓存150的至少一个队列深度阈值。

例如,如果指令122基于累计总数据量132确定在所述时间段内超过了数据速率阈值134(320处为“是”),则指令124可以减小scm读取缓存150的至少一个队列深度阈值(例如,如结合图3b针对存在多个队列深度阈值的示例更详细地描述的)。然后在350处,指令122可以将所述时间段内的总数据量132重置为零(以便为下一个时间段做准备),并且开始下一个时间段和/或在下一个时间段(例如,下一个100ms时间段)内再次开始(在305处)执行图3a的方法300a。

在其他示例中,如果指令122基于累计总数据量132而确定在所述时间段内未超过数据速率阈值134(320处为“否”),则指令124可以增大scm读取缓存150的至少一个队列深度阈值(例如,如结合图3c针对存在多个队列深度阈值的示例更详细地描述的)。然后在350处,指令122可以将所述时间段内的总数据量132重置为零(以便为下一个时间段做准备),并且开始下一个时间段和/或在下一个时间段(例如,下一个100ms时间段)内再次开始(在305处)执行图3a的方法300a。

在一些示例中,计算设备100可能正在执行从scm读取缓存150进行读取和写入到scm读取缓存的多个不同线程(例如,一个或多个读取线程和一个降级线程)。在这样的示例中,用于确定在所述时间段内向/从scm读取缓存150传送的总数据量的指令122可以表示在所述时间段内由所有这些线程所读取/写入的总数据量(例如,通过使累计总量基于由scm读取缓存150完成的每个io的大小)。

在本文描述的示例中,通过重复测量在固定时间间隔内从scm读取缓存150读取和写入到scm读取缓存的总数据量132,指令122可以获取在最近的时间段内去往和来自scm读取缓存150的数据传送速率(例如,其可以是所述时间段内的总数据量132除以所述时间段的长度)的周期性样本。通过获取表示scm读取缓存150所经历的数据速率的那些周期性样本,指令122可以收集关于在先前时间段内是否超过了数据速率阈值134的有用度量。

尽管本文在100ms时间段的背景下描述了示例,但是可以利用其他合适的时间段。在本文描述的示例中,在使用较短的间隔(例如10ms,这可以提供关于最近观察到的数据传送速率的更频繁的反馈,但也可能消耗更多的资源)与使用较长的间隔(例如1秒,这可以消耗更少的资源,但也可能提供不那么频繁的反馈,并且更容易因数据传送的临时突发而失真)之间可能存在权衡。虽然在一些示例中100ms可能是这些竞争问题之间的合适折衷,但在其他示例中,其他时间段可能更合适,尤其是随着计算设备资源的性能能力的提高。

再次参考图3a,在存在scm读取缓存150的一个队列深度阈值138的示例中,指令124可以在方法300a的340处增大该一个队列深度阈值138,并且可以在方法300a的330处减小该一个队列深度阈值138。在存在被指令124相同对待的多个队列深度阈值138(例如,读取队列深度阈值和写入队列深度阈值138)的其他示例中,指令124可以在方法300a的340处增大这两个队列深度阈值138,并且可以在方法300a的330处减小这两个队列深度阈值138。在其他示例中,如上所述,指令121可以实施计算设备100的scm读取缓存150的不同的读取队列深度阈值和写入队列深度阈值,并且指令124可以不同地调整这些阈值。例如,指令124可以在330处要么减小读取队列深度阈值、要么减小写入队列深度阈值(但不是两者都减小),并且指令124可以在340处要么增大读取队列深度阈值、要么增大写入队列深度阈值(但不是两者都增大)。

在一些示例中,指令124可以不同地且以分阶段的方式调整scm读取缓存150的读取队列深度阈值和写入队列深度阈值,如下文结合图3b和图3c所描述的。例如,参考图3b,方法300b可以是用于实施方法300a的框330(即,减小队列深度阈值)的示例方法,在所述示例方法中,以分阶段的方式调整读取队列深度阈值和写入队列深度阈值。例如,当指令122确定在所述时间段内读取/写入的总数据量132大于数据速率阈值134时(320处为“是”),指令124可以前进到方法300b的332,在332,指令124可以确定写入队列深度阈值是否高于第一最小值(例如,零或另一个合适的值)。如果是(332处为“是”),则在334处,指令124可以减小写入队列深度阈值,并且然后前进到(图3a的)方法300a的350。如果否(332处为“否”;即,写入队列深度阈值为第一最小值),则在336处,指令124可以确定读取队列深度阈值是否高于第二最小值(例如,读取队列深度阈值的最大值或默认值的10%)。如果是(336处为“是”),则在338处,指令124可以减小读取队列深度阈值,并且然后前进到(图3a的)方法300a的350。当读取队列深度阈值为第二最小值时(336处为“否”),则在339处,指令124可以既不减小写入队列深度阈值也不减小读取队列深度阈值,并且然后前进到(图3a的)方法300a的350。

在图3b的示例中,当以分阶段的方式减小不同的队列深度阈值时,指令124可以首先减小写入队列深度阈值直到达到最小值(例如0或另一个合适的最小值),并仅在写入队列深度阈值已经达到最小值之后才开始减小读取队列深度阈值,并且然后在写入队列深度阈值和读取队列深度阈值均达到它们各自的最小值(可以相同或不同)的情况下停止减小任一个队列深度阈值。以这种方式,本文描述的示例可以使从scm读取缓存150中进行读取优先于写入(诸如将干净数据从主缓存140写入到scm读取缓存150的降级操作)。通过以这种方式调整队列深度阈值,与绕过scm读取缓存150以进行读取操作相比,可以更积极地丢弃降级操作。这在一些示例中可能是有利的,因为例如当前的读取操作将立即实现从scm读取缓存150的较低延迟读取(与从后端存储装置160进行读取相比)的益处,而鉴于在稍后的时间实现从scm读取缓存150的较低延迟读取的可能性,降级操作实际上会导致写入到scm读取缓存150的当前成本。如此,与绕过scm读取缓存150以进行读取相比,更积极地丢弃写入可能是有利的。

在本文描述的示例中,队列深度阈值可以增大或减小任何合适的量,诸如固定量、成比例的量(例如,队列深度阈值的当前值的10%)或任何其他合适的固定量或可变量。

现在参考图3c,方法300c可以是用于实施方法300a的框340(即,增大队列深度阈值)的示例方法,在所述示例方法中,以分阶段的方式调整读取队列深度阈值和写入队列深度阈值。例如,当指令122确定在所述时间段内读取/写入的总数据量132不大于数据速率阈值134时(320处为“否”),指令124可以前进到方法300c的342,在342,指令124可以确定读取队列深度阈值是否低于第一最大值(例如,读取队列深度阈值的默认值(如100、500)或任何其他合适的值)。如果是(342处为“是”),则指令124可以增大读取队列深度阈值,并且然后前进到(图3a的)方法300a的350。如果否(342处为“否”;即,读取队列深度阈值为第一最大值),则在346处,指令124可以确定写入队列深度阈值是否低于第二最大值,所述第二最大值可以与第一最大值相同或不同(例如,写入队列深度阈值的默认值(如100、500)或任何其他合适的值)。如果是(346处为“是”),则指令124可以增大写入队列深度阈值,并且然后前进到(图3a的)方法300a的350。如果否(346处为“否”;即,写入队列深度阈值为第二最大值),则指令124可以确定既不增大读取队列深度阈值也不增大写入队列深度阈值。

在图3c的示例中,当以分阶段的方式增大不同的队列深度阈值时,指令124可以首先增大读取队列深度阈值直到达到第一最大值,并仅在读取队列深度阈值已经达到第一最大值之后才开始增大写入队列深度阈值,并且然后在读取队列深度阈值和写入队列深度阈值均达到它们各自的最大值(可以相同或不同)的情况下停止增大这两个队列深度阈值中的任一个。以这种方式,至少出于与上文结合图3b所描述的那些原因类似的原因,本文描述的示例可以使从scm读取缓存150进行读取优先于写入(例如,降级操作)。

再次参考图1和图2,如上所述,指令126可以接收对scm读取缓存150的io请求180,并且作为响应,确定io请求180的类型,并将scm读取缓存150未完成的io的当前数量136与scm读取缓存150的(多个)队列深度阈值138之一进行比较(在方法200的215处)。在这样的示例中,如上文所述,指令126还可以基于io请求180的类型和比较的结果,在以下操作之间进行选择:(1)使用scm读取缓存150来处理io请求180,(2)丢弃io请求180,以及(3)在不使用scm读取缓存150的情况下处理io请求180,并且执行所述选择(在方法200的220处)。在一些示例中,这些功能可以通过与上述后台进程(或线程)不同的io进程(或线程)来执行,其中,在一些示例中,所述io进程至少部分地由指令126(例如,参见图5的用于实施io进程的io指令524)来实施。例如,io进程可以是在计算设备100中实施对数据的自适应缓存的进程。

下文可以结合图1和图4来更详细地描述上文结合图2的215、220和225所描述的功能。参考图4,方法400可以在405处开始,在405,指令126可以接收对scm读取缓存150的io请求180。在410处,指令126可以确定io请求180是读取请求还是降级请求。响应于确定io请求180是对所请求数据的读取请求(410处为“读取”),在425处,指令126可以确定scm读取缓存150未完成的读取io的当前数量是否大于scm读取缓存150的读取队列深度阈值。如果是(425处为“是”),则在430处,指令126可以如上所述绕过scm读取缓存150并将所请求数据从后端存储装置160(或其他存储装置,诸如远程节点或阵列的(多个)存储设备)读取到主缓存140中(不从scm读取缓存150中读取所请求数据),并且然后前进到405以等待对scm读取缓存150的后续io请求。在其他示例中,在430处,在确定是否继续绕过scm读取缓存150并从其他存储装置(例如,后端存储装置160)读取所请求数据之前,可以进行进一步的(多个)确定,如下文更详细描述的。如果scm读取缓存150未完成的读取io的当前数量不大于读取队列深度阈值(425处为“否”),则在435处,指令126可以使用scm读取缓存150来执行io请求180,这在该示例中可以包括将(读取请求180的)所请求数据从scm读取缓存150中读取到主缓存140中。如上所述,对所请求数据的读取请求180可以由主机读取io(例如,来自与计算设备100分离的主机计算设备的对至少所请求数据的读取请求)触发。

返回到方法400的410,响应于确定io请求180是降级请求(410处为“降级”),在415处,指令126可以确定scm读取缓存150未完成的写入io的当前数量是否大于scm读取缓存150的写入队列深度阈值。如果是(415处为“是”),则在420处,指令126可以如上所述丢弃io请求180(即,降级请求180),跳过将数据从主缓存140降级到scm读取缓存150,并且然后前进到405以等待对scm读取缓存150的后续io请求。如果否(415处为“否”),则在435处,指令126可以使用scm读取缓存150来执行io请求180,这在该示例中可以包括将数据从主缓存140降级到scm读取缓存150(包括将与降级请求相关联的数据写入到scm读取缓存150),并且然后前进到405以等待对scm读取缓存150的后续io请求。如上所述,降级请求180可以是由计算设备100内部生成的用于将干净数据从主缓存140降级到scm读取缓存150中的请求。

在本文描述的示例中,指令126可以维护对scm读取缓存150的未完成io的数量136,所述数量实际上可以被表示为两个数量,包括对scm读取缓存150的未完成读取io的数量和对scm读取缓存150的未完成写入io的数量。在这样的示例中,指令126可以例如通过以下方式来维护这些数量:在每次向scm读取缓存150(例如,向实施scm读取缓存150的scm设备的设备驱动器)发出读取io请求时增大未完成读取io的数量,并且在每次完成了(例如,被报告为完成)对scm读取缓存150的读取io请求之一时减小该数量。类似地,指令126可以在每次向scm读取缓存150(例如,向实施scm读取缓存150的scm设备的设备驱动器)发出写入io请求时增大未完成写入io的数量,并且在每次完成了(例如,被报告为完成)对scm读取缓存150的写入io请求之一时减小该数量。在其他示例中,指令126可以以任何其他合适的方式确定这些数量。在本文描述的一些示例中,可以通过接收对scm读取缓存150的io请求180(例如,读取请求或降级请求)来触发(例如,响应于以上操作而执行)对scm读取缓存150的未完成读取或写入io的当前数量(例如,当前队列深度136)与相应的队列深度阈值的比较。在这样的示例中,可以由执行图4的方法的io进程或线程(例如,如上所述由图5的示例的io指令524(包括指令126)实施的进程或线程)来接收io请求180。

在其他示例中,io进程(或线程)本身(例如,指令126)可以周期性地确定主缓存140是否太满(例如,空闲空间太少),并且确定触发将(多个)页面(例如,最近最少使用的(多个)页面)从主缓存140降级到scm读取缓存150。在这样的示例中,响应于确定将数据从主缓存140降级到scm读取缓存150,指令126可以执行与上文参考方法400所描述的从410开始的过程类似的过程。例如,响应于确定对数据进行降级,指令126可以在scm读取缓存150未完成的写入io的当前数量不大于scm读取缓存150的写入队列深度阈值时将数据从主缓存140降级到scm读取缓存150(在435处)。在其他示例中,响应于确定对数据进行降级,指令126可以在scm读取缓存150未完成的写入io的当前数量大于scm读取缓存150的写入队列深度阈值时跳过将数据从主缓存140降级到scm读取缓存150(例如,类似于420)。

在一些示例中,即使当指令126确定scm读取缓存150的未完成读取的io的数量大于读取队列深度阈值时,从scm读取缓存150进行读取而不是从后端存储装置160或远程节点(例如,另一存储阵列)进行读取也可能更有利。在这样的示例中,这可能取决于与在绕过了scm读取缓存150的情况下将从其中读取所请求数据的(多个)存储设备有关的各种因素。例如,当所请求数据随后从包括scm读取缓存150的计算设备(例如,计算设备100)本地的(多个)存储设备(诸如后端存储装置160的(多个)存储设备)中读取并且这些(多个)存储设备具有相对较低的利用率时,绕过scm读取缓存150是可接受的。然而,例如,当将从远程节点(例如,远程计算设备,诸如另一存储阵列)或从具有相对较高利用率的(多个)存储设备读取所请求数据时,即使超过scm读取缓存150的读取队列深度阈值,继续从scm读取缓存150中读取所请求数据也可能更有利。如此,当超过读取队列深度阈值时,在决定是否针对读取请求180绕过scm读取缓存150之前,本文的一些示例可以进行进一步的确定。

例如,响应于对scm读取缓存150的读取请求180(例如,图4的410处的“读取”)并且确定scm读取缓存150未完成的读取io的当前数量大于scm读取缓存150的读取队列深度阈值(425处为“是”),在决定是(i)绕过scm读取缓存150并将所请求数据从除scm读取缓存150以外的存储装置读取到主缓存140中,还是(ii)不顾被超过的读取队列深度阈值继续将所请求数据从scm读取缓存150读取到主缓存140中之前,指令126可以进行进一步的确定(例如,在430处)。例如,在430处,指令126可以进行与在绕过了scm读取缓存150的情况下将从其中读取所请求数据的其他存储装置的位置以及该其他存储装置的当前利用率中的一个或多个有关的进一步的确定。例如,响应于读取请求180(例如,410处为“读取”)并且确定未完成读取io的当前数量136大于scm读取缓存150的读取队列深度阈值(425处为“是”),则在430处,指令126可以首先确定在绕过了scm读取缓存150的情况下将从哪个(哪些)其他存储设备读取所请求数据以及这些(多个)其他存储设备的位置。例如,如果指令126确定(多个)其他存储设备位于远离计算设备100的远程节点(例如,参见图5的“远程节点”,诸如计算设备、存储阵列等)中,则指令126可以决定不顾被超过的读取队列深度阈值从scm读取缓存150中读取所请求数据。在这样的示例中,从scm读取缓存150中读取数据(即使超过了读取队列深度阈值)、而不是从远程节点(例如,图5的io584)中读取所请求数据可能更有利,从远程节点中进行读取可能涉及与从计算设备100的后端存储装置160的任何本地存储设备中进行读取相比显著更高的延迟。在一些示例中,当指令126确定(多个)其他存储设备位于计算设备100本地(例如,在后端存储装置160中)时,则指令126可以进一步确定这些(多个)其他存储设备的当前利用率。在这样的示例中,如果这些(多个)存储设备中的一个或多个的当前利用率大于利用率阈值,则指令126可以决定不顾被超过的读取队列深度阈值继续从scm读取缓存150中读取所请求数据。在这样的示例中,继续从scm读取缓存150而不是(多个)其他存储设备中读取数据可能更有利,因为与在(多个)存储设备的利用率较低时相比,这些(多个)存储设备的相对较高的利用率可能导致显著更高的读取延迟。在其他示例中,当指令126确定(多个)其他存储设备位于计算设备100本地(例如,在后端存储装置160中)并且当前利用率低于阈值时,则在430处,指令126可以决定继续绕过scm读取缓存150,并从(例如,后端存储装置160的)(多个)其他存储设备中进行读取。在这样的示例中,(多个)其他存储设备的位置和利用率可以使得当超过了读取队列深度阈值时,从这些(多个)存储设备、而不是从scm读取缓存150中进行读取可能更有利。在这样的示例中,在超过读取队列深度阈值时,指令126可以基于与否则将从其中进行读取的(多个)特定存储设备有关的条件视情况智能地决定是否优选绕过scm读取缓存150。在这样的示例中,针对不同io和(多个)不同的后端存储设备的这种个性化确定可以使得总体上减少存储系统102(或502,其也可以执行这些功能)中的延迟。在本文描述的示例中,在框430处,指令126可以使用对(多个)存储设备的利用率的任何合适的度量。例如,存储设备(例如,诸如hdd、ssd等物理存储设备)的利用率可以基于在给定时间对所述存储设备的未完成io的数量。在这样的示例中,存储设备的最大利用率可以由可以允许在存储设备上同时未完成的io的最大数量来表示。在这样的示例中,存储设备的利用率可以表示为未完成io的实际数量,或表示为允许未完成的最大io的比例(例如50%、100%等)。在这样的示例中,利用率阈值可以设置为固定的io数量或最大利用率的阈值百分比(例如50%)。在其他示例中,可以使用利用率的其他度量。

图5是用于调整多个scm缓存中的每一个的队列深度阈值的示例系统502的框图。图5中所示的示例与图1中所示的示例不同,但为了便于说明,图5中所示的示例扩展了如上所述的图1中所图示的示例。然而,结合图5所描述的示例不被解释为限制上文结合图1所描述的示例。

在图5的示例中,存储系统502包括计算设备500,所述计算设备包括(如上文结合图1所描述的):至少一个处理资源110、包括指令121的机器可读存储介质120、包括主缓存140的存储器130、以及包括多个存储设备162、164、166、168等的后端存储装置160。如上所述,指令121可由至少一个处理资源110执行以实施本文中结合图1至图5所描述的功能。另外,图5的示例图示了后台指令522(包括如本文描述的指令122和124)以至少部分地实施如上文结合图2所描述的后台进程(或线程),并且图示了io指令524(包括如本文描述的指令126)以至少部分地实施如上文结合图4所描述的io进程(或线程)。在图5的示例中,计算设备500可以经由一个或多个计算机网络与远程主机计算设备进行通信(例如,以接收诸如读取io582等io请求,并返回io响应等),并且可以经由一个或多个计算机网络与包括一个或多个存储设备(例如,hdd、ssd等)的远程节点(例如,计算设备、存储阵列等)进行通信,计算设备500可以与所述远程节点交换数据(例如,经由io通信584等)。

在图5所示的示例中,计算设备100可以包括多个scm读取缓存150、550、650等。尽管在图5的示例中的计算设备500中图示了三个scm读取缓存,但是在示例中,计算设备500可以包括任何合适数量的一个或多个scm读取缓存(例如,三个以上,等)。在这样的示例中,指令121可执行来针对多个scm缓存中的每一个独立地执行指令522的后台进程(例如,如上文结合图2所描述的)。在这样的示例中,每个后台进程(即,针对每个scm读取缓存)用于针对每个scm缓存独立地维护至少一个队列深度阈值。例如,对于scm读取缓存150,相应的后台进程可以在存储器130中维护至少包括以下各项的数据152:(1)时间段内的总数据量132,(2)数据速率阈值134,(3)未完成读取io(或请求)的数量133(或读取队列深度133),(4)未完成写入io(或请求)的数量135(或写入队列深度135),(5)读取队列深度阈值137,以及(6)写入队列深度阈值139。相应后台进程可以在存储器130中维护每个scm读取缓存的相应数据。

例如,对于scm读取缓存550,相应的后台进程可以在存储器130中维护至少包括以下各项的数据552:(1)时间段内的总数据量532,(2)数据速率阈值534,(3)未完成读取io(或请求)的数量533(或读取队列深度533),(4)未完成写入io(或请求)的数量536(或写入队列深度536),(5)读取队列深度阈值537,以及(6)写入队列深度阈值539。在这样的示例中,对于scm读取缓存650,相应的后台进程可以在存储器130中维护至少包括以下各项的数据652:(1)时间段内的总数据量632,(2)数据速率阈值634,(3)未完成读取io(或请求)的数量633(或读取队列深度633),(4)未完成写入io(或请求)的数量635(或写入队列深度635),(5)读取队列深度阈值637,以及(6)写入队列深度阈值639。

在一些示例中,指令522可以针对多个scm读取缓存(或“scm缓存”)中的每一个周期性地执行用于scm缓存的后台进程(如上所述),其中,对后台进程的每次执行都在不同的时间段内执行。在这样的示例中,对后台进程的每次周期性执行可以包括:指令122确定在给定时间段内向和从相应的scm缓存传送的数据量;以及指令124响应于基于所确定的数据量而确定超过了scm读取缓存的数据速率阈值(如上所述)来调整相应scm缓存的至少一个队列深度阈值。

在图5的示例中,对于所述多个scm缓存中的每一个,指令524可以使用相应scm缓存的相应数据152、552、652来执行io进程(如上所述),所述相应数据包括相应scm缓存的一个或多个相应队列深度阈值(例如,scm读取缓存150的阈值137和139;scm读取缓存550的阈值537和539;以及scm读取缓存650的阈值637和639;等)。例如,对于每个scm缓存,执行io进程可以包括指令126响应于对相应scm缓存的降级请求并且确定相应scm缓存的写入队列深度大于scm缓存的相应写入队列深度阈值,来丢弃io请求而不将数据写入到scm缓存或后端存储装置160。执行io进程可以进一步包括指令126响应于对相应scm缓存的读取请求并且确定相应scm缓存的读取队列深度大于scm缓存的相应读取队列深度阈值,来绕过相应的scm缓存并且将所请求数据从其他存储装置(例如,后端存储装置160)读取到主缓存140(例如,在一些示例中需要进行进一步的确定,如本文其他地方所描述的)中。

在本文描述的示例中,计算设备500可以为任何给定的io请求确定适当的一个scm读取缓存(例如,经由基于地址的确定性过程等)。

在本文描述的示例中,短语“基于”不是排他的,并且不应被理解为“排他地基于”。相反,本文所使用的短语“基于”是包括性的,并且与替代性短语“至少基于”或“至少部分地基于”含义相同。如此,在本文中描述为“基于”特定条件、数据等的任何确定、决定、比较等可以被理解为意味着所述决定、比较等是至少基于(或至少部分地基于)该条件、数据等,并且还可以是基于其他(多个)条件、数据等。在本文所描述的示例中,被描述为由“指令”执行的功能可以被理解为那些指令当由处理资源执行时可以执行的功能。在其他示例中,结合指令所描述的功能可以由一个或多个引擎实施,所述引擎可以是用于实施(多个)引擎的功能的硬件和编程的任何组合。

如本文所使用的,“计算设备”可以是服务器、存储设备、存储阵列、台式计算机或膝上型计算机、交换机、路由器或包括处理资源的任何其他处理设备或装备。在本文所描述的示例中,处理资源可以包括例如包括在单个计算设备中或跨多个计算设备分布的一个处理器或多个处理器。如本文所使用的,“处理器”可以是中央处理单元(cpu)、基于半导体的微处理器、图形处理单元(gpu)、被配置用于检索和执行指令的现场可编程门阵列(fpga)、适用于检索和执行存储于机器可读存储介质上的指令的其他电子电路、或其组合中的至少一者。在本文所描述的示例中,处理资源可以取出、解码和执行存储在存储介质上的指令以执行结合存储在存储介质上的指令所描述的功能。在其他示例中,结合本文所描述的任何指令所描述的功能可以以电子电路的形式、在机器可读存储介质上编码的可执行指令的形式或其组合的形式实施。存储介质可以位于执行机器可读指令的计算设备中或远离计算设备但所述计算设备可访问(例如,经由计算机网络)以供执行。在图1和图5所示的示例中,存储介质120可以由一个机器可读存储介质或多个机器可读存储介质实施。

在本文所描述的示例中,存储阵列可以是计算设备,该计算设备包括多个存储设备和一个或多个控制器,所述控制器用于与主机设备交互并控制对存储设备的访问。在一些示例中,存储设备可以包括hdd、ssd或任何其他合适类型的存储设备或其任何组合。在一些示例中,(多个)控制器可以虚拟化由存储设备提供的存储容量以使主机能够访问由来自多个不同存储设备的存储空间构成的虚拟对象(例如,卷)。

在其他示例中,上文结合本文所描述的指令所描述的功能可以由一个或多个引擎实施,所述引擎可以是用于实施(多个)引擎的功能的硬件和编程的任何组合。在本文所描述的示例中,硬件和编程的这种组合可以以多种不同的方式实施。例如,用于引擎的编程可以是存储在至少一种非暂态机器可读存储介质上的处理器可执行指令,并且用于引擎的硬件可以包括用于执行这些指令的至少一个处理资源。在一些示例中,硬件还可以包括用于至少部分地实施(多个)引擎中的至少一个引擎的其他电子电路。在一些示例中,至少一个机器可读存储介质可以存储当由至少一个处理资源执行时至少部分地实施(多个)引擎中的一些或全部的指令。在一些示例中,计算设备可以包括存储指令的至少一个机器可读存储介质和用于执行指令的至少一个处理资源。在其他示例中,引擎可以由电子电路实施。

如本文所使用的,“机器可读存储介质”可以是用于包含或存储如可执行指令、数据等信息的任何电子、磁性、光学或其他物理存储装置。例如,本文所描述的任何机器可读存储介质可以是ram、eeprom、易失性存储器、非易失性存储器、闪存、存储驱动器(例如,hdd、ssd)、任何类型的存储盘(例如,压缩盘、dvd等)等中的任何一者,或其组合。进一步地,本文所描述的任何机器可读存储介质可以是非暂态的。在本文所描述的示例中,一个或多个机器可读存储介质可以是物品(或制品)的一部分。物品或制品可以指代任何制造的单个部件或多个部件。在一些示例中,指令可以是安装包的一部分,所述安装包在被安装时可以由处理资源执行以实施本文描述的功能。本说明书(包括任何所附权利要求、摘要和附图)中所公开的全部特征、和/或如此公开的任何方法或过程的所有元素都可以组合为除了至少一些这种特征和/或元素相互排他的组合之外的任何组合。例如,可以结合本文结合图1至图5中的任何图所描述的功能提供本文结合图1至图5中的任何其他图所描述的功能。


技术特征:

1.一种用于存储系统的方法,所述方法包括:

利用所述存储系统的至少一个处理资源来确定在给定的时间段内从储存级存储器(scm)读取缓存中读取和写入到所述储存级存储器读取缓存的总数据量,所述存储系统包括主缓存、所述储存级存储器读取缓存和后端存储装置;

响应于基于所确定的总数据量而确定超过了所述储存级存储器读取缓存的数据速率阈值,来调整所述储存级存储器读取缓存的至少一个队列深度阈值;

响应于对所述储存级存储器读取缓存的输入/输出(io)请求,将所述储存级存储器读取缓存的队列深度与所述储存级存储器读取缓存的所述至少一个队列深度阈值之一进行比较;

基于所述输入/输出请求的类型和所述比较的结果,在以下操作之间进行选择:使用所述储存级存储器读取缓存来处理所述输入/输出请求、丢弃所述输入/输出请求、以及在不使用所述储存级存储器读取缓存的情况下处理所述输入/输出请求;以及

响应于所述选择来执行所选的处理或丢弃操作。

2.如权利要求1所述的方法,其中:

所述选择包括基于确定所述输入/输出请求是将数据降级到所述储存级存储器读取缓存的请求、并且所述比较结果指示所述储存级存储器读取缓存未完成的写入输入/输出的当前数量大于所述储存级存储器读取缓存的所述至少一个队列深度阈值中的写入队列深度阈值,来选择丢弃所述输入/输出请求,其中,所述队列深度为写入队列深度;并且

所述执行包括响应于所述选择来丢弃所述输入/输出请求而不将所述数据写入所述储存级存储器读取缓存或所述后端存储装置。

3.如权利要求1所述的方法,其中:

所述选择包括基于确定所述输入/输出请求是对所请求数据的读取请求、并且所述比较结果指示所述储存级存储器读取缓存未完成的读取输入/输出的当前数量大于所述储存级存储器读取缓存的所述至少一个队列深度阈值中的读取队列深度阈值,来选择在不使用所述储存级存储器读取缓存的情况下处理所述输入/输出请求,其中,所述队列深度为读取队列深度;并且

所述执行包括绕过所述储存级存储器读取缓存并将所请求数据从所述存储系统的后端存储装置或远程计算设备中的至少一个读取到所述存储系统的所述主缓存中。

4.如权利要求1所述的方法,其中:

所述选择包括基于以下任一项来选择使用所述储存级存储器读取缓存处理所述输入/输出请求:

(i)确定所述输入/输出请求是将数据降级到所述储存级存储器读取缓存的请求,并且所述比较结果指示所述储存级存储器读取缓存未完成的写入输入/输出的当前数量不大于所述储存级存储器读取缓存的所述至少一个队列深度阈值中的写入队列深度阈值;或者

(ii)确定所述输入/输出请求是从所述储存级存储器读取缓存中读取所请求数据的读取请求,并且所述比较结果指示所述储存级存储器读取缓存未完成的读取输入/输出的当前数量不大于所述储存级存储器读取缓存的所述至少一个队列深度阈值中的读取队列深度阈值;并且

所述执行包括基于所述选择来进行以下任一操作:

将所述数据降级到所述储存级存储器读取缓存;或者

从所述储存级存储器读取缓存中读取所请求数据。

5.一种机器可读存储介质,所述机器可读存储介质包括指令,所述指令能够由存储系统的至少一个处理资源执行以进行以下操作:

确定在给定的时间段内从储存级存储器(scm)读取缓存中读取和写入到所述储存级存储器读取缓存的总数据量;

响应于基于所确定的总数据量而确定超过了所述储存级存储器读取缓存的数据速率阈值,来调整所述储存级存储器读取缓存的读取队列深度阈值和写入队列深度阈值中的至少一个;

响应于对所述储存级存储器读取缓存的输入/输出(io)请求:

当处理所述输入/输出请求包括写入所述储存级存储器读取缓存、并且所述储存级存储器读取缓存未完成的写入输入/输出的当前数量大于所述写入队列深度阈值时,丢弃所述输入/输出请求;以及

当所述输入/输出请求是对所请求数据的读取请求、并且所述储存级存储器读取缓存未完成的读取输入/输出的当前数量大于所述读取队列深度阈值时,绕过所述储存级存储器读取缓存并将所请求数据从所述存储系统的后端存储装置或从远程计算设备读取到所述存储系统的主缓存中。

6.如权利要求5所述的机器可读存储介质,其中,用于绕过所述储存级存储器读取缓存并将所请求数据从所述后端存储装置读取到所述主缓存中的所述指令能够执行来将所请求数据从所述后端存储装置读取到所述主缓存中、而不从所述储存级存储器读取缓存中读取所请求数据。

7.如权利要求5所述的机器可读存储介质,其中:

用于调整所述读取队列深度阈值和所述写入队列深度阈值中的至少一个的所述指令包括:能够由所述至少一个处理资源执行以响应于基于所确定的总数据量而确定超过了所述储存级存储器读取缓存的所述数据速率阈值,来调整所述读取队列深度阈值或所述写入队列深度阈值的指令。

8.如权利要求7所述的机器可读存储介质,其中,所述指令能够由所述至少一个处理资源执行,以响应于对所述储存级存储器读取缓存的所述输入/输出请求进行以下操作:

当处理所述输入/输出请求包括写入所述储存级存储器读取缓存时:

将所述储存级存储器读取缓存未完成的写入输入/输出的当前数量与所述写入队列深度阈值进行比较;并且

响应于确定所述储存级存储器读取缓存未完成的写入输入/输出的当前数量大于所述写入队列深度阈值,丢弃所述输入/输出请求;以及

当所述输入/输出请求是对所请求数据的读取请求时:

将所述储存级存储器读取缓存未完成的读取输入/输出的当前数量与所述读取队列深度阈值进行比较;并且

响应于确定所述储存级存储器读取缓存未完成的读取输入/输出的当前数量大于所述读取队列深度阈值,绕过所述储存级存储器读取缓存并将所请求数据从所述后端存储装置读取到所述主缓存中。

9.如权利要求8所述的机器可读存储介质,其中,所述指令能够由所述至少一个处理资源执行以进行以下操作:

响应于基于所确定的总数据量而确定超过了所述储存级存储器读取缓存的所述数据速率阈值来执行以下操作之一:

当所述写入队列深度阈值高于第一最小值时,减小所述写入队列深度阈值;

当所述写入队列深度阈值为所述第一最小值并且所述读取队列深度阈值高于第二最小值时,减小所述读取队列深度阈值;以及

当所述写入队列深度阈值为所述第一最小值并且所述读取队列深度阈值为所述第二最小值时,既不减小所述写入队列深度阈值,也不减小所述读取队列深度阈值。

10.如权利要求8所述的机器可读存储介质,其中,所述指令能够由所述至少一个处理资源执行以进行以下操作:

响应于基于所确定的总数据量而确定未超过所述储存级存储器读取缓存的所述数据速率阈值来执行以下操作之一:

当所述读取队列深度阈值低于第一最大值时,增大所述读取队列深度阈值;

当所述读取队列深度阈值为所述第一最大值并且所述写入队列深度阈值低于第二最大值时,增大所述写入队列深度阈值;以及

当所述读取队列深度阈值为所述第一最大值并且所述写入队列深度阈值为所述第二最大值时,既不增大所述读取队列深度阈值,也不增大所述写入队列深度阈值。

11.如权利要求5所述的机器可读存储介质,其中:

所述输入/输出请求是读取请求或降级请求,其中,所述降级请求是将干净数据从所述主缓存降级到所述储存级存储器读取缓存的请求;并且

其中,所述储存级存储器读取缓存未完成的所述写入输入/输出每个在实施降级请求。

12.如权利要求11所述的机器可读存储介质,其中,所述指令能够由所述至少一个处理资源执行以进行以下操作:

响应于确定将数据从所述主缓存降级到所述储存级存储器读取缓存,进行以下操作之一:

当所述储存级存储器读取缓存未完成的写入输入/输出的当前数量不大于所述储存级存储器读取缓存的所述至少一个队列深度阈值中的写入队列深度阈值时,将所述数据从所述主缓存降级到所述储存级存储器读取缓存;以及

当所述储存级存储器读取缓存未完成的写入输入/输出的当前数量大于所述写入队列深度阈值时,跳过将所述数据从所述主缓存降级到所述储存级存储器读取缓存。

13.如权利要求5所述的机器可读存储介质,其中,所述指令能够由所述至少一个处理资源执行,以便在包括所述给定时间段的多个连续时间段中的每个时间段内进行以下操作:

累计在所述时间段期间从所述储存级存储器读取缓存中读取和写入到所述储存级存储器读取缓存的所述总数据量;

当所述时间段已经过去时,基于在所述时间段所累计的总数据量来确定在所述时间段内是否超过了所述储存级存储器读取缓存的所述数据速率阈值;

响应于确定在所述时间段内超过了所述储存级存储器读取缓存的所述数据速率阈值,来调整所述储存级存储器读取缓存的所述读取队列深度阈值和所述写入队列深度阈值中的至少一个;以及

将所述总数据量重置为零,以便为下一个时间段做准备。

14.如权利要求5所述的机器可读存储介质,其中,所述指令能够由所述至少一个处理资源执行以进行以下操作:

响应于所述输入/输出请求并且确定所述储存级存储器读取缓存未完成的输入/输出的当前数量不大于所述储存级存储器读取缓存的所述至少一个队列深度阈值中的任何一个:

当处理所述输入/输出请求包括写入所述储存级存储器读取缓存、并且所述储存级存储器读取缓存未完成的写入输入/输出的当前数量不大于所述写入队列深度阈值时,将与所述输入/输出请求相关联的数据写入所述储存级存储器读取缓存;并且

当所述输入/输出请求是所述读取请求、并且所述储存级存储器读取缓存未完成的读取输入/输出的当前数量不大于所述读取队列深度阈值时,从所述储存级存储器读取缓存中读取所请求数据。

15.如权利要求5所述的机器可读存储介质,其中,所述输入/输出请求是所述读取请求,并且所述指令能够由所述至少一个处理资源执行以进行以下操作:

响应于对所述储存级存储器读取缓存的所述读取请求、并且确定所述储存级存储器读取缓存未完成的读取输入/输出的当前数量大于所述读取队列深度阈值:

至少部分地基于对所述后端存储装置的至少一个存储设备的利用率的度量、以及所述至少一个存储设备中的每个存储设备的位置中的至少一项,来确定是(i)绕过所述储存级存储器读取缓存并将所请求数据从所述后端存储装置读取到所述主缓存中,还是(ii)无论是否超过所述读取队列深度阈值都将所请求数据从所述储存级存储器读取缓存读取到所述主缓存中。

16.一种存储系统,包括:

至少一个处理资源;

主缓存;

储存级存储器(scm)读取缓存;

后端存储装置;以及

至少一个非暂态机器可读存储介质,所述至少一个非暂态机器可读存储介质包括指令,所述指令能够由所述至少一个处理资源执行以进行以下操作:

执行后台进程,所述后台进程包括:

确定在给定时间段内传送到所述储存级存储器读取缓存和从所述储存级存储器读取缓存传送的数据量;以及

响应于基于所确定的数据量而确定超过了所述储存级存储器读取缓存的数据速率阈值来调整所述储存级存储器读取缓存的读取队列深度阈值和写入队列深度阈值中的至少一个;以及

执行输入/输出(io)进程,所述输入/输出进程包括:

响应于对所述储存级存储器读取缓存的输入/输出请求:

当所述输入/输出请求是将数据降级到所述储存级存储器读取缓存的请求、并且所述储存级存储器读取缓存的写入队列深度大于所述写入队列深度阈值时,丢弃所述输入/输出请求而不将所述数据写入所述储存级存储器读取缓存;并且

当所述输入/输出请求是对所请求数据的读取请求、并且所述储存级存储器读取缓存的读取队列深度大于所述读取队列深度阈值时,绕过所述储存级存储器读取缓存并将所请求数据从所述后端存储装置或从远程计算设备读取到所述主缓存中。

17.如权利要求16所述的存储系统,进一步包括:

多个储存级存储器缓存,所述多个储存级存储器缓存包括所述储存级存储器读取缓存;

其中,所述指令能够由所述至少一个处理资源执行以进行以下操作:

针对所述多个储存级存储器缓存中的每一个独立地执行所述后台进程,其中,所述后台进程用于针对所述储存级存储器缓存中的每一个独立地维护至少一个队列深度阈值;以及

针对所述多个储存级存储器缓存中的每一个,使用所述储存级存储器缓存的相应至少一个队列深度阈值来执行所述输入/输出进程。

18.如权利要求17所述的存储系统,其中,用于执行所述后台进程的所述指令包括能够由所述至少一个处理资源执行以进行以下操作的指令:

针对所述多个储存级存储器缓存中的每一个:

针对所述储存级存储器缓存周期性地执行所述后台进程,其中,对所述后台进程的每次执行都在不同的时间段执行。

19.如权利要求16所述的存储系统,其中:

所述储存级存储器读取缓存包括由具有比串行附接scsi(sas)固态驱动器(ssd)更低的延迟的储存级存储器设备实施的读取缓存,其中,串行附接scsi固态驱动器是一种非易失性存储器(nvm);并且

所述储存级存储器设备用于使用与nvmexpresstm(nvmetm)一致的协议来与所述存储系统的其他部件进行通信。

20.如权利要求19所述的存储系统,其中:

所述主缓存包括易失性存储器;并且

所述后端存储装置包括一个或多个非易失性存储设备,其中,所述非易失性存储设备包括硬盘驱动器(hdd)或固态驱动器(ssd)中的至少一者或者硬盘驱动器与固态驱动器的组合。

技术总结
本公开涉及基于队列深度阈值绕过储存级存储器读取缓存。示例可以基于队列深度阈值来绕过储存级存储器(SCM)读取缓存。示例可以基于在某个时间段内从SCM读取缓存中读取和写入到所述SCM读取缓存的数据量来调整所述SCM读取缓存的队列深度阈值,并且可以基于所述SCM读取缓存未完成的IO数量与所述SCM读取缓存的队列深度阈值的比较来绕过所述SCM读取缓存。

技术研发人员:G·谢尔吉尔;山田高荣
受保护的技术使用者:慧与发展有限责任合伙企业
技术研发日:2019.11.29
技术公布日:2020.06.09

转载请注明原文地址: https://bbs.8miu.com/read-30296.html

最新回复(0)