当前位置: 首页 > news >正文

从单机缓存到分布式缓存那些事

作者:秦怀

1 缓存前世今生

1.1 故事从硬件开始

Cache 一词来源于 1967 年的一篇电子工程期刊论文。其作者将法语词“cache”赋予“safekeeping storage”的涵义,用于电脑工程领域。当时没有 Cache,CPU 和内存都很慢,CPU 直接访问内存。

  • Intel 80386芯片组增加了对可选的 Cache 的支持,高级主板带有 64KB,甚至高端的 128KB Write-Through Cache。
  • Intel 80486 CPU 里面加入了 8KB 的 L1 Unified Cache,当时也叫做内部 Cache,不分代码和数据,都存在一起;芯片组中的 Cache,变成了 L2,也被叫做外部 Cache,从 128KB 到 256KB 不等;增加了 Write-back 的 Cache 属性。
  • Pentium (奔腾) CPU 的 L1 Cache 分为 Code 和 data,各自 8KB;L2 还被放在主板上。
  • Pentium Pro(奔腾) 的 L2 被放入到 CPU 的 Package 上。
  • Pentium 3(奔腾) 开始,L2 Cache 被放入了 CPU 的 Die 中。
  • Intel Core CPU 开始,L2 Cache 为多核共享。

当 CPU 处理数据时,它会先到 Cache 中去寻找,如果数据因之前的操作已经读取而被暂存其中,就不需要再从 随机存取存储器(Main memory)中读取数据——由于 CPU 的运行速度一般比主内存的读取速度快,主存储器周期(访问主存储器所需要的时间)为数个时钟周期。因此若要访问主内存的话,就必须等待数个 CPU 周期从而造成浪费。

提供“缓存”的目的是为了让数据访问的速度适应 CPU 的处理速度,其基于的原理是内存中“程序执行与数据访问的局域性行为”,即一定程序执行时间和空间内,被访问的代码集中于一部分。为了充分发挥缓存的作用,不仅依靠“暂存刚刚访问过的数据”,还要使用硬件实现的指令预测数据预取 技术——尽可能把将要使用的数据预先从内存中取到缓存里。

CPU 的缓存曾经是用在超级计算机上的一种高级技术, 不过现今电脑上使用的的 AMD 或 Intel 微处理器都在芯片内部集成了大小不等的数据缓存和指令缓存, 通称为 L1 缓存 (L1 Cache 即 Level 1 On-die Cache, 第一级片上高速缓冲存储器);
而比 L1 更大容量的 L2 缓存曾经被放在 CPU 外部 (主板或者 CPU 接口卡上), 但是现在已经成为 CPU 内部的标准组件; 更昂贵的 CPU 会配备比 L2 缓存还要大的 L3 缓存 (level 3 On-die Cache 第三级高速缓冲存储器)

1.2 概念的扩展

如今缓存的概念已被扩充, 不仅在 CPU 和主内存之间有 Cache, 而且在内存和硬盘之间也有 Cache (磁盘缓存), 乃至在硬盘与网络之间也有某种意义上的 Cache 称为 Internet 临时文件夹或网络内容缓存等凡是位于速度相差较大的两种硬件之间, 用于协调两者数据传输速度差异的结构, 均可称之为 Cache

现在我们软件开发中常说的缓存,是指磁盘和 CPU 之间的,协调两者传输速度的结构。

2 缓存的特征

2.1 主要特征

  • 命中率:命中率=返回正确结果数/请求缓存次数,命中率越高,表明缓存的使用率也就越高。
  • 吞吐量:缓存的吞吐量使用 OPS 值(每秒操作数,Operations per Second,ops/s)来衡量,反映了对缓存进行并发读、写操作的效率,即缓存本身的工作效率高低。
  • 缓存淘汰策略:
    • FIFO (first in first out):先进先出策略,最先进入缓存的数据在缓存空间不够的情况下(超出最大元素限制)会被优先被清除掉,以腾出新的空间接受新的数据。
    • LFU (less frequently used):最少使用策略,无论是否过期,根据元素的被使用次数判断,清除使用次数较少的元素释放空间。
    • LRU (least recently used):最近最少使用策略,无论是否过期,根据元素最后一次被使用的时间戳,清除最远使用时间戳的元素释放空间。

2.2 是否适合缓存的考虑

不是所有数据都适合缓存,我们使用缓存,是想用较小的成本换取较大的收益,在决定是否缓存之前,可以考虑以下的问题:

  • 是否有一致性的要求,缓存和底层存储是否需要强一致性
  • 缓存是不是高效的?命中率大概怎么样?
  • 缓存多久,是否需要设置 TTL
  • 数据结构是否适合缓存
  • 计算后缓存,亦或是缓存之后计算

2.3 惊群效益

如果许多不同的应用程序进程同时请求一个缓存键,但出现缓存未命中,随后所有应用程序进程都并行执行相同的数据库查询,此时就会发生惊群效应,也称作叠罗汉效应。此查询的代价越高,对数据库的影响就越大。一般可以通过缓存预热、缓存不存在的空值来减少。

3 缓存的分类

根据应用的耦合度,一般分为本地缓存和分布式缓存:

  • 本地缓存:在应用中的缓存组件,应用和 Cache 是在同一个进程内,请求特别快,没有网络开销。
  • 分布式缓存:与应用分离的缓存组件,可以认为是独立的服务,和应用分开,多个应用之间可以共享,但是会存在网络请求。

4 分布式缓存存在的必要性

先聊缓存的必要性,计算机的世界里,倘若有无法解决不了的问题,一般都可以再加一层来解决,而缓存从被提出开始,就是那个加了的一层。CPU的速度很快,数据库操作很慢,怎么办?CPU缓存很小很贵很快,但是数据库的磁盘很慢很大很便宜,怎么办?内存来解决!

可以提前将一些比较耗时的数据结果暂存到内存(如果有持久化,也会同时存储在磁盘中)中,如果有相同请求,可以直接返回,如果数据变更(更新或者删除),再处理掉缓存。大家平日里接触最多的,可能就是浏览器的缓存,有时候多次访问,有些数据根本不会再去请求,会优先使用浏览器的本地缓存。
除此之外,微博也是如此,

单机的缓存,可以满足大部分的场景,但是单节点的最大容量不能超过整个系统的内存,而且像 memcached 这种存储,断电内容就会彻底丢失,Redis 则有持久化的能力,只是通电之后需要花点时间从磁盘将数据 load 回内存中。

现在几乎应用服务器都是分布式的,如果只做单机缓存,意味着每个服务器的缓存,都存了一份,极大概率存在不一致的情况,比如 一个用户第一次请求命中机器 A,有缓存,第二次命中机器 B ,又没缓存,只能重新缓存了一份在机器 B 上。

5 分布式缓存设计可能需要考虑的几个问题

站在巨人(Redis)的肩膀上, 我们可以学到很多优秀的设计、理念,设计一个功能比较全面的分布式缓存,到底需要考虑哪些问题?

下面聊聊几点比较常见的:

5.1 、断电了怎么办?(持久化)

必须支持 持久化,可以异步的将数据刷盘,落到磁盘中,重新启动的时候能够加载已有的数据。那刷盘的时机是怎么样的?只要改一个数据就刷一次盘么?还是修改数据到达某个阈值,才进行刷盘,这些都是策略,最好是可以支持配置,这些规则其实我们都可以从 Redis 这些优秀的缓存中间件中学习到。
当然,如果在一定场景下,能接受数据完全丢失,不需要持久化,那么可以设置为关闭,可以节约性能开销。

5.2 2、内存不足怎么办?(缓存淘汰策略)

单机内存不足,可以删除一些数据。但是到底删除哪些数据,这必须有一个决策的算法,这就是缓存淘汰策略。
常见的缓存淘汰策略有以下几种:

  • FIFO:先进先出(First In,First Out),如同队列,新数据在尾部加入,内存不足的时候,淘汰的数据从队列头部移除。
  • LFU:最低频率使用淘汰算法(Least Frequently Used),也称为最近最不常使用,将使用频率最低的数据淘汰。
  • LRU:最近时间未使用(Least Recently used),也称为最近最少使用,内存不足的时候,总是淘汰最长时间未被使用得数据。

5.3 3、需不需要自定义协议?

一个稳定的分布式缓存系统,还需要一套序列化协议,怎么设计一个简单而又高效的协议,是个值得思考的问题。

比如 Redis 使用得就是 RESP(REdis Serialization Protocol) 协议,这是专门为 Redis 设计的,属于应用层的通信协议,本质上和 HTTP 是同一层级,而 Redis 的传输层使用的是 TCP。如果是服务器接收请求的场景,那么服务端从 TCPsocket 缓存区里面读取数据,然后经过了 RESP 协议解码知乎,会得到我们所需的指令。

简单讲一下,RESP 主要就是 想用更少的数据,表达所需的更丰富的内容,也就是压缩数据量,增加信息量。
比如第一个字节,决定了数据类型:

  • 简单字符串 :Simple Strings,第一个字节响应 +
  • 错误:Errors,第一个字节响应 -
  • 整型:Integers,第一个字节响应 :
  • 批量字符串:Bulk Strings,第一个字节响应 $
  • 数组:Arrays,第一个字节响应 *

5.4 4、一台机器存储不够怎么办?(可拓展)

不能一直增加单台机器的容量,抛开成本不讲,单机大容量,网络带宽,磁盘 IO,计算资源等都可能成为较大的瓶颈,肯定需要支持横向拓展(水平拓展),比如 Redis 集群模式。与横向拓展对应的是垂直拓展,也就是增加单个节点的容量,性能。互联网发展的这些年,已经证明了分布式系统是一个更优的选项。

5.5 5、如果有一台机器宕机了怎么办?(高可用)

如果多台机器中,有机器宕机怎么办?从事前、事中、事后来看:

  • 事前:需要可监控,需要有监控节点(比如 Redis 中的哨兵),并且有可以切换的节点(从节点)。
  • 事中:怎么切换,哪一个机器作为“主持人“角色进行切换,切换哪一个机器,都是需要抉择的。
  • 事后:切换之后,下线机器怎么处理。

5.6 6、是否支持并发?(高并发)

并发写入怎么办?Redis 采取的是队列的方式,内部不允许并发执行,也就不需要加锁,解锁的操作,如果考虑使用锁来实现,需要同时考虑上下文切换的成本,而我们简单的版本可以使用加锁的方式来实现。

6 使用分布式缓存可能会遇到的几个问题

6.1 1、一致性问题

如何保证缓存和数据库的一致性问题,是一个比较大的话题,我们除了保证数据库和缓存一致,分布式缓存的 master 和 slave 也需要保持一致。一般一致性分为以下几种:

  • 强一致性:数据库更新操作与缓存更新操作是原子性的,缓存与数据库的数据在任何时刻都是一致的,很难实现。
  • 弱一致性:当数据更新后,缓存中的数据可能是更新前的值,也可能是更新后的值,这种更新是异步的。
  • 最终一致性:一种特殊的弱一致性,在一定时间后,数据会达到一致的状态。最终一致性是弱一致性的理想状态,也是分布式系统的数据一致性解决方案上比较推崇的。

根据 CAP 原理,分布式系统在可用性、一致性和分区容错性上无法兼得,通常由于分区容错无法避免,所以一致性和可用性难以同时成立。

这里的几种方案就不展开讲了,几种更新策略:

  • 1、先更新缓存,再更新数据库:
    • 在两个线程一起更新的场景下,如果先更新缓存的线程后更新数据库,很容易出现一致性问题。
  • 2、先更新数据库,再更新缓存
    • 在两个线程一起更新的场景下,如果先更新数据库的线程由于执行慢了一些,后更新缓存,很容易出现一致性问题。
  • 3、先删除缓存,再更新数据库
    • 先删除缓存的线程,后更新数据库,仍然有一致性问题
  • 4、先更新数据库,再删除缓存
    • 先更新数据库的线程,后删除缓存,没有问题!删除缓存之后,会回源到数据库。
    • 但是没删除缓存之前,数据库更新了,读取会读到脏数据。所以我们一般推荐双删,更新之前删一次,更新之后删一次。
    • 这个时候有人会问,如果同时有个读请求,读的是写之前的脏数据,但是写入到缓存是比较慢的,刚刚好在删除之后,那缓存数据就还是脏数据?是的,这个时候一般靠第二次删除延迟来处理,延迟删除。
    • 这个时候肯定有人问,那要是删除失败了怎么办?
      • 直接补偿重试
      • 消息队列,异步重试
      • 基于 mysql binlog 增量订阅消费补偿

这个问题我们在这个分布式缓存的里面就不详细聊了,之后单独聊这个话题,串行化是我们最后的倔强,但是高并发就难了,所以我们一般是保证最终一致性即可。

6.2 2、缓存穿透

缓存穿透是指,缓存和数据库都没有的数据,被大量请求,比如订单号不可能为 -1,但是用户请求了大量订单号为 -1 的数据,由于数据不存在,缓存就也不会存在该数据,所有的请求都会直接穿透到数据库。
如果被恶意用户利用,疯狂请求不存在的数据,就会导致数据库压力过大,甚至垮掉。
注意:穿透的意思是,都没有,直接一路打到数据库。

那对于这种情况,我们该如何解决呢?

  1. 接口增加业务层级的Filter,进行合法校验,这可以有效拦截大部分不合法的请求。

  2. 作为第一点的补充,最常见的是使用布隆过滤器,针对一个或者多个维度,把可能存在的数据值 hash 到 bitmap 中,bitmap 证明该数据不存在则该数据一定不存在,但是 bitmap 证明该数据存在也只能是可能存在,因为不同的数值 hash 到的 bit 位很有可能是一样的,hash 冲突会导致误判,多个 hash 方法也只能是降低冲突的概率,无法做到避免。

  3. 另外一个常见的方法,则是针对数据库与缓存都没有的数据,对空的结果进行缓存,但是过期时间设置得较短,一般五分钟内。而这种数据,如果数据库有写入,或者更新,必须同时刷新缓存,否则会导致不一致的问题存在。

6.3 3、缓存雪崩

缓存雪崩是指缓存中有大量的数据,在同一个时间点,或者较短的时间段内,全部过期了,这个时候请求过来,缓存没有数据,都会请求数据库,则数据库的压力就会突增,扛不住就会宕机。
针对这种情况,一般我们都是使用以下方案:

  1. 如果是热点数据,先预热,而且可以考虑设置永远不过期。
  2. 缓存的过期时间除非比较严格,要不考虑设置一个波动随机值,比如理论十分钟,那这类key的缓存时间都加上一个13分钟,过期时间在713分钟内波动,有效防止都在同一个时间点上大量过期。
  3. 方法1避免了有效过期的情况,但是要是所有的热点数据在一台redis服务器上,也是极其危险的,如果网络有问题,或者redis服务器挂了,那么所有的热点数据也会雪崩(查询不到),因此将热点数据打散分不到不同的机房中,也可以有效减少这种情况。
  4. 也可以考虑双缓存的方式,数据库数据同步到缓存 A 和 B,A 设置过期时间,B 不设置过期时间,如果 A 为空的时候去读 B,同时异步去更新缓存,但是更新的时候需要同时更新两个缓存。
  5. 使用缓存组件时,可以设置为异步回源,或者允许读取未物理删除的数据。

比如设置产品的缓存时间:


redis.set(id,value,60*60 + Math.random()*1000);

6.4 4、缓存击穿

缓存击穿是指数据库原本有得数据,但是缓存中没有,一般是缓存突然失效了,这时候如果有大量用户请求该数据,缓存没有则会去数据库请求,会引发数据库压力增大,可能会瞬间打垮。

针对这类问题,一般有以下做法:

  1. 如果是热点数据,那么可以考虑设置永远不过期。
  2. 如果数据一定会过期,那么就需要在数据为空的时候,设置一个互斥的锁,只让一个请求通过,只有一个请求去数据库拉取数据,取完数据,不管如何都需要释放锁,异常的时候也需要释放锁,要不其他线程会一直拿不到锁。

下面是缓存击穿的时候互斥锁的写法,注意:获取锁之后操作,不管成功或者失败,都应该释放锁,而其他的请求,如果没有获取到锁,应该等待,再重试。当然,如果是需要更加全面一点,应该加上一个等待次数,比如1s中,那么也就是睡眠五次,达到这个阈值,则直接返回空,不应该过度消耗机器,以免当个不可用的场景把整个应用的服务器带挂了。

    public static String getProductDescById(String id) {String desc = redis.get(id);// 缓存为空,过期了if (desc == null) {// 互斥锁,只有一个请求可以成功if (redis.setnx(lock_id, 1, 60) == 1) {try {// 从数据库取出数据desc = getFromDB(id);redis.set(id, desc, 60 * 60 * 24);} catch (Exception ex) {LogHelper.error(ex);} finally {// 确保最后删除,释放锁redis.del(lock_id);return desc;}} else {// 否则睡眠200ms,接着获取锁Thread.sleep(200);return getProductDescById(id);}}}

6.5 5、缓存热点

像微博这种,有些热点新闻,突然爆了,大量用户访问同一个 key,key 在同一个缓存节点,很容易就过载,节点会卡顿甚至挂掉,这种我们就叫缓存热点。

解决方案一般是通过实时数据流比如 Spark ,分析热点 Key ,一般都有一个增长的过程,然后在 Key 后面加上一些随机的编号,比如明星出轨_01, 明星出轨_02…,目的是让这些 key 分布在不同的机器上,而客户端获取的时候,带上随机的 key,随机访问一个就可以。

想要探测热 Key,除了实时数据流,也可以在 redis 之上的 proxy 上面做,一般我们在公司都不是直接连接 redis ,而是连接的 proxy,因此我们也可以通过在 proxy 中使用滑动时间窗口,对每个 key 进行计数,超过一定的阈值,就设置为热 key。

那如何快速针对热 key 进行动态处理呢?弄一个独立的缓存数据服务,根据流量来动态拆分热 key,动态的增长成为热 key 我们可以通过分析发现,但是如果是秒杀等业务呢?需要支持实时拆分热 key,用分布式配置中心来配置热 key,感知到配置热 key 则进行需要的处理,这里因业务而异,可以降级成读取本地内存,可以进行拆分等等。

当然,如果能够正对秒杀等活动,或者大促活动,拉出独立的集群进行路由,隔离影响,那也是一种方案。

这是京东的处理方案: https://gitee.com/jd-platform-opensource/hotkey ,对任意突发性的无法预先感知的热点请求,包括并不限于热点数据(如突发大量请求同一个商品)、热用户(如爬虫、刷子)、热接口(突发海量请求同一个接口)等,进行毫秒级精准探测到。 然后对这些热数据、热用户等,推送到该应用部署的所有机器JVM内存中,以大幅减轻对后端数据存储层的冲击,并可以由客户端决定如何使用这些热key(譬如对热商品做本地缓存、对热用户进行拒绝访问、对热接口进行熔断或返回默认值)。 这些热key在整个应用集群内保持一致性。

6.6 6、缓存大 Key

缓存大 key 是指缓存的值 value 特别大,如果同一时间大量请求访问了同一个大 key,带宽很容易被占满,其他请求进不来。

大 key 定义参考如下:

  • string类型的key超过10KB
  • hash/set/zset/list 等数据结构中元素个数大于 5k/整体占用内存大于 10MB

如何判断是不是大 key,一般看网络的出流量,如果突增特别厉害,但是入流量变化不大的情况下,基本可以判断为大 key

  • 事前我们可以在代码 review 的时候就得判断 value 是不是特别大,不能写这种代码。或者封装一层 redis 操作切面,异步对 key 的 value 做监控,进行打点告警。
  • 其次,写代码的时候如果发现要 set 这种大的 value 值,那就得想办法拆分,把对象拆成属性,或者按照属性分类。如果是一个不可分割的整体,那就得考虑一下技术方案是不是要推翻重来了,一般我们不太可能把几 M 的图片直接二进制存 redis。
  • dump RDB 数据,进行离线数据分析,给出告警,但是不够实时。
  • Redis 提供了 bigkeys 参数能够使 redis-cli 以遍历的方式分析 Redis 实例中的所有 Key,并返回 Key 的整体统计信息与每个数据类型中 Top1 的大 Key,bigkeys 仅能分析并输入六种数据类型(STRING LISTHASHSETZSETSTREAM), 命令示例为 redis-cli -h 127.0.0.1 -p 6379 --bigkeys

7 总结

缓存不是银弹,是一把刀,用得好,可以乱杀(夸大),用不好,得包扎(一点不夸大,得提桶跑路那种)。

相关文章:

从单机缓存到分布式缓存那些事

作者:秦怀 1 缓存前世今生 1.1 故事从硬件开始 Cache 一词来源于 1967 年的一篇电子工程期刊论文。其作者将法语词“cache”赋予“safekeeping storage”的涵义,用于电脑工程领域。当时没有 Cache,CPU 和内存都很慢,CPU 直接访…...

华为新手机和支付宝碰一下 带来更便捷支付体验

支付正在变的更简单。 11月26日,华为新品发布会引起众多关注。发布会上,华为常务董事余承东专门提到,华为Mate 70和Mate X6折叠屏手机的“独门支付秘技”——“碰一下”,并且表示经过华为和支付宝的共同优化,使用“碰…...

element ui select绑定的值是对象的属性时,显示异常.

需要声明 value-key"value",如果还不行可能是数据类型不一致数字0和字符串0是不一致的. el-select v-model"value" clearable placeholder"Select" value-key"value" style"width: 240px"><!-- <el-option v-for&…...

基于Springboot开发的时光兼职网

一、功能介绍 时光兼职网包含管理员、用户、商家三个角色以及前后台系统。 前台系统功能 首页、兼职信息推荐、查看更多等 职位申请、申请日期、上传简历、点击下载简历、留言反馈等 个人中心、上传图片、更新信息等 后台系统功能 用户登录&#xff1a; 个人中心、修改密码…...

Vue3 Ts 如何获取组件的类型

vue3 Ts ref 子组件 1、默认写法 typeof&#xff1a;获取ts类型 InstanceType&#xff1a;获取模版的实例 <tempolate><myComponent ref"myCompRef"> </tempolate><script setup lang"ts"> import { ref } from "vue&quo…...

Unity类银河战士恶魔城学习总结(P146 Delete Save file-P147 Encryption of save data删除数据和加密数据)

【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili 教程源地址&#xff1a;https://www.udemy.com/course/2d-rpg-alexdev/ 本章节实现了快速删除存档和加密存档 以下是加密前和加密后的对比 SaveManager.cs using System.Collections; using System.Collection…...

Uniapp 使用自定义字体

技术栈&#xff1a;Uniapp 简介 为了更好的还原UI图片效果&#xff0c;往往需要使用特殊字体&#xff0c;引入字体包。 因实际业务运行平台在微信小程序上&#xff0c;对发布包的项目文件大小有限制&#xff0c;项目中某些比较大的静态资源需要放在服务器上来远程加载&#x…...

Scala

统计成绩练习 1.计算每个同学的总分和平均分 2.统计每个科目的平均分 3.列出总分前三名和单科前三名&#xff0c;并保存结果到文件中 解题思路如下&#xff1a; 1.读入txt文件&#xff0c;按行读入 2.处理数据 &#xff08;1&#xff09;计算每个同学的总分…...

fnOS中安装HAOS,集成haier

只作为自己记录重要事项&#xff0c;不做详细教程。大致流程 安装飞牛OS&#xff0c;简称fnosfnos中有集成Docker在docker中安装haos在haos中安装hacs在hacs中添加haier 在docker中安装haos 安装好fnos后&#xff0c;docker里面找到haos&#xff0c;里面下载最多的&#xff0c…...

基于群晖搭建个人图书架-TaleBook based on Docker

前言 在群晖Container Manager中部署失败&#xff0c;转通过ssh部署。 一、准备工作 名称备注群晖SSH“终端机和SNMP”中启用SSH软件secureCRT等docker-compose.ymlGithub下载并修改 二、过程 2.1 创建本地文件夹 本地路径为&#xff1a; /docker/Calibre/data 2.2 下载d…...

spring导出多个文件,要求打包成压缩包

背景 业务要求我们批量生成一批excel&#xff0c;并将这些excel压缩成一个压缩包导出给前端。 实现 java自带了ZipOutputStream&#xff0c;可以直接生成压缩包&#xff0c;因此&#xff0c;我们直接使用这个&#xff0c;在内存中生成压缩包&#xff0c;直接返回给前端。&am…...

Vue 3中实现多个自定义组件之间的切换

在 Vue 3 中&#xff0c;如果你想在 HTML 页面中实现多个自定义组件之间的切换&#xff0c;你可以使用 Vue 的条件渲染功能&#xff0c;比如 v-if、v-else-if 和 v-else 指令&#xff0c;或者使用 <component> 标签结合 is 属性来动态绑定组件。 1. 打开HBuilder X 图1 …...

opengl 三角形

最后效果&#xff1a; OpenGL version: 4.1 Metal 不知道为啥必须使用VAO 才行。 #include <glad/glad.h> #include <GLFW/glfw3.h>#include <iostream> #include <vector>void framebuffer_size_callback(GLFWwindow *window, int width, int heigh…...

shell脚本练习(2)

1. 使用case实现成绩优良差的判断 2. for创建20用户 用户前缀由用户输入 用户初始密码由用户输入 例如&#xff1a;test01,test10 3. for ping测试指网段的主机 网段由用户输入&#xff0c;例如用户输入192.168.2 &#xff0c;则ping 192.168.2.10 --- 192.168.2.2…...

JS数组的一些方法

前言 忘了在哪里听说JS是用来处理各种各样的数据的&#xff0c;所以掌握一些数组的处理方法极其重要 而最近学校要进行测试&#xff0c;本着复习回顾的想法&#xff0c;决定将一些我所知道的数组处理方法整理整理 不过难免有遗漏与错误&#xff0c;还望各位大佬指正 forEac…...

学习threejs,使用CubeCamera相机创建反光效果

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️CubeCamera 立方体相机 二、…...

#渗透测试#红蓝攻防#HW#漏洞挖掘#漏洞复现01-笑脸漏洞(vsftpd)

免责声明 本教程仅为合法的教学目的而准备&#xff0c;严禁用于任何形式的违法犯罪活动及其他商业行为&#xff0c;在使用本教程前&#xff0c;您应确保该行为符合当地的法律法规&#xff0c;继续阅读即表示您需自行承担所有操作的后果&#xff0c;如有异议&#xff0c;请立即停…...

使用nginx请求转发时前端报跨域问题解决

当其他接口都没有问题&#xff0c;后端也进行了跨域的配置时&#xff0c;此时问题应该就出现在nginx中 我发现当上传文件大小小于1m时并不会发生错误&#xff0c;所以我们应该配置一下nginx允许上传文件的大小 在nginx.conf中添加 在nginx目录下重启nginx即可 &#xff08;Wi…...

贪心算法入门(二)

第1题 越野跑 查看测评数据信息 为了能在下一次跑步比赛中有好的发挥&#xff0c;桐桐在一条山路上开始了她的训练 。桐桐希望能在每次训练中跑得尽可能远&#xff0c;不过她也知道农场中的一条规定&#xff1a;独自进山的时间不得超过M秒(1 < M < 10,000,000)。 整条…...

欧拉函数——acwing

题目一&#xff1a;欧拉函数 873. 欧拉函数 - AcWing题库 分析&#xff08;欧拉函数相关知识点&#xff09; 互质数不了解可以参考之前笔记&#xff0c;以便更好了解&#xff1a; 数论—快速幂&#xff0c;欧几里得及其扩展&#xff0c;逆元&#xff0c;单位元_数论单位元函…...

Spring集成Mybatis的实现

实现步骤大纲 第一步&#xff1a;准备数据库表 使用t_act表&#xff08;账户表&#xff09; 第二步&#xff1a;IDEA中创建一个模块&#xff0c;并引入依赖 spring-contextspring-jdbcmysql驱动mybatismybatis-spring&#xff1a;mybatis提供的与spring框架集成的依赖德鲁伊连…...

Redis中的分布式锁(步步为营)

分布式锁 概述 分布式锁指的是&#xff0c;所有服务中的所有线程都去获取同一把锁&#xff0c;但只有一个线程可以成功的获得锁&#xff0c;其他没有获得锁的线程必须全部等待&#xff0c;直到持有锁的线程释放锁。 分布式锁是可以跨越多个实例&#xff0c;多个进程的锁 分布…...

Linux下的三种 IO 复用

目录 一、Select 1、函数 API 2、使用限制 3、使用 Demo 二、Poll 三、epoll 0、 实现原理 1、函数 API 2、简单代码模板 3、LT/ET 使用过程 &#xff08;1&#xff09;LT 水平触发 &#xff08;2&#xff09;ET边沿触发 4、使用 Demo 四、参考链接 一、Select 在…...

微服务即时通讯系统的实现(服务端)----(2)

目录 1. 语音识别子服务的实现1.1 功能设计1.2 模块划分1.3 模块功能示意图1.4 接口的实现 2. 文件存储子服务的实现2.1 功能设计2.2 模块划分2.3 模块功能示意图2.4 接口的实现 3. 用户管理子服务的实现3.1 功能设计3.2 模块划分3.3 功能模块示意图3.4 数据管理3.4.1 关系数据…...

数据库原理-期末复习基础知识第二弹

1、数据的逻辑独立性是指 外模式/模式映像 当模式改变的时候&#xff0c;由数据库管理员对各个外模式/模式的映像做出相应改变&#xff0c;使外模式保持不变。由于应用程序是按照外模式进行编写的&#xff0c;故应用程序不必修改&#xff0c;保证了数据与程序的逻辑独立性。 …...

智能云在线编辑网站(完结篇)

开始及初步计划 1.前端tiptip编辑器框架vue3 2.后端Pythonflaskmysql 3.大模型调用&#xff1a;飞桨系列&#xff08;ppasr&#xff09; 前言&#xff1a;以此篇谨记从软件杯到天津生成式ai答辩过程及结束。 『如蚍蜉见青天&#xff0c;双肩难挑日月』&#xff0c;感叹世事多…...

多源传感器构建机器人的Gazebo模型

构建包含GNSS、IMU、LiDAR、Camera传感器的Gazebo模型涉及多个步骤&#xff0c;包括设置工作环境、创建URDF文件、安装必要的Gazebo插件和依赖项。以下是一个详细的步骤指南&#xff0c;帮助你开始构建这个Gazebo模型。 1. 设置工作环境 首先&#xff0c;确保你已经安装了ROS…...

linux中top 命令返回数据解释

当您在 Linux 终端中运行 top 命令时,它会显示一个动态更新的系统状态视图,其中包括许多有关系统性能的数据。下面是对 top 命令返回数据的详细解释: 标题栏 top - 22:46:12 up 2 days, 3:14, 1 user, load average: 0.05, 0.07, 0.09 22:46:12:当前时间。up 2 days, 3:14…...

【Vue3】【Naive UI】<NDropdown>标签

【Vue3】【Naive UI】 标签 基本设置自定义渲染交互事件其他属性 【VUE3】【Naive UI】&#xff1c;NCard&#xff1e; 标签 【VUE3】【Naive UI】&#xff1c;n-button&#xff1e; 标签 【VUE3】【Naive UI】&#xff1c;a&#xff1e; 标签 【VUE3】【Naive UI】&#xff1c…...

ADS学习笔记 7. 超外差接收机设计

基于ADS2023 update2 更多ADS学习笔记&#xff1a;ADS学习笔记 1. 功率放大器设计ADS学习笔记 2. 低噪声放大器设计ADS学习笔记 3. 功分器设计ADS学习笔记 4. 微带分支定向耦合器设计ADS学习笔记 5. 微带天线设计ADS学习笔记 6. 射频发射机设计 目录 -1、射频接收机性能指标…...

新型大语言模型的预训练与后训练范式,阿里Qwen

前言&#xff1a;大型语言模型&#xff08;LLMs&#xff09;的发展历程可以说是非常长&#xff0c;从早期的GPT模型一路走到了今天这些复杂的、公开权重的大型语言模型。最初&#xff0c;LLM的训练过程只关注预训练&#xff0c;但后来逐步扩展到了包括预训练和后训练在内的完整…...

k8s 1.28 二进制安装与部署

第一步 &#xff1a;配置Linux服务器 #借助梯子工具 192.168.196.100 1C8G kube-apiserver、kube-controller-manager、kube-scheduler、etcd、kubectl、haproxy、keepalived 192.168.196.101 1C8G kube-apiserver、kube-controller-manager、kube-scheduler、etcd、kubectl、…...

Ubuntu 常用解压与压缩命令

.zip文件 unzip FileName.zip # 解压 zip DirName.zip DirName # 将DirName本身压缩 zip -r DirName.zip DirName # 压缩&#xff0c;递归处理&#xff0c;将指定目录下的所有文件和子目录一起压缩 zip DirName.zip DirName 行为&#xff1a; 只压缩 DirName 目录本身&#xff…...

使用ECharts创建带百分比标注的环形图

在数据可视化领域&#xff0c;环形图是一种非常有效的图表类型&#xff0c;它能够清晰地展示各部分与整体的关系。今天&#xff0c;我们将通过ECharts来创建一个带百分比标注的环形图&#xff0c;并详细解释如何实现这一效果。 1. 数据准备 首先&#xff0c;我们定义了一些基础…...

lvs虚拟服务器之LVS-NAT模式

一.集群 二.LVS:虚拟服务器:工作在传输层,解决高并发 三.LVS-NAT一.集群1.概念:集群就是一组计算机集群核心&#xff1a;任务调度集群目的提高性能,降低成本,提高可扩展性,增强可靠性集群分类HA&#xff1a;高可用集群&#xff08;High Availability Cluster&#xff09;:避免单…...

虚拟机添加硬盘驱动,Windows 系统添加 VirtIO 驱动(Windows ISO 安装镜像添加驱动)

为什么要在 ISO 镜像里添加驱动&#xff1f;而不是在进系统以后装驱动&#xff1f;一切都是形势所迫。如果你也是爱折腾的人&#xff0c;那么这也会成为一个有用的方案之一。   最近&#xff0c;因为在给公司研究部署 OpenStack&#xff0c;在制作初始镜像&#xff08;也就是…...

20241128解决Ubuntu20.04安装libesd0-dev异常的问题

20241128解决Ubuntu20.04安装libesd0-dev异常的问题 2024/11/28 16:36 缘起&#xff1a;中科创达的高通CM6125开发板的Android10的编译环境需要。 安装异常&#xff1a;rootrootrootroot-X99-Turbo:~$ rootrootrootroot-X99-Turbo:~$ sudo apt-get install libesd0-dev Readi…...

Linux命令进阶·如何切换root以及回退、sudo命令、用户/用户组管理,以及解决创建用户不显示问题和Ubuntu不显示用户名只显示“$“符号问题

目录 1. root用户&#xff08;超级管理员&#xff09; 1.1 用于账户切换的系统命令——su 1.2 退回上一个用户命令——exit 1.3 普通命令临时授权root身份执行——sudo 1.3.1 为普通用户配置sudo认证 2. 用户/用户组管理 2.1 用户组管理 2.2 用户管理 2.2.1 …...

基于链表的基础笔试/面试题

1. 反转链表 问题描述&#xff1a;反转一个单向链表。 示例&#xff1a; 输入&#xff1a;1 → 2 → 3 → 4 → 5 输出&#xff1a;5 → 4 → 3 → 2 → 1 class ListNode {int val;ListNode next;ListNode(int x) {val x;} }public class LinkedList {public ListNode …...

文件比较和文件流

文件比较和文件流 一、文本比较工具 diff1.基本用法1.1输出格式 2.常用选项 二、文件流1.文件的打开模式2.文件流的分类ifstreamofstreamfstrem区别 3.文件流的函数1. 构造函数2. is_open 用于判断文件是否打开3. open4. getline5. close6. get()7. read8. write9. put10. gcou…...

unity如何让一个物体拥有按钮功能

在 Unity 中&#xff0c;要让一个物体&#xff08;例如一个 3D 模型、UI 元素或其他对象&#xff09;变成一个按钮&#xff0c;你需要为它添加交互功能。这通常意味着让物体能够响应点击事件&#xff0c;像 UI 按钮那样触发某些行为。对于 3D 物体&#xff0c;可以通过 射线检测…...

【RISC-V CPU Debug 专栏 1 -- RISC-V debug 规范】

文章目录 RISC-V Debug调试用例支持的功能限制和不包括的内容RISC-V 调试架构的主要组件用户与调试主机调试翻译器调试传输硬件调试传输模块(DTM)调试模块(DM)调试功能触发模块版本介绍RISC-V Debug RISC-V 调试规范为 RISC-V 处理器提供了一套标准化的调试接口和功能,旨…...

【论文阅读】Federated learning backdoor attack detection with persistence diagram

目的&#xff1a;检测联邦学习环境下&#xff0c;上传上来的模型是不是恶意的。 1、将一个模型转换为|L|个PD,&#xff08;其中|L|为层数&#xff09; 如何将每一层转换成一个PD&#xff1f; 为了评估第&#x1d457;层的激活值&#xff0c;我们需要&#x1d450;个输入来获…...

IDEA Maven 打包找不到程序包错误或找不到符号,报错“程序包不存在“

参考文章&#xff1a;https://blog.csdn.net/yueeryuanyi/article/details/14211090 问题&#xff1a;IDEA Maven 打包找不到程序包错误或找不到符号,报错“程序包不存在“编译都没问题 解决思路 – >【清除缓存】 1. 强制刷新Maven缓存 选择 Maven 标签&#xff0c;Exe…...

MySQL数据库做题笔记

题目链接https://leetcode.cn/problems/invalid-tweets-ii/description/https://leetcode.cn/problems/invalid-tweets-ii/description/ # Write your MySQL query statement below SELECT tweet_id FROM Tweets where LENGTH(content)>140 OR (length(content)-length(rep…...

100个python经典面试题详解(新版)

应老粉要求,每晚加餐一个最新面试题 包括Python面试中常见的问题,涵盖列表、元组、字符串插值、比较操作符、装饰器、类与对象、函数调用方式、数据结构操作、序列化、数据处理函数等多个方面。 旨在帮助数据科学家和软件工程师准备面试或提升Python技能。 7、Python面试题…...

Leetcode3232:判断是否可以赢得数字游戏

题目描述&#xff1a; 给你一个 正整数 数组 nums。 Alice 和 Bob 正在玩游戏。在游戏中&#xff0c;Alice 可以从 nums 中选择所有个位数 或 所有两位数&#xff0c;剩余的数字归 Bob 所有。如果 Alice 所选数字之和 严格大于 Bob 的数字之和&#xff0c;则 Alice 获胜。 如…...

Python 爬虫实战基于 Class 的天气查询与反爬虫练习

需求&#xff1a; 要实现一个简单的天气查询爬虫&#xff0c;使用 requests 库来获取网页内容&#xff0c;使用 BeautifulSoup 来解析网页并提取天气信息。以下是一个基本示例&#xff0c;展示了如何抓取天气信息并输出当天的温度和天气状况。 以下是使用 class 类方式实现带有…...

C语言——库函数

常用的函数 https://cplusplus.com/reference/ 没事儿多看看 1 数学函数 #include <math.h> #include <stdio.h> int main() {printf("%lf\n", sqrt(4));//开平方根——>double类型printf("%lf\n", pow(2, 10));//求几次方的——>do…...

软件测试丨Pytest 第三方插件与 Hook 函数

Pytest不仅是一个用于编写简单和复杂测试的框架&#xff0c;还有大量的第三方插件以及灵活的Hook函数供我们使用&#xff0c;这些功能大大增强了其在软件测试中的应用。通过使用Pytest&#xff0c;测试开发变得简便、安全、高效&#xff0c;同时也能帮助我们更快地修复Bug&…...