关于java:Redis-问题排查解决手册值得收藏

32次阅读

共计 10478 个字符,预计需要花费 27 分钟才能阅读完成。

性能相干的数据指标

通过 Redis-cli 命令行界面拜访到 Redis 服务器,而后应用 info 命令获取所有与 Redis 服务相干的信息。通过这些信息来剖析文章前面提到的一些性能指标。

info 命令输入的数据可分为 10 个类别,别离是:

  • server
  • clients
  • memory
  • persistence
  • stats
  • replication
  • cpu
  • commandstats
  • cluster
  • keyspace

这篇次要介绍比拟重要的 2 局部性能指标 memory 和 stats。

须要留神的是 info 命令返回的信息,并没有命令响应提早相干的数据信息,所以前面会具体介绍怎么获取与提早相干的数据指标。

假使你感觉 info 输入的信息太多并且横七竖八,能够指定 info 命令的参数来获取单个分类下的数据。比方输出 info memory 命令,会只返回与内存相干的数据。

为了疾速定位并解决性能问题,这里抉择 5 个关键性的数据指标,它蕴含了大多数人在应用 Redis 上会常常碰到的性能问题。

内存使用率 used_memory

上图中 used_memory 字段数据表示的是:由 Redis 分配器调配的内存总量,以字节(byte)为单位。其中 used_memory_human 上的数据和 used_memory 是一样的值,它以 M 为单位显示,仅为了不便浏览。

used_memory 是 Redis 应用的内存总量,它蕴含了理论缓存占用的内存和 Redis 本身运行所占用的内存(如元数据、lua)。它是由 Redis 应用内存分配器调配的内存,所以这个数据并没有把内存碎片节约掉的内存给统计进去。

其余字段代表的含意,都以字节为单位:

  • used_memory_rss:从操作系统上显示曾经调配的内存总量。
  • mem_fragmentation_ratio:内存碎片率。
  • used_memory_lua:Lua 脚本引擎所应用的内存大小。
  • mem_allocator:在编译时指定的 Redis 应用的内存分配器,能够是 libc、jemalloc、tcmalloc。

因内存替换引起的性能问题

内存使用率是 Redis 服务最要害的一部分。如果一个 Redis 实例的内存使用率超过可用最大内存 (used_memory > 可用最大内存),那么操作系统开始进行内存与 swap 空间替换,把内存中旧的或不再应用的内容写入硬盘上(硬盘上的这块空间叫 Swap 分区),以便腾出新的物理内存给新页或流动页 (page) 应用。

在硬盘上进行读写操作要比在内存上进行读写操作,工夫上慢了近 5 个数量级,内存是 0.1μs 单位、而硬盘是 10ms。如果 Redis 过程上产生内存替换,那么 Redis 和依赖 Redis 上数据的利用会受到重大的性能影响。

通过查看 used_memory 指标可晓得 Redis 正在应用的内存状况,如果 used_memory> 可用最大内存,那就阐明 Redis 实例正在进行内存替换或者曾经内存替换结束。管理员依据这个状况,执行绝对应的应急措施。

跟踪内存使用率

若是在应用 Redis 期间没有开启 rdb 快照或 aof 长久化策略,那么缓存数据在 Redis 解体时就有失落的危险。因为当 Redis 内存使用率超过可用内存的 95% 时,局部数据开始在内存与 swap 空间来回替换,这时就可能有失落数据的危险。

当开启并触发快照性能时,Redis 会 fork 一个子过程把以后内存中的数据齐全复制一份写入到硬盘上。因而若是以后应用内存超过可用内存的 45% 时触发快照性能,那么此时进行的内存替换会变的十分危险(可能会失落数据)。假使在这个时候实例上有大量频繁的更新操作,问题会变得更加重大。

通过缩小 Redis 的内存占用率,来防止这样的问题,或者应用上面的技巧来防止内存替换产生:

1)如果缓存数据小于 4GB,就应用 32 位的 Redis 实例。因为 32 位实例上的指针大小只有 64 位的一半,它的内存空间占用空间会更少些。这有一个害处就是,假如物理内存超过 4GB,那么 32 位实例能应用的内存依然会被限度在 4GB 以下。要是实例同时也共享给其余一些利用应用的话,那可能须要更高效的 64 位 Redis 实例,这种状况下切换到 32 位是不可取的。不论应用哪种形式,Redis 的 dump 文件在 32 位和 64 位之间是相互兼容的,因而假使有缩小占用内存空间的需要,能够尝试先应用 32 位,前面再切换到 64 位上。

2)尽可能的应用 Hash 数据结构。因为 Redis 在贮存小于 100 个字段的 Hash 构造上,其存储效率是十分高的。所以在不须要汇合 (set) 操作或 list 的 push/pop 操作的时候,尽可能的应用 Hash 构造。比方,在一个 web 应用程序中,须要存储一个对象示意用户信息,应用单个 key 示意一个用户,其每个属性存储在 Hash 的字段里,这样要比给每个属性独自设置一个 key-value 要高效的多。通常状况下假使有数据应用 string 构造,用多个 key 存储时,那么应该转换成单 key 多字段的 Hash 构造。如上述例子中介绍的 Hash 构造应蕴含,单个对象的属性或者单个用户各种各样的材料。Hash 构造的操作命令是 HSET(key, fields, value)和 HGET(key, field),应用它能够存储或从 Hash 中取出指定的字段。

3)设置 key 的过期工夫。一个缩小内存使用率的简略办法就是,每当存储对象时确保设置 key 的过期工夫。假使 key 在明确的工夫周期内应用或者旧 key 不大可能被应用时,就能够用 Redis 过期工夫命令 (expire,expireat, pexpire, pexpireat) 去设置过期工夫,这样 Redis 会在 key 过期时主动删除 key。如果你晓得每秒钟有多少个新 key-value 被创立,那能够调整 key 的存活工夫,并指定阀值去限度 Redis 应用的最大内存。

4)回收 key。在 Redis 配置文件中(个别叫 Redis.conf),通过设置“maxmemory”属性的值能够限度 Redis 最大应用的内存,批改后重启实例失效。也能够应用客户端命令 config set maxmemory 去批改值,这个命令是立刻失效的,但会在重启后会生效,须要应用 config rewrite 命令去刷新配置文件。若是启用了 Redis 快照性能,应该设置“maxmemory”值为零碎可应用内存的 45%,因为快照时须要一倍的内存来复制整个数据集,也就是说如果以后已应用 45%,在快照期间会变成 95%(45%+45%+5%),其中 5% 是预留给其余的开销。如果没开启快照性能,maxmemory 最高能设置为零碎可用内存的 95%。

当内存应用达到设置的最大阀值时,须要抉择一种 key 的回收策略,可在 Redis.conf 配置文件中批改“maxmemory-policy”属性值。若是 Redis 数据集中的 key 都设置了过期工夫,那么“volatile-ttl”策略是比拟好的抉择。但如果 key 在达到最大内存限度时没可能迅速过期,或者基本没有设置过期工夫。那么设置为“allkeys-lru”值比拟适合,它容许 Redis 从整个数据集中筛选最近起码应用的 key 进行删除(LRU 淘汰算法)。Redis 还提供了一些其余淘汰策略,如下:

  • volatile-lru:应用 LRU 算法从已设置过期工夫的数据汇合中淘汰数据。
  • volatile-ttl:从已设置过期工夫的数据汇合中筛选行将过期的数据淘汰。
  • volatile-random:从已设置过期工夫的数据汇合中随机筛选数据淘汰。
  • allkeys-lru:应用 LRU 算法从所有数据汇合中淘汰数据。
  • allkeys-random:从数据汇合中任意抉择数据淘汰
  • no-enviction:禁止淘汰数据。

通过设置 maxmemory 为零碎可用内存的 45% 或 95%(取决于长久化策略)和设置“maxmemory-policy”为“volatile-ttl”或“allkeys-lru”(取决于过期设置),能够比拟精确的限度 Redis 最大内存使用率,在绝大多数场景下应用这 2 种形式可确保 Redis 不会进行内存替换。假使你放心因为限度了内存使用率导致失落数据的话,能够设置 noneviction 值禁止淘汰数据。

命令解决数 total_commands_processed

在 info 信息里的 total_commands_processed 字段显示了 Redis 服务解决命令的总数,其命令都是从一个或多个 Redis 客户端申请过去的。Redis 每时每刻都在解决从客户端申请过去的命令,它能够是 Redis 提供的 140 种命令的任意一个。total_commands_processed 字段的值是递增的,比方 Redis 服务别离解决了 client_x 申请过去的 2 个命令和 client_y 申请过去的 3 个命令,那么命令解决总数 (total_commands_processed) 就会加上 5。

剖析命令解决总数,诊断响应提早。

在 Redis 实例中,跟踪命令解决总数是解决响应提早问题最要害的局部,因为 Redis 是个单线程模型,客户端过去的命令是依照程序执行的。比拟常见的提早是带宽,通过千兆网卡的提早大概有 200μs。假使显著看到命令的响应工夫变慢,提早高于 200μs,那可能是 Redis 命令队列里期待解决的命令数量比拟多。

如上所述,延迟时间减少导致响应工夫变慢可能是因为一个或多个慢命令引起的,这时能够看到每秒命令解决数在显著降落,甚至于前面的命令齐全被阻塞,导致 Redis 性能升高。要剖析解决这个性能问题,须要跟踪命令解决数的数量和延迟时间。

比方能够写个脚本,定期记录 total_commands_processed 的值。当客户端显著发现响应工夫过慢时,能够通过记录的 total_commands_processed 历史数据值来判断命理解决总数是回升趋势还是降落趋势,以便排查问题。

应用命令解决总数解决延迟时间减少。

通过与记录的历史数据比拟得悉,命令解决总数的确是处于回升或降落状态,那么可能是有 2 个起因引起的:

  • 命令队列里的命令数量过多,前面命令始终在期待中。
  • 几个慢命令阻塞 Redis。

上面有三个方法能够解决,因下面 2 条起因引起的响应提早问题。

1)应用多参数命令:若是客户端在很短的工夫内发送大量的命令过去,会发现响应工夫显著变慢,这因为前面命令始终在期待队列中后面大量命令执行结束。有个办法能够改善提早问题,就是通过单命令多参数的模式取代多命令单参数的模式。举例来说,循环应用 LSET 命令去增加 1000 个元素到 list 构造中,是性能比拟差的一种形式,更好的做法是在客户端创立一个 1000 元素的列表,用单个命令 LPUSH 或 RPUSH,通过多参数结构模式一次性把 1000 个元素发送的 Redis 服务上。上面的表格是 Redis 的一些操作命令,有单个参数命令和反对多个参数的命令,通过这些命令可尽量减少应用多命令的次数。

2)管道命令:另一个缩小多命令的办法是应用管道(pipeline),把几个命令合并一起执行,从而缩小因网络开销引起的提早问题。因为 10 个命令独自发送到服务端会引起 10 次网络提早开销,应用管道会一次性把执行后果返回,仅须要一次网络提早开销。Redis 自身反对管道命令,大多数客户端也反对,假使以后实例提早很显著,那么应用管道去升高提早是十分无效的。

3)防止操作大汇合的慢命令:如果命令解决频率过低导致延迟时间减少,这可能是因为应用了高工夫复杂度的命令操作导致,这意味着每个命令从汇合中获取数据的工夫增大。所以缩小应用高工夫简单的命令,能显著的进步的 Redis 的性能。上面的表格是高工夫复杂度命令的列表,其详细描述了命令的属性,有这助于高效正当的、最优化的应用这些命令(如果不得不应用的话),以进步 Redis 性能。

延迟时间

Redis 的提早数据是无奈从 info 信息中获取的。假使想要查看延迟时间,能够用 Redis-cli 工具加 –latency 参数运行,如:

Redis-cli --latency -h 127.0.0.1 -p 6379

其 host 和 port 是 Redis 实例的 ip 及端口。因为以后服务器不同的运行状况,延迟时间可能有所误差,通常 1G 网卡的延迟时间是 200μs。

以毫秒为单位测量 Redis 的响应延迟时间,楼主本机的提早是 300μs:

跟踪 Redis 提早性能

Redis 之所以这么风行的次要起因之一就是低提早个性带来的高性能,所以说解决提早问题是进步 Redis 性能最间接的方法。拿 1G 带宽来说,若是延迟时间远高于 200μs,那显著是呈现了性能问题。尽管在服务器上会有一些慢的 IO 操作,但 Redis 是单核承受所有客户端的申请,所有申请是按良好的程序排队执行。因而若是一个客户端发过来的命令是个慢操作,那么其余所有申请必须期待它实现后能力继续执行。

应用提早命令进步性能

一旦确定延迟时间是个性能问题后,这里有几个方法能够用来剖析解决性能问题。

1. 应用 slowlog 查出引发提早的慢命令:

Redis 中的 slowlog 命令能够让咱们疾速定位到那些超出指定执行工夫的慢命令,默认状况下命令若是执行工夫超过 10ms 就会被记录到日志。slowlog 只会记录其命令执行的工夫,不蕴含 io 往返操作,也不记录单由网络提早引起的响应慢。

通常 1gb 带宽的网络提早,预期在 200μs 左右,假使一个命令仅执行工夫就超过 10ms,那比网络提早慢了近 50 倍。想要查看所有执行工夫比较慢的命令,能够通过应用 Redis-cli 工具,输出 slowlog get 命令查看,返回后果的第三个字段以奥妙位单位显示命令的执行工夫。如果只须要查看最初 10 个慢命令,输出 slowlog get 10 即可。对于怎么定位到是由慢命令引起的提早问题,可查看 total_commands_processed 介绍章节。

图中字段别离意思是:

  • 1= 日志的惟一标识符
  • 2= 被记录命令的执行工夫点,以 UNIX 工夫戳格局示意
  • 3= 查问执行工夫,以微秒为单位。例子中命令应用 54 毫秒。
  • 4= 执行的命令,以数组的模式排列。残缺命令是 config get *。

假使你想自定义慢命令的规范,能够调整触发日志记录慢命令的阀值。若是很少或没有命令超过 10ms,想升高记录的阀值,比方 5 毫秒,可在 Redis-cli 工具中输出上面的命令配置:

config set slowlog-log-slower-than 5000

也能够在 Redis.config 配置文件中设置,以奥妙位单位。

2. 监控客户端的连贯:

因为 Redis 是单线程模型(只能应用单核),来解决所有客户端的申请,但因为客户端连接数的增长,解决申请的线程资源开始升高调配给单个客户端连贯的解决工夫,这时每个客户端须要破费更多的工夫去期待 Redis 共享服务的响应。

这种状况下监控客户端连接数是十分重要的,因为客户端创立连接数的数量可能超出预期的数量,也可能是客户端端没有无效的开释连贯。在 Redis-cli 工具中输出 info clients 能够查看到以后实例的所有客户端连贯信息。如下图,第一个字段 (connected_clients) 显示以后实例客户端连贯的总数:

Redis 默认容许客户端连贯的最大数量是 10000。若是看到连接数超过 5000 以上,那可能会影响 Redis 的性能。假使一些或大部分客户端发送大量的命令过去,这个数字会低的多。

3. 限度客户端连接数:

自 Redis2.6 当前,容许使用者在配置文件(Redis.conf)maxclients 属性上批改客户端连贯的最大数,也能够通过在 Redis-cli 工具上输出 config set maxclients 去设置最大连接数。依据连接数负载的状况,这个数字应该设置为预期连接数峰值的 110% 到 150 之间,若是连接数超出这个数字后,Redis 会回绝并立即敞开新来的连贯。

通过设置最大连接数来限度非预期数量的连接数增长,是十分重要的。另外,新连贯尝试失败会返回一个谬误音讯,这能够让客户端晓得,Redis 此时有非预期数量的连接数,以便执行对应的解决措施。上述二种做法对管制连接数的数量和持续保持 Redis 的性能最优是十分重要的,

4. 增强内存治理:

较少的内存会引起 Redis 延迟时间减少。如果 Redis 占用内存超出零碎可用内存,操作系统会把 Redis 过程的一部分数据,从物理内存替换到硬盘上,内存替换会显著的减少延迟时间。对于怎么监控和缩小内存应用,可查看 used_memory 介绍章节。

5. 性能数据指标:

剖析解决 Redis 性能问题,通常须要把延迟时间的数据变动与其余性能指标的变动相关联起来。命令解决总数降落的产生可能是由慢命令阻塞了整个零碎,但如果命令解决总数的减少,同时内存使用率也减少,那么就可能是因为内存替换引起的性能问题。

对于这种性能指标相关联的剖析,须要从历史数据上来察看到数据指标的重要变动,此外还能够察看到单个性能指标相关联的所有其余性能指标信息。这些数据能够在 Redis 上收集,周期性的调用内容为 Redis info 的脚本,而后剖析输入的信息,记录到日志文件中。当提早发生变化时,用日志文件配合其余数据指标,把数据串联起来排查定位问题。

内存碎片率

info 信息中的 mem_fragmentation_ratio 给出了内存碎片率的数据指标,它是由操零碎调配的内存除以 Redis 调配的内存得出:

used_memory 和 used_memory_rss 数字都蕴含的内存调配有:

  • 用户定义的数据:内存被用来存储 key-value 值。
  • 外部开销:存储外部 Redis 信息用来示意不同的数据类型。

used_memory_rss 的 rss 是 Resident Set Size 的缩写,示意该过程所占物理内存的大小,是操作系统调配给 Redis 实例的内存大小。除了用户定义的数据和外部开销以外,used_memory_rss 指标还蕴含了内存碎片的开销,内存碎片是由操作系统低效的调配 / 回收物理内存导致的。

操作系统负责调配物理内存给各个利用过程,Redis 应用的内存与物理内存的映射是由操作系统上虚拟内存治理分配器实现的。

举个例子来说,Redis 须要调配间断内存块来存储 1G 的数据集,这样的话更无利,但可能物理内存上没有超过 1G 的间断内存块,那操作系统就不得不应用多个不间断的小内存块来调配并存储这 1G 数据,也就导致内存碎片的产生。

内存分配器另一个简单的层面是,它常常会事后调配一些内存块给援用,这样做会使放慢应用程序的运行。

了解资源性能

跟踪内存碎片率对了解 Redis 实例的资源性能是十分重要的。内存碎片率稍大于 1 是正当的,这个值示意内存碎片率比拟低,也阐明 redis 没有产生内存替换。但如果内存碎片率超过 1.5,那就阐明 Redis 耗费了理论须要物理内存的 150%,其中 50% 是内存碎片率。若是内存碎片率低于 1 的话,阐明 Redis 内存调配超出了物理内存,操作系统正在进行内存替换。内存替换会引起非常明显的响应提早,可查看 used_memory 介绍章节。

上图中的 0.99 即 99%。

用内存碎片率预测性能问题

假使内存碎片率超过了 1.5,那可能是操作系统或 Redis 实例中内存治理变差的体现。上面有 3 种办法解决内存治理变差的问题,并进步 Redis 性能:

1. 重启 Redis 服务器:

如果内存碎片率超过 1.5,重启 Redis 服务器能够让额定产生的内存碎片生效并从新作为新内存来应用,使操作系统复原高效的内存治理。额定碎片的产生是因为 Redis 开释了内存块,但内存分配器并没有返回内存给操作系统,这个内存分配器是在编译时指定的,能够是 libc、jemalloc 或者 tcmalloc。通过比拟 used_memory_peak, used_memory_rss 和 used_memory_metrics 的数据指标值能够查看额定内存碎片的占用。

从名字上能够看出,used_memory_peak 是过来 Redis 内存应用的峰值,而不是以后应用内存的值。如果 used_memory_peak 和 used_memory_rss 的值大抵上相等,而且二者显著超过了 used_memory 值,这阐明额定的内存碎片正在产生。在 Redis-cli 工具上输出 info memory 能够查看下面三个指标的信息:

在重启服务器之前,须要在 Redis-cli 工具上输出 shutdown save 命令,意思是强制让 Redis 数据库执行保留操作并敞开 Redis 服务,这样做能保障在执行 Redis 敞开时不失落任何数据。在重启后,Redis 会从硬盘上加载长久化的文件,以确保数据集继续可用。

2. 限度内存替换:

如果内存碎片率低于 1,Redis 实例可能会把局部数据交换到硬盘上。内存替换会重大影响 Redis 的性能,所以应该减少可用物理内存或缩小实 Redis 内存占用。可查看 used_memory 章节的优化倡议。

3. 批改内存分配器:

Redis 反对 glibc’s malloc、jemalloc11、tcmalloc 几种不同的内存分配器,每个分配器在内存调配和碎片上都有不同的实现。

不倡议一般管理员批改 Redis 默认内存分配器,因为这须要齐全了解这几种内存分配器的差别,也要从新编译 Redis。这个办法更多的是让其理解 Redis 内存分配器所做的工作,当然也是改善内存碎片问题的一种方法。

回收 key

info 信息中的 evicted_keys 字段显示的是,因为 maxmemory 限度导致 key 被回收删除的数量。对于 maxmemory 的介绍见后面章节,回收 key 的状况只会产生在设置 maxmemory 值后,不设置会产生内存替换。当 Redis 因为内存压力须要回收一个 key 时,Redis 首先思考的不是回收最旧的数据,而是在最近起码应用的 key 或行将过期的 key 中随机抉择一个 key,从数据集中删除。

这能够在配置文件中设置 maxmemory-policy 值为“volatile-lru”或“volatile-ttl”,来确定 Redis 是应用 lru 策略还是过期工夫策略。假使所有的 key 都有明确的过期工夫,那过期工夫回收策略是比拟适合的。若是没有设置 key 的过期工夫或者说没有足够的过期 key,那设置 lru 策略是比拟正当的,这能够回收 key 而不必思考其过期状态。

依据 key 回收定位性能问题

跟踪 key 回收是十分重要的,因为通过回收 key,能够保障正当调配 Redis 无限的内存资源。如果 evicted_keys 值常常超过 0,那应该会看到客户端命令响应延迟时间减少,因为 Redis 岂但要解决客户端过去的命令申请,还要频繁的回收满足条件的 key。

须要留神的是,回收 key 对性能的影响远没有内存替换重大,若是在强制内存替换和设置回收策略做一个抉择的话,抉择设置回收策略是比拟正当的,因为把内存数据交换到硬盘上对性能影响十分大(见后面章节)。

缩小回收 key 以晋升性能

缩小回收 key 的数量是晋升 Redis 性能的间接方法,上面有 2 种办法能够缩小回收 key 的数量:

1. 减少内存限度:

假使开启快照性能,maxmemory 须要设置成物理内存的 45%,这简直不会有引发内存替换的危险。若是没有开启快照性能,设置零碎可用内存的 95% 是比拟正当的,具体参考后面的快照和 maxmemory 限度章节。如果 maxmemory 的设置是低于 45% 或 95%(视长久化策略),通过减少 maxmemory 的值能让 Redis 在内存中存储更多的 key,这能显著缩小回收 key 的数量。若是 maxmemory 曾经设置为举荐的阀值后,减少 maxmemory 限度岂但无奈晋升性能,反而会引发内存替换,导致提早减少、性能升高。maxmemory 的值能够在 Redis-cli 工具上输出 config set maxmemory 命令来设置。

须要留神的是,这个设置是立刻失效的,但重启后失落,须要永久化保留的话,再输出 config rewrite 命令会把内存中的新配置刷新到配置文件中。

2. 对实例进行分片:

分片是把数据宰割成适合大小,别离寄存在不同的 Redis 实例上,每一个实例都蕴含整个数据集的一部分。通过分片能够把很多服务器联结起来存储数据,相当于减少总的物理内存,使其在没有内存替换和回收 key 的策略下也能存储更多的 key。如果有一个十分大的数据集,maxmemory 曾经设置,理论内存应用也曾经超过了举荐设置的阀值,那通过数据分片能显著缩小 key 的回收,从而进步 Redis 的性能。分片的实现有很多种办法,上面是 Redis 实现分片的几种常见形式:

  • a. Hash 分片:一个比较简单的办法实现,通过 Hash 函数计算出 key 的 Hash 值,而后值所在范畴对应特定的 Redis 实例。
  • b. 代理分片:客户端把申请发送到代理上,代理通过分片配置表抉择对应的 Redis 实例。如 Twitter 的 Twemproxy,豌豆荚的 codis。
  • c. 一致性 Hash 分片
  • d. 虚构桶分片

总结

对于开发者来说,Redis 是个速度十分快的 key-value 内存数据库,并提供了不便的 API 接口。为了最好最优的应用 Redis,须要了解哪些因素能影响到 Redis 性能,哪些数据指标能帮忙咱们防止性能陷阱。通过本篇,能了解 Redis 中的重要性能指标,怎么查看,更重要的是怎么利用这些数据排查解决 Redis 性能问题。

本篇博客次要翻译了一电子书的两头 15 页,电子书地址是:

https://www.datadoghq.com/wp-…

楼主翻译程度无限,如有谬误之处请多多包涵,也欢送指出交换,心愿本文对大家有所帮忙。

作者:蘑菇学生 \
出处:http://mushroom.cnblogs.com/

近期热文举荐:

1.600+ 道 Java 面试题及答案整顿(2021 最新版)

2. 终于靠开源我的项目弄到 IntelliJ IDEA 激活码了,真香!

3. 阿里 Mock 工具正式开源,干掉市面上所有 Mock 工具!

4.Spring Cloud 2020.0.0 正式公布,全新颠覆性版本!

5.《Java 开发手册(嵩山版)》最新公布,速速下载!

感觉不错,别忘了顺手点赞 + 转发哦!

正文完
 0