Java web应用性能分析之【高并发之缓存-多级缓存】

     

          说到缓存,作为java开发第一时间想到的是不是上图所示的Redis,又或者是Guava Cache、Caffeine、EhCache这些;Redis作为分布式缓存、其他的可以作为本地缓存。但是作为一名资深开发人员,着眼的层面应该再提升一个级别,从结构层面去考虑缓存,其实缓存指的是“多级缓存”。我们所说的Java Web应用,在当前技术栈下指的是基于springcloud的微服务应用,如下图微服务请求响应示意图所示,从客户端到服务端每个环节都有缓存。随着互联网业务的增长,微服务架构为了应对三高(高可用、高性能、高并发)中的高性能、高并发,需要引入“缓存”来应对高并发问题,从而达到高性能的目的。因此,在微服务架构中、要聊缓存、需要了解一下软件架构“三高”中的高并发。

微服务请求响应示意图

软件架构“三高”特性

  • 高并发(High Concurrency):高并发指的是系统能够同时处理大量的用户请求或操作。在高并发环境下,系统需要有效地管理资源,如线程和数据库连接,以便同时服务于大量用户或执行大量任务,而不会降低性能或导致服务中断。
  • 高可用(High Availability):高可用性指的是系统能够持续不断地为用户提供服务,即使面临部分故障或维护操作。这通常通过冗余设计(如多服务器、负载均衡、故障转移等)实现,确保系统的关键部分在任何时候都有一个备份可以接管工作。
  • 高性能(High Performance):高性能涉及到系统响应用户请求的速度和处理数据的能力。这不仅包括快速响应用户的交互请求,还包括在后端处理大量数据时的效率。提升性能通常涉及到优化代码、使用高效的算法、以及合理地利用硬件资源。

        这三个特性相互依赖,是打造健壮系统的基础。例如,提高系统的并发能力可能需要牺牲一部分性能;同样地,为了保证高可用性,可能需要投入更多的硬件资源和复杂的系统设计,这也可能影响性能。因此,在设计和优化系统时,需要在这三个方面之间找到一个平衡点。

高性能

        什么是高性能呢?高性能是指程序处理速度非常快,所占内存少,cpu占用率低。高性能的指标经常和高并发的指标紧密相关,想要提高性能,那么就要提高系统发并发能力,两者互相捆绑在一起。应用性能优化的时候,对于计算密集型和IO密集型还是有很大差别,需要分开来考虑。还有可以增加服务器的数量,内存,IO等参数提升系统的并发能力和性能,但不要浪费资源,要考虑硬件的使用率最高才能发挥到极致。

怎么样提高性能呢?

        避免因为IO阻塞让CPU闲置,导致CPU的浪费避免多线程间增加锁来保证同步,导致并行系统串行化免创建、销毁、维护太多进程、线程,导致操作系统浪费资源在调度上。

高可用

        高可用通常来描述一个系统经过专门的设计,从而减少停工时间,而保持其服务的高度可用性。高可用注意如果使用单机,一旦挂机将导致服务不可用,可以使用集群来代替单机,一台服务器挂了,还有其他后备服务器能够顶上。或者使用分布式部署项。比如现在redis的高可用的集群方案有: Redis单副本,Redis多副本(主从),Redis Sentinel(哨兵),Redis Cluster,Redis自研。

高并发的场景

        高并发系统是指在同一时间内有大量用户同时访问和操作的应用软件系统。例如:互联网(热门门户网站、微博、社交媒体、电商)、金融、游戏等领域。由于用户量大、访问频繁,高并发系统需要具备高性能、高可用性、高扩展性等特点,以满足用户的需求。

       例如,在电商平台上有大量用户同时浏览、搜索商品,提交订单等操作;社交媒体平台上有大量用户同时发布、点赞、评论等操作。这些场景需要系统能够同时处理大量请求,并保证系统的性能、可用性和用户体验。

高并发带来的问题

1. 请求延迟(系统性能的下降和延迟增加)

        在高并发系统中,请求延迟是一个常见的问题。由于大量的用户同时发起请求,系统需要处理大量的请求,导致请求的响应时间变长,甚至出现请求超时的情况。这种情况不仅影响用户体验,还可能对业务造成影响。

2. 资源竞争(资源竞争和资源耗尽、系统性能的下降和延迟增加)

        由于多个线程或进程同时访问共享资源,高并发系统容易出现资源竞争的问题。当多个请求同时访问同一份资源时,系统需要对其进行加锁或同步处理,以避免数据不一致和冲突。如果资源竞争过于激烈,会导致系统性能下降,甚至出现死锁和崩溃的情况。

3. 数据库瓶颈(资源竞争和资源耗尽、系统性能的下降和延迟增加)

        许多高并发系统都会使用数据库来存储和处理数据。在大量用户同时访问和操作时,数据库容易成为系统的瓶颈。数据库的读写性能、连接数和数据量等都可能成为制约系统性能的因素。如果数据库无法承受高并发请求,会导致查询速度变慢、数据丢失等问题。

4. 系统宕机(系统稳定性和可用性的挑战)

        在高并发场景下,如果系统的容量和性能没有得到充分的规划和设计,一旦遭遇流量高峰,系统容易宕机。此外,系统的硬件故障、网络故障以及软件缺陷等也可能导致系统宕机。系统宕机不仅影响用户体验,还可能对业务造成重大损失。

高并发的特点

  • 大量请求:高并发场景下,系统需要同时处理大量的请求,这些请求可能来自于不同的用户或客户端。
  • 同时访问:这些请求几乎同时到达系统,需要在短时间内进行处理和响应。
  • 资源竞争:由于大量请求同时到达,系统的资源(如CPU、内存、网络带宽等)可能会面临竞争和争夺。
  • 响应时间要求高:高并发场景通常对系统的响应速度有较高的要求,用户期望能够快速获取响应结果

高并发的指标

        高并发是现在互联网分布式框架设计必须要考虑的因素之一,它是可以保证系统能被同时并行处理很多请求,对于高并发来说,它的指标有:

  • 响应时间:系统对进来的请求反应的时间,比如你打开一个页面需要1秒,那么这1秒就是响应时间。
  • 吞吐量:吞吐量是指每秒能处理多少请求数量,好比你吃饭,每秒能吃下多少颗米饭。秒查询率:秒查询率是指每秒响应请求数,和吞吐量差不多。
  • 并发用户数:同时承载正常使用系统功能的用户数量。例如一个即时通讯系统,同时在线量一定程度上代表了系统的并发用户数。


高并发应对策略:缓存、限流、降级

  •  缓存:缓解系统负载压力,提高系统响应速度。Java web应用性能分析之【高并发之缓存-多级缓存】-CSDN博客
  • 限流:控制并发访问量,保护系统免受过载影响。Java web应用性能分析之【高并发之限流】-CSDN博客
  • 降级:保证核心功能的稳定性,舍弃非关键业务或简化处理。Java web应用性能分析之【高并发之降级】-CSDN博客

缓存定义

        缓存是一种临时存储数据的技术,意味着在数据被使用之前将其复制到一个更快的存储介质中。在计算机领域,缓存一般用于提高系统的响应速度和性能。

        缓存是一种提高系统性能的技术,但需要适时清理以避免问题。删除缓存可以释放存储空间并保持系统的稳定性,但也可能带来一些不便。因此,在删除缓存之前,我们需要慎重考虑,并了解系统的特殊需求。

        1.磁盘缓存:存储在硬盘等永久性存储介质上,用于加速数据的读取和访问。

        2.CPU缓存:位于处理器内部的高速存储器,用于暂时存储频繁访问的数据或指令,提高计算机的性能。

        3.应用缓存:存储在内存中的应用程序数据或资源,用于提高应用程序的响应速度和用户体验。在这里,下面讲的“微服务架构下的缓存”即是应用缓存。

微服务架构下的缓存

        如下图“微服务请求响应示意图”所示,从web请求响应这个链路来说,每个节点都有自己的缓存,所以将缓存又叫做“多级缓存”,这样更具体。从前到后的多级缓存包括:浏览器缓存----》CDN缓存---》Nginx缓存-----》分布缓存------》java进程内的缓存

        多级缓存就是充分利用请求处理的每个环节,分别添加缓存,减轻Tomcat压力,提升服务性能:

  • 浏览器访问静态资源时,优先读取浏览器本地缓存
  • 访问非静态资源(ajax查询数据)时,访问服务端
  • 请求到达Nginx后,优先读取Nginx本地缓存
  • 如果Nginx本地缓存未命中,则去直接查询Redis(不经过Tomcat)
  • 如果Redis查询未命中,则查询Tomcat
  • 请求进入Tomcat后,优先查询JVM进程缓存
  • 如果JVM进程缓存未命中,则查询数据库

各种介质数据访问延迟

操作类型

粗略时间

访问本地内存

100ns

SSD磁盘搜索

100,000ns

网络数据包在同一个数据中心来回一次的时间

500,000ns

非SSD磁盘搜索

10,000,000ns

按顺序从网络读取1MB数据

10,000,000ns

按顺序从非SSD磁盘读取1MB数据

30,000,000ns

跨大西洋网络数据包来回一次的时间

150,000,000ns

跨太平洋网络数据包来回一次的时间

300,000,000ns

每秒等于多少

1,000,000,000ns

  • 美国访问中国的数据中心网络延迟就有300ms
  • redis/memcached一次请求(1-2k)大概耗时0.5ms
  • 加索引的数据库的一次请求(1-2k)大概耗时50ms,是缓存的100倍
技术栈各个层次的缓存

缓存为什么显著提升性能
  • 缓存数据通常来自内存,比磁盘等其他介质有更快的访问速度
  • 缓存的数据通常是终态,不需要中间计算,节省了CPU资源消耗
  • 缓存降低了数据库、磁盘、网络的负载压力,使得这些IO设备能获得更好的响应特性,提升系统整体性能
缓存不适宜场景
  • 频繁修改的数据

此类型数据应用还来不及读取就失效了徒增系统负担,一般数据的读写比在2:1以上,缓存才有意义。

  • 没有热点的访问

缓存使用内存存储,内存资源有限且宝贵,如果数据没有二八定律即大部分访问集中在小部分数据上,则缓存效果不会明显

  • 数据不一致与脏读不允许

一般会对缓存数据设置过期时间,过期时间内可能会和数据库不一致,如果业务容忍则也可以使用,如果业务不能容忍,则需要数据在数据库变更时也要清除缓存。

客户端缓存--浏览器缓存

        浏览器缓存是指将网页中的资源(如HTML、CSS、JavaScript、图像等)存储在用户的浏览器内部,以便在后续请求同一资源时可以直接从本地缓存中获取,而无需再次从服务器下载。

适用场景

        浏览器缓存适用于那些静态内容变化较少的网页和静态资源,可以显著提升网站性能和用户体验,并减少服务器的负载。

常见用法

        使用浏览器缓存可以通过设置响应头中的Expires和Cache-Control字段来控制缓存的行为。

        1.使用Expires字段:Expires字段指定了缓存的过期时间,是一个具体的日期和时间。服务器可以在响应头中添加Expires字段,告诉浏览器在该时间之前可以直接从缓存中获取资源,而无需再向服务器发起请求。例如:Expires: Mon, 31 Dec 2022 23:59:59 GMT。

        2.使用Cache-Control字段:Cache-Control字段提供了更灵活的缓存控制选项。可以通过设置max-age指令来指定缓存的最大有效时间,单位是秒。例如:Cache-Control: max-age=3600 表示资源可以在1小时内直接从缓存中获取。还可以使用其他指令,如no-cache表示缓存但不使用缓存、no-store表示禁止缓存等。

注意事项

        浏览器缓存存储实时性不敏感的数据,如商品框架、商家评分、评价和广告词。它有过期时间,并通过响应头进行控制。实时性要求高的数据不适合使用浏览器缓存。

服务端缓存--CDN缓存

        CDN(Content Delivery Network)是建立在承载网之上的分布式网络,由分布在不同区域的边缘节点服务器组成。

        CDN缓存通常用于存放静态页面数据、活动页面、图片等数据。它有两种缓存机制:推送机制(将数据主动推送到CDN节点)和拉取机制(首次访问时从源服务器获取数据并存储在CDN节点)。

缓存相关常见问题_CDN(CDN)-阿里云帮助中心

服务端缓存--Nginx缓存

第一步:客户端第一次向Nginx请求数据A;

第二步:当Nginx发现缓存中没有数据A时,会向服务端请求数据A;

第三步:服务端接收到Nginx发来的请求,则返回数据A到Nginx,并且缓存在Nginx;

第四步:Nginx返回数据A给客户端应用;此时状态码是200

第五步:客户端第二次向Nginx请求数据A;

第六步:当Nginx发现缓存中存在数据A时,则不会请求服务端;

第七步:Nginx把缓存中的数据A返回给客户端应用。此时状态码是304

Nginx-cache配置

Nginx通过proxy_cache来实现缓存。Buffer(缓冲)主要用于传输效率不同步或者优先级不相同的设备之间传输数据,通过对一方数据进行临时存放,在统一发送的方式传递给另一方,以降低进程间的等待时间;Cache(缓存)主要用于将硬盘上已有的数据在内存中建立缓存数据,提高数据的访问效率。

而proxy_cache只有在Proxy Buffer机制开启的情况下Proxy Cache的配置才会发挥作用

相关配置

  • proxy_zone:zone | off 默认是off,即关闭proxy_cache功能,zone为用于存放缓存的内存区域名称,可以在http/server、location块内使用

  • proxy_cache_path: path [levels=levels] keys_zone-name:size [inactive=time] [max_size=size] 只能在http块内使用
    • path设置缓存数据存放的路径

    • levels设置目录层级,如levels=1:2,表示有两个子目录

    • keys_zone 设置内存zone的名称和大小,如keys_zone=my:10m

    • inactive设置缓存多长时间失效,当磁盘上的缓存数据在该时间段内没有被访问过,就会失效,数据将被删除,默认10s

    • max_size 设置硬盘中最多缓存多少数据,数据超出,则删除最少访问的数据

  • proxy_cache_methods GET HEAD POST 设置缓存哪些方法

  • proxy_cache_min_uses 1 设置缓存的最小使用次数

  • proxy_cache_valid code time 对不同的状态码缓存不同的时间

  • proxy_cache_key line 设置缓存的key值

http {
    proxy_cache_path    /var/www/cache #缓存地址
                        levels=1:2 #目录分级
                        keys_zone=test_cache:10m #开启的keys空间名字:空间大小(1m可以存放8000个key)
                        max_size=10g #目录最大大小(超过时,不常用的将被删除)
                        inactive=60m #60分钟内没有被访问的缓存将清理
                        use_temp_path=off; #是否开启存放临时文件目录,关闭默认存储在缓存地址
                                         
    server {
    		# 使用缓存
        location / {
            proxy_cache test_cache;    #开启缓存对应的名称,在keys_zone命名好
            proxy_cache_valid 200 304 12h;    #状态码为200 304的缓存12小时
            proxy_cache_valid any 10m;    #其他状态缓存10分钟
            proxy_cache_key $host$uri$is_args$args;    #设置key值
            add_header Nginx-Cache "$upstream_cache_status";
        }
        #不使用缓存
        if ($request_uri ~ ^/(login|register) ) {    #当请求地址有login或register时
        	set $nocache = 1;    #设置一个自定义变量为true
    		}
    		location / {
        	proxy_no_cache $nocache $arg_nocache $arg_comment;
        	proxy_no_cache $http_pragma $http_authoriztion;
    		}
    }
}

expires配置
location ~ .*.(jpg|jpeg|gif|png)$ {
	# 设置图片缓存过期时间
	expires 1d;
} 

# 匹配静态目录
location ~^ /(|css|js) / {
	expires 2h;
}
    location ~ .*\.(?:htm|html)$    #不缓存html
    {
        add_header Cache-Control "private, no-store, no-cache, must-revalidate, proxy-revalidate";
    }
 }

响应会返回给浏览器Expires属性,展示的是过期时间,之后再次请求该资源时,如果没有超过响应返回的Expires时,则不需要向服务器访问,直接从缓存中获取

静态资源缓存
# 缓存zone levels表示缓存层级以及目录位数 keys_zone表示缓存内存大小 inactive有效期 max_size所占用的最大磁盘大小
proxy_cache_path /data/nginx/cache_ad levels=1:2 keys_zone=cache_cache_ad:100m inactive=7d max_size=200m;

server {
 location / {
   # 缓存zone
   proxy_cache cache_ad;
   # 缓存key  进行md5
   proxy_cache_key $host$uri$is_args$args;
   # 什么情况下进行缓存存储
   proxy_cache_valid 200 304 12h;
 }
}
检查是否使用Nginx缓存

        如果需要知道Nginx是否使用了缓存,你可以在响应头中加入$upstream_cache_status变量以进行检测。

add_header X-Cache-Status $upstream_cache_status;这是在Nginx添加缓存命中状态的add_header指令。

        此示例X-Cache-Status在响应客户端时添加HTTP标头。以下是$upstream_cache_status可能的值。

  • MISS在缓存中找不到响应,因此从原始服务器获取响应。然后缓存响应。HIT响应直接来自有效的缓存。
  • BYPASS响应是从原始服务器获取的,而不是从缓存中提供的,因为请求与proxy_cache_bypass指令匹配。
  • EXPIRED缓存中的记录已过期。响应包含来自原始服务器的新内容。
  • STALE内容过时,因为源服务器未正确响应但proxy_cache_use_stale已配置。
  • UPDATING缓存内容已过时,因为当前正在更新以响应先前的请求,并且proxy_cache_use_stale updating已配置。
  • REVALIDATED-proxy_cache_revalidate指令已启用,Nginx验证当前缓存的内容是否仍然有效通过If-Modified-SinceIf-None-Match
服务端缓存--分布式缓存

        分布式寻址算法是分布式对象缓存的关键,即缓存键如何分布到不同服务器,集群增加节点时如何处理

均匀hash算法
  • 针对一个key,计算hashcode,然后在对节点数量取模,完成寻址。
  • 当新增节点或某个节点故障时,会有大量key的缓存失效,给数据库带来压力。
一致性hash算法
  • 解决分布式缓存集群扩容时数据访问不一致问题的算法,防止缓存雪崩。
  • 实现步骤
    • 构建一个一致性hash环(0-(232-1),也是hashcode的范围即4个字节的范围)
    • 首先根据node的hashcode把node加入到环上
    • 再根据key的hashcode把key加入到环上
    • 最后沿着环按顺时针找到最近的node完成寻址
  • 当新增节点或某个节点故障时,仅有少量的key的缓存失效,把压力降到最低。
  • 但此算法的缺点就是node的hashcode可能分布不均匀导致负载不均衡,需要基于虚拟节点的一致性hash
基于虚拟节点的一致性hash算法
  • 把node拆分成M个虚拟节点(nodeN_0......nodeN_M)
  • 然后把虚拟节点按hashcode放入hash环,解决均衡问题
  • 虚拟节点越多,对增减节点时缓存失效的概率越低,同时算法的效率也会降低,综合起来M应该在150-200
Redis VS Memcached
  • Redis支持复杂的数据结构,Memcached只支持字符串
  • Redis支持多路复用、异步IO保证高性能
  • Redis支持主从复制保证高可用
  • Redis原生支持集群模式
Redis集群
  • 集群预分好16384个slot,根据CRC16(key) mod 16384的值决定key放入哪个slot,结果是均匀的
  • Redis-cluster把所有物理节点映射到[0-16383]slot上,并负责维护slot与服务器的映射关系
  • slot使得增/删节点变得非常简单,可以像磁盘分区一样自由分配slot,在配置文件里可显示指定也可默认
  • 管理员可以根据机器的配置和负载情况进行slot的动态调整,基本上解决了最开始的负载均衡问题
  • 当新增节点时,各节点会分一些slot到新节点。当删除节点时,该节点上的slot也会分给其他节点
  • 所有的Redis节点彼此互联,客户端连接集群上任何一个可用节点即可
服务端缓存--java进程内的缓存(本地缓存)

        在Java中,本地缓存通常是指在应用程序中缓存数据的一种方式,以便更快地访问经常需要的数据。这种缓存通常是在应用程序的生命周期内有效的。

        在JAVA里一般我们有几种类型的应用缓存可以选择,比如JAVA本身提供的缓存(HashMap,ConcurrentHashMap)、还有第三方提供的应用缓存Ecache、 Guava 、caffeine。因为JAVA提供的缓存只提供了简单的缓存增加删除机制,基本上没有任何扩展的功能,所以就衍生了一批第三方的应用缓存,第三方应用缓存提供了丰富的扩展功能,比如说我们上面的缓存自动失效机制、缓存事件订阅机制,这些扩展机制能给开发人员管理缓存提供很大的便捷性。 

        本地缓存对比

框架命中率速度回收算法使用难度集群适用场景内存线程安全
Guava cache第三LRU、LFU、FIFO不支持读多写少,允许少量缓存偏移jvm堆内存
Caffeine第一W-TinyLFU不支持读多写少,允许少量缓存偏移,能用 Caffeine 就别用 Guava cachejvm堆内存
Ehcache第二LRU、LFU、FIFO支持分布式系统中对数据一致性要求高jvm堆内存+直接内存+磁盘
HashMap
ConcurrentHashMap

引入缓存带来的问题

  • 缓存预热

        缓存中存放的是热点数据,热点数据通过LRU算法筛选出来的,整个过程时间比较长,过程内性能一般,需要在缓存系统启动时就把热点数据加载好就是缓存预热warm up,

  • 缓存穿透

        如果不恰当的业务或恶意请求持续高并发的请求某个不存在的缓存,如果缓存没有相应的对策,那所有的查询请求都落到数据库上,带来很大的压力,甚至崩溃。一个简单的对策是将不存在的数据也缓存起来值为null,并设置较短的过期时间

  • 缓存雪崩

        当缓存服务器崩溃时,请求压力全部打倒数据库,导致数据库也宕机,进而整个服务失效。发生这种问题时甚至不能简单的重启缓存服务器和数据库服务器来恢复。

  • 缓存一致性:数据库和分布式缓存不一致

  分两种情况,读数据和写数据(更新)

    1. 读数据:读数据时候先读取缓存,如果缓存没有(miss hit)就读取数据库,然后在从数据库中取出数据并添加到缓存中,最后在返回数据给客户端。

        

    2. 更新数据:  先更新数据库数据在删除缓存(也有人认为先删除缓存在更新数据库)。

      

  • 缓存一致性:数据库和多级缓存不一致      

但事无完美,当引入多级缓存后,我们又会遇到缓存数据一致性的挑战,以下图为例:

图片

缓存一致性问题

我们都知道作为数据库写操作,是不通过缓存的。假设商品服务实例 1 将 1 号商品价格调整为 80 元,这会衍生一个新问题:如何主动向应用程序推送数据变更的消息来保证它们也能同步更新缓存呢?

相信此时你已经有了答案。没错,我们需要在当前架构中引入 MQ 消息队列,利用 RocketMQ 的主动推送功能来向其他服务实例以及 Redis 缓存服务发起变更通知。

图片

通过 RocketMQ 解决保证消息一致性

如上图所示,在商品服务实例 1 对商品调价后,主动向 RocketMQ Broker 发送变更消息,Broker 将变更信息推送至其他实例与 Redis 集群,这些服务实例在收到变更消息后,在缓存中先删除过期缓存,再创建新的数据,以此保证各实例数据一致。

看到这里你会发现,对于缓存来说,并没有终极的解决方案。虽然多级缓存设计带来了更好的应用性能,但也为了缓存一致性必须引入 MQ 增加了架构的复杂度。那到底多级缓存设计该如何取舍呢?在我看来,有三种情况特别适合引入多级缓存。

  • 第一种情况,缓存的数据是稳定的。例如邮政编码、地域区块、归档的历史数据这些信息适合通过多级缓存减小 Redis 与数据库的压力。

  • 第二种情况,瞬时可能会产生极高并发的场景。例如春运购票、双 11 零点秒杀、股市开盘交易等,瞬间的流量洪峰可能击穿 Redis 缓存,产生流量雪崩。这时利用预热的进程内缓存分摊流量,减少后端压力是非常有必要的。

  • 第三种情况,一定程度上允许数据不一致。例如某博客平台中你修改了自我介绍这样的非关键信息,此时在应用集群中其他节点缓存不一致也并不会带来严重影响,对于这种情况我们采用T+1的方式在日终处理时保证缓存最终一致就可以了。

以上是我总结的三种适合服务层做多级缓存的场景。当然如果你们的应用并发量不大,在未来的1~2 年内利用 Redis 分布式缓存集群完全可以胜任应用性能要求,那自然就没有必要设计多级缓存,我们要根据业务特点灵活调整架构。

  • 缓存不可用之旁路缓存

        缓存不可用时,不能阻塞正常业务请求。Redis 是一个独立的系统软件,和业务应用程序是两个软件,当我们部署了 Redis 实例后,它只会被动地等待客户端发送请求,然后再进行处理。所以,如果应用程序想要使用 Redis 缓存,我们就要在程序中增加相应的缓存操作代码。所以,我们也把 Redis 称为旁路缓存,也就是说,读取缓存、读取数据库和更新缓存的操作都需要在应用程序中来完成。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/638968.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LLM多模态——GPT-4o改变人机交互的多模式 AI 模型应用

1. 概述 OpenAI 发布了迄今为止最新、最先进的语言模型 – GPT-4o也称为“全“ 模型。这一革命性的人工智能系统代表了一次巨大的飞跃,其能力模糊了人类和人工智能之间的界限。 GPT-4o 的核心在于其原生的多模式特性,使其能够无缝处理和生成文本、音频…

微信小程序开发环境的搭建

一、注册微信小程序账号 二、安装微信开发者工具 1.下载微信开发者工具。 官网下载地址:https://mp.weixin.qq.com/debug/wxadoc/dev/devtools/downloads.html 2、选择稳定版Window64下载安装 3、下载完毕后,点击下一步安装 三、使用微信开发者工具…

slam14讲(第8讲、前端里程计)LK光流、直接法

直接法的引出 因为第7讲大部分都是讲特征点法,通过提取orb特征点和点的描述子,来构建两帧图像之间的特征点对应关系。这种方法会有缺点: 关键点和描述子提取计算耗时,如果相机的频率高,则slam算法大部分耗时被占。特…

2种方法将集合数据List构建成树形结构

文章目录 递归循环构建树结构hutool.TreeUtil.build构建树结构 递归循环构建树结构 先查最外层树节点数据&#xff0c;再递归遍历每一层子节点数据 public ApiResultDto<List<LocationDto>> getTreeByParams(LocationSearchDto searchDto, SecurityUser user) {// …

代码随想录算法训练营第三十六天|860.柠檬水找零、406.根据身高重建队列、452. 用最少数量的箭引爆气球

860.柠檬水找零 文档讲解&#xff1a;代码随想录 题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 注意看提示&#xff1a; bills[i] 不是 5 就是 10 或是 20 场景较为固定 遇到了20&#xff0c;优先消耗10 class Solution:def lemonadeChange(self, bills: …

秋招突击——算法——模板题——区间DP——合并石子

文章目录 题目内容思路分析实现代码分析与总结 题目内容 思路分析 基本思路&#xff0c;先是遍历区间长度&#xff0c;然后再是遍历左端点&#xff0c;最后是遍历中间的划分点&#xff0c;将阶乘问题变成n三次方的问题 实现代码 // 组合数问题 #include <iostream> #in…

宝塔Linux下安装EMQX服务并设置匿名访问

简述 之前有在Windows和Linux下搭建过EMQX服务并且使用方面都没问题,但那都是使用的用户和密码方式访问,且前提都是通过浏览器进入EMQX的配置页面设置的属性; 但这次使用的是腾讯云租用的宝塔Liniux,由于没有浏览器只能通过命令行方式修改EMQX配置以达到目的;由于事先没看…

使用 CapSolver API 服务解决 Arkose Labs FunCaptcha 验证码

使用 CapSolver API 服务解决 Arkose Labs FunCaptcha 验证码 FunCaptcha 以其复杂的图像验证而闻名&#xff0c;对自动化系统构成了巨大的挑战。CapSolver 的 API 服务利用先进的 AI 技术轻松应对和解决 FunCaptcha 挑战。本指南探讨了 CapSolver 如何实现无缝自动化&#xff…

汇聚荣科技有限公司优点有哪些?

在当今快速发展的科技时代&#xff0c;企业之间的竞争愈发激烈。作为一家专注于科技创新与研发的公司&#xff0c;汇聚荣科技有限公司凭借其卓越的技术实力和创新能力&#xff0c;在业界树立了良好的口碑。那么&#xff0c;汇聚荣科技有限公司究竟有哪些优点呢?接下来&#xf…

XX数字中台技术栈及能力

XX数字中台技术栈及能力 1 概述 XX数字中台面向数据开发者、数据管理者和数据应用者&#xff0c;提供数据汇聚、融合、治理、开发、挖掘、共享、可视化、智能化等能力&#xff0c;实现数据端到端的全生命周期管理&#xff0c;以共筑数字基础底座&#xff0c;共享数据服务能力…

The Missing Semester of Your CS Education(计算机教育中缺失的一课)

Shell 工具和脚本(Shell Tools and Scripting) 一、shell脚本 1.1、变量赋值 在bash中为变量赋值的语法是foobar&#xff0c;访问变量中存储的数值&#xff0c;其语法为 $foo。 需要注意的是&#xff0c;foo bar &#xff08;使用空格隔开&#xff09;是不能正确工作的&…

llama-factory学习个人记录

框架、模型、数据集准备 1.llama-factory部署 # 克隆仓库 git clone https://github.com/hiyouga/LLaMA-Factory.git # 创建虚拟环境 conda create --name llama_factory python3.10 # 激活虚拟环境 conda activate llama_factory # 安装依赖 cd LLaMA-Factory pip install -…

Java 使用继承和重写父类方法写一个商品入库案例

package 练习.商品入库;import java.util.Scanner; // 抽象手机类 public abstract class Phone {//测试方法public static void main(String[] args){// 华为手机huawei h new huawei();h.setName("华为P40");h.setPrice(1999.99);h.setConfiguration("8128GB…

Go知识点复习

Go知识点复习 1.关于包的使用和GOPATH的配置 src:用于以代码包的形式组织并保存Go源码文件, 需要手动创建pkg目录&#xff1a;用于存放经由go install命令构建安装后的代码包&#xff08;包含Go库源码文件&#xff09;的“.a”归档文件bin目录&#xff1a;与pkg目录类似&…

kind: Telemetry

访问日志 访问日志提供了一种从单个工作负载实例的角度监控和理解行为的方法。 Istio 能够以一组可配置的格式为服务流量生成访问日志&#xff0c; 使操作员可以完全控制日志记录的方式、内容、时间和地点。 有关更多信息&#xff0c;请参阅获取 Envoy 的访问日志。 https:/…

ThingsBoard如何拆分前后端分离启动

后端启动 前端启动 注意事项 ThingsBoard是一个开源的物联网平台&#xff0c;它原本的设计就考虑到了现代Web应用的前后端分离架构。尽管其核心是一个后端服务&#xff0c;负责设备连接、数据处理和存储等&#xff0c;但其用户界面是作为单独的前端应用程序实现的&#xff0c…

Windows下部署Seata1.5.2,解决Seata无法启动问题

目录 1. 版本说明 2. Windows下部署Seata1.5.2 2.1 创建回滚日志表undo_log 2.2 创建Seata服务端需要的四张表 2.3 在nacos创建seata命名空间&#xff0c;添加seataServer.yml配置 2.4 修改本地D:/tool/seata-server-1.5.2/seata/conf/applicaltion.yml文件 2.5 启动Seat…

抖音运营_抖音电商介绍

截止20年8月&#xff0c;抖音的日活跃数高达6亿。 20年6月&#xff0c;上线抖店 &#xff08;抖音官方电商&#xff09; 一 抖店的定位和特色 1 一站式经营 帮助商家进行 商品交易、店铺管理、客户服务 等全链路的生意经营 2 多渠道拓展 抖音、今日头条、西瓜、抖音火山版…

markdown 文件渲染工具推荐 obsidian publish

背景 Markdown 是一种轻量级的标记语言&#xff0c;最开始使用它是觉得码字非常方便&#xff0c;从一开始的 word 排版到 markdown &#xff0c;还不太不习惯&#xff0c;用了 obsidian把一些文字发在网上后&#xff0c;才逐渐发现他的厉害之处。 让人更加专注于内容本身&…

信号:MSK调制和GMSK调制

目录 一、MSK信号 1. MSK信号的第k个码元 2.MSK信号的频率间隔 3.MSK信号的相位连续性 3.1 相位路径 3.2初始相位ψk 4.MSK信号的产生 原理框图 5.MSK信号的频谱图 二、高斯最小频移键控(GMSK) 1.频率响应 2.GMSK调制产生方式 2.1 高斯滤波器法 2.2 正交调制器法…