Java实现本地缓存、分布式缓存及多级缓存

news2025/7/18 4:14:51

以下均为自己参考其它博主文章或自己理解整理而成,如有错误之处,欢迎在评论区批评指正!

0. 缓存简介

       像MySql等传统的关系型数据库已经不能适用于所有的业务场景,比如电商系统的秒杀场景,APP首页的访问流量高峰场景,很容易造成关系型数据库的瘫痪,随着缓存技术的出现很好的解决了这个问题。

0.1 什么是缓存?

       就是把访问量较高的热点数据从传统的关系型数据库中加载到内存中,当用户再次访问热点数据时是从内存中加载,减少了对数据库的访问量,解决了高并发场景下容易造成数据库宕机的问题。

0.2 为什么要用缓存?

       针对于这个问题要从两个方面去考虑,一个是应用系统的高并发场景,另一个就是应用系统的高性能情况。

0.2.1 高性能情况

       用户第一次访问数据时,缓存中没有数据,要从数据库中获取数据,因为是从磁盘中拿数据,读取数据的过程比较慢。拿到数据后将数据存储在缓存中,用户第二次访问数据时,可以从缓存中直接获取,因为缓存是直接操作内存的,访问数据速度比较快。

img

0.2.2 高并发场景

       操作缓存能够承受的并发访问量是远远大于访问数据库的,比如redis,它的读的速度是110000次/s,写的速度是81000次/s。所以说将数据库中访问量高的数据存储到缓存中,用户请求的时候直接访问数据库,不必访问数据库,提高应用程序的并发量。

img

0.3 缓存分类

缓存基本上分为三类:本地缓存、分布式缓存、多级缓存。

       根据缓存和应用程序是否属于同一个进程,将缓存分为本地缓存和分布式缓存。基于本地缓存和分布式缓存都有各自的优点和缺点,后面又出现了多级缓存的概念。

0.3.1 本地缓存

本地缓存:是指和应用程序在同一个进程内的内存空间去存储数据,数据的读写都是在同一个进程内完成的。

优点:读取速度快,但是不能进行大数据量存储。

       本地缓存不需要远程网络请求去操作内存空间,没有额外的性能消耗,所以读取速度快。但是由于本地缓存占用了应用进程的内存空间,比如java进程的jvm内存空间,故不能进行大数据量存储。

缺点:

  • 应用程序集群部署时,会存在数据更新问题(数据更新不一致)

       本地缓存一般只能被同一个应用进程的程序访问,不能被其他应用程序进程访问。在单体应用集群部署时,如果数据库有数据需要更新,就要同步更新不同服务器节点上的本地缓存的数据来保证数据的一致性,但是这种操作的复杂度高,容易出错。可以基于redis的发布/订阅机制来实现各个部署节点的数据同步更新。

  • 数据会随着应用程序的重启而丢失

       因为本地缓存的数据是存储在应用进程的内存空间的,所以当应用进程重启时,本地缓存的数据会丢失。

实现:

  • 缓存存储的数据一般都是key-value键值对的数据结构,在java语言中,常用的字典实现包括 HashMap 和 ConcurretHashMap。

  • 除了上面说的实现方式以外,也可以用Guava、Ehcache以及Caffeine等封装好的工具包来实现本地缓存。

0.3.2 分布式缓存

分布式缓存:分布式缓存是独立部署的服务进程,并且和应用程序没有部署在同一台服务器上,所以是需要通过远程网络请求来完成分布式缓存的读写操作,并且分布式缓存主要应用在应用程序集群部署的环境下。

优点:

  • 支持大数据量存储

       分布式缓存是独立部署的进程,拥有自身独自的内存空间,不需要占用应用程序进程的内存空间,并且还支持横向扩展的集群方式部署,所以可以进行大数据量存储。

  • 数据不会随着应用程序重启而丢失

       分布式缓存和本地缓存不同,拥有自身独立的内存空间,不会受到应用程序进程重启的影响,在应用程序重启时,分布式缓存的存储数据仍然存在。

  • 数据集中存储,保证数据的一致性

       当应用程序采用集群方式部署时,集群的每个部署节点都有一个统一的分布式缓存进行数据的读写操作,所以不会存在像本地缓存中数据更新问题,保证了不同服务器节点的数据一致性。

  • 数据读写分离,高性能,高可用

       分布式缓存一般支持数据副本机制,实现读写分离,可以解决高并发场景中的数据读写性能问题。而且在多个缓存节点冗余存储数据,提高了缓存数据的可用性,避免某个缓存节点宕机导致数据不可用问题。

缺点:

  • 数据跨网络传输,读写性能不如本地缓存

       分布式缓存是一个独立的服务进程,并且和应用程序进程不在同一台机器上,所以数据的读写要通过远程网络请求,这样相对于本地缓存的数据读写,性能要低一些。

分布式缓存的实现:典型实现包括 MemCached 和 Redis。

0.3.3 多级缓存

       基于本地缓存和分布式缓存的优缺点,多级缓存应运而生,在实际的业务开发中一般也是采用多级缓存。注意:本地缓存一般存储更新频率低,访问频率高数据,分布式缓存一般存储更新频率很高的数据。

       多级缓存的请求流程:本地缓存作为一级缓存,分布式缓存作为二级缓存。当用户获取数据时,先从一级缓存中获取数据,如果一级缓存有数据则返回数据,否则从二级缓存中获取数据。如果二级缓存中有数据则更新一级缓存,然后将数据返回客户端。如果二级缓存没有数据则去数据库查询数据,然后更新二级缓存,接着再更新一级缓存,最后将数据返回给客户端。

img

多级缓存的实现:可以使用Guava或者Caffeine作为一级缓存,Redis作为二级缓存。

       注意:在应用程序集群部署时,如果数据库的数据有更新的情况,一级缓存的数据更新容易出现数据不一致的情况。因为是集群部署,多个部署节点实现一级缓存数据更新难度比较大,不过我们可以通过Redis的消息发布/订阅机制来实现多个节点缓存数据一致性问题。

1. 本地缓存详细介绍及具体实现

1.1 介绍

参考链接:本地缓存:为什么要用本地缓存?用它会有什么问题?_Gimtom的博客-CSDN博客_本地缓存

       对于缓存的作用不言而喻,可以提高查询效率,比去数据库查询的速度要快。项目中我们经常会使用Nosql数据库,如Redis等做缓存。但是对于数据量很小的,访问非常频繁的,我们也可以存在本地缓存中。

       在高性能的服务架构设计中,缓存是一个不可或缺的环节。在实际的项目中,我们通常会将一些热点数据存储到Redis或Memcached 这类缓存中间件中,只有当缓存的访问没有命中时再查询数据库。在提升访问速度的同时,也能降低数据库的压力。

       随着不断的发展,这一架构也产生了改进,在一些场景下可能单纯使用Redis类的远程缓存已经不够了,还需要进一步配合本地缓存使用,例如Guava cache或Caffeine,从而再次提升程序的响应速度与服务性能。于是,就产生了使用本地缓存作为一级缓存,再加上远程缓存作为二级缓存的两级缓存架构。

       在先不考虑并发等复杂问题的情况下,两级缓存的访问流程可以用下面这张图来表示:

在这里插入图片描述

本地缓存:在客户端本地的物理内存中划出一部分空间来缓存客户端回写到服务器的数据,当本地回写缓存达到缓存阈值时,将数据写入到服务器中。

特点:

  • 本地缓存是基于本地环境的内存,访问速度非常快,对于一些变更频率低、实时性要求低的数据,可以放在本地缓存中,提升访问速度。

  • 使用本地缓存能够减少和Redis类的远程缓存间的数据交互,减少网络I/O开销,降低这一过程中在网络通信上的耗时。

  • 本地回写缓存功能可以在很大程度上降低服务器读写能力和网络负载,一般将本地缓存作为一级缓存,远程缓存作为二级缓存。

本地缓存应该具有的功能:

  1. 超过最大限制有对应淘汰策略,如LRU、LFU

  2. 过期时间淘汰,如定时、懒式、定期;

  3. 持久化

  4. 统计监控

1.2 本地缓存方案选型

1.2.1 使用ConcurrentHashMap实现本地缓存

       缓存的本质就是存储在内存中的KV数据结构,对应的就是jdk中线程安全的ConcurrentHashMap,但是要实现缓存,还需要考虑淘汰、最大限制、缓存过期时间淘汰等等功能。

优点:实现简单,不需要引入第三方包,比较适合一些简单的业务场景。缺点是如果需要更多的特性,需要定制化开发,成本会比较高,并且稳定性和可靠性也难以保障。对于比较复杂的场景,建议使用比较稳定的开源工具。

1.2.2 基于Guava Cache实现本地缓存

       Guava是Google团队开源的一款 Java 核心增强库,包含集合、并发原语、缓存、IO、反射等工具箱,性能和稳定性上都有保障,应用十分广泛。Guava Cache支持很多特性:

  • 支持最大容量限制

  • 支持两种过期删除策略(插入时间和访问时间)

  • 支持简单的统计功能

  • 基于LRU算法实现

引入依赖如下:

  <dependency>
     <groupId>com.google.guava</groupId>
     <artifactId>guava</artifactId>
     <version>31.1-jre</version>
 </dependency>

测试代码:

 @Slf4j
 public class GuavaCacheTest {
     public static void main(String[] args) throws ExecutionException {
         Cache<String, String> cache = CacheBuilder.newBuilder()
                 .initialCapacity(5)  // 初始容量
                 .maximumSize(10)     // 最大缓存数,超出淘汰
                 .expireAfterWrite(60, TimeUnit.SECONDS) // 过期时间
                 .build();
 ​
         String orderId = String.valueOf(123456789);
         // 获取orderInfo,如果key不存在,callable中调用getInfo方法返回数据
         String orderInfo = cache.get(orderId, () -> getInfo(orderId));
         log.info("orderInfo = {}", orderInfo);
 ​
     }
 ​
     private static String getInfo(String orderId) {
         String info = "";
         // 先查询redis缓存
         log.info("get data from redis");
 ​
         // 当redis缓存不存在查db
         log.info("get data from mysql");
         info = String.format("{orderId=%s}", orderId);
         return info;
     }
 }

1.2.3 基于Caffeine实现本地缓存

       Caffeine是基于java8实现的新一代缓存工具,缓存性能接近理论最优,可以看作是Guava Cache的增强版,功能上两者类似,不同的是Caffeine采用了一种结合LRU、LFU优点的算法:W-TinyLFU,在性能上有明显的优越性。

引入依赖如下:

 <dependency>
     <groupId>com.github.ben-manes.caffeine</groupId>
     <artifactId>caffeine</artifactId>
     <version>2.9.3</version>
 </dependency>

测试代码如下:

 @Slf4j
 public class CaffeineTest {
     public static void main(String[] args) {
         Cache<String, String> cache = Caffeine.newBuilder()
                 .initialCapacity(5)
                 // 超出时淘汰
                 .maximumSize(10)
                 //设置写缓存后n秒钟过期
                 .expireAfterWrite(60, TimeUnit.SECONDS)
                 //设置读写缓存后n秒钟过期,实际很少用到,类似于expireAfterWrite
                 //.expireAfterAccess(17, TimeUnit.SECONDS)
                 .build();
 ​
         String orderId = String.valueOf(123456789);
         String orderInfo = cache.get(orderId, key -> getInfo(key));
         System.out.println(orderInfo);
     }
 ​
     private static String getInfo(String orderId) {
         String info = "";
         // 先查询redis缓存
         log.info("get data from redis");
 ​
         // 当redis缓存不存在查db
         log.info("get data from mysql");
         info = String.format("{orderId=%s}", orderId);
         return info;
     }
 }

1.2.4 基于Encache实现本地缓存

       Encache是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider。同Caffeine和Guava Cache相比,Encache的功能更加丰富,扩展性更强。

优点:

  • 支持多种缓存淘汰算法,包括LRU、LFU和FIFO

  • 缓存支持堆内存储、堆外存储、磁盘存储(支持持久化)三种

  • 支持多种集群方案,解决数据共享问题

引入依赖如下:

  <dependency>
     <groupId>org.ehcache</groupId>
     <artifactId>ehcache</artifactId>
     <version>3.9.7</version>
 </dependency>

测试代码如下:

 @Slf4j
 public class EhcacheTest {
     private static final String ORDER_CACHE = "orderCache";
     public static void main(String[] args) {
         CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
                 // 创建cache实例
                 .withCache(ORDER_CACHE, CacheConfigurationBuilder
                         // 声明一个容量为20的堆内缓存
                         .newCacheConfigurationBuilder(String.class, String.class, ResourcePoolsBuilder.heap(20)))
                 .build(true);
         // 获取cache实例
         Cache<String, String> cache = cacheManager.getCache(ORDER_CACHE, String.class, String.class);
 ​
         String orderId = String.valueOf(123456789);
         String orderInfo = cache.get(orderId);
         if (StrUtil.isBlank(orderInfo)) {
             orderInfo = getInfo(orderId);
             cache.put(orderId, orderInfo);
         }
         log.info("orderInfo = {}", orderInfo);
     }
 ​
     private static String getInfo(String orderId) {
         String info = "";
         // 先查询redis缓存
         log.info("get data from redis");
 ​
         // 当redis缓存不存在查db
         log.info("get data from mysql");
         info = String.format("{orderId=%s}", orderId);
         return info;
     }
 }

1.2.5 选型方案对比

  • 从易用性角度,Guava Cache、Caffeine和Encache都有十分成熟的接入方案,使用简单。

  • 从功能性角度,Guava Cache和Caffeine功能类似,都是只支持堆内缓存,Encache相比功能更为丰富。

  • 从性能上进行比较,Caffeine最优、GuavaCache次之,Encache最差(下图是三者的性能对比结果)。

在这里插入图片描述

       对于本地缓存的方案中,我比较推荐Caffeine,性能上遥遥领先。虽然Encache功能更为丰富,甚至提供了持久化和集群的功能,但是这些功能完全可以依靠其他方式实现。真实的业务工程中,建议使用Caffeine作为本地缓存,另外使用redis或者memcache作为分布式缓存,构造多级缓存体系,保证性能和可靠性。

1.2 本地缓存问题及解决

1.2.1 缓存一致性

       两级缓存与数据库的数据要保持一致,一旦数据发生了修改,在修改数据库的同时,本地缓存、远程缓存应该同步更新。

1.2.1.1 解决方案1: MQ

       一般现在部署都是集群部署,有多个不同节点的本地缓存,可以使用MQ的广播模式,当数据修改时向MQ发送消息,节点监听并消费消息,删除本地缓存,达到最终一致性。 在这里插入图片描述

1.2.1.2 解决方案2:Canal + MQ

       如果你不想在你的业务代码发送MQ消息,还可以适用近几年比较流行的方法:订阅数据库变更日志,再操作缓存。Canal 订阅Mysql的 Binlog日志,当发生变化时向MQ发送消息,进而也实现数据一致性。

在这里插入图片描述

1.2.2 如何提供本地缓存命中率

待完善。。。。。。

1.3 本地缓存详细代码实现过程

1.3.1 基于ConcurrentHashMap实现的本地缓存

参考链接:Java实现本地缓存_柳落青的博客-CSDN博客_java 本地缓存

       本地缓存一般使用键值对方式的存储,那么在Java中肯定是选用Map,由于concurrentHashMap的线程安全性,所以就选择了这个。过期策略采用的定时清除,实现方式可以后台启动一个线程去扫,也可以用定时器,本例子使用的是定时器。

 package localcache;
 ​
 import java.util.Map;
 import java.util.Timer;
 import java.util.TimerTask;
 import java.util.concurrent.ConcurrentHashMap;
 ​
 /**
  * 基于ConcurrentHashMap定义的本地缓存工具类
  */
 public class ConcurrentHashMapLocalCacheUtil {
     // 默认大小
     private static final int DEFAULT_CAPACITY = 1024;
 ​
     // 最大缓存大小
     private static final int MAX_CAPACITY = 10000;
 ​
     // 默认缓存过期时间
     private static final long DEFAULT_TIMEOUT = 3600;
 ​
     // 1000毫秒
     private static final long SECOND_TIME = 1000;
 ​
     // 存储缓存的Map
     private static final ConcurrentHashMap<String, Object> map;
 ​
     // 采用定时器进行定时
     private static final Timer timer;
 ​
     static {
         // 创建指定容量为DEFAULT_CAPACITY大小的ConcurrentHashMap
         map = new ConcurrentHashMap<>(DEFAULT_CAPACITY);
         // 新建定时器
         timer = new Timer();
     }
 ​
     // 私有化构造方法
     private ConcurrentHashMapLocalCacheUtil() {
 ​
     }
 ​
     /**
      * 利用定时器任务类实现的缓存任务清除类
      */
     static class ClearTask extends TimerTask {
         private String key;
 ​
         public ClearTask(String key) {
             this.key = key;
         }
 ​
         @Override
         public void run() {
             ConcurrentHashMapLocalCacheUtil.remove(key);
         }
     }
 ​
     //==================缓存的增删改查========================
     /**
      * 判断容量大小
      */
     public static boolean checkCapacity() {
         return map.size() < MAX_CAPACITY;
     }
 ​
     /**
      * 添加缓存
      */
     public static boolean put(String key, Object object) {
         // 添加前先检查缓存大小
         if (checkCapacity()) {
             // 有剩余空间时存入数据
             map.put(key, object);
             // 通过定时器指定任务的默认超时时间
             timer.schedule(new ClearTask(key), DEFAULT_TIMEOUT);
             return true;
         }
         return false;
     }
 ​
     /**
      * 可指定延时时间的添加缓存
      */
     public static boolean put(String key, Object object, int time_out) {
         // 添加前检查缓存大小
         if (checkCapacity()) {
             // 有剩余空间时存入数据
             map.put(key, object);
             // 通过定时器指定任务的超时时间
             timer.schedule(new ClearTask(key), time_out * SECOND_TIME);
             return true;
         }
         return false;
     }
 ​
     /**
      * 批量增加缓存
      */
     public static boolean put(Map<String, Object> batchMap, int time_out) {
         // 添加前检查缓存大小
         if (map.size() + batchMap.size() <= MAX_CAPACITY) {
             // 有剩余空间时存入数据
             map.putAll(map);
             // 为每个缓存添加任务及超时时间
             for (String key : batchMap.keySet()) {
                 timer.schedule(new ClearTask(key), time_out * SECOND_TIME);
             }
             return true;
         }
         return false;
     }
 ​
     /**
      * 删除缓存
      */
     public static void remove(String key) {
         map.remove(key);
     }
 ​
     /**
      * 清除所有缓存
      */
     public void clearAll() {
         // 判断缓存中是否还有数据
         if (map.size() > 0) {
             // 清空map
             map.clear();
         }
         // 终止定时器
         timer.cancel();
     }
 ​
     /**
      * 获取缓存
      */
     public static Object get(String key) {
         return map.get(key);
     }
 ​
     /**
      * 判断是否包含某个缓存
      */
     public static boolean isContain(String key) {
         return map.contains(key);
     }
 ​
 }
 ​

或者

参考链接:Java简单实现本地缓存-pudn.com

 package localcache;
 ​
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 ​
 /**
  * @ClassName SimpleCache
  * @Description Java简单实现本地缓存,注意:此代码不适合集群部署环境
  * @Author Jiangnan Cui
  * @Date 2022/9/25 20:16
  * @Version 1.0
  */
 public class SimpleCache {
 ​
     /**
      * 有效时间30分钟:30 * 60 * 1000
      */
     private static final long CACHE_HOLD_TIME_30M = 1800000L;
 ​
     /**
      * 有效时间key后缀
      */
     private static final String SECONDS = "_seconds";
 ​
     private static volatile SimpleCache cache;
 ​
     private static Map<String, Object> CACHE_MAP;
 ​
     private SimpleCache() {
         CACHE_MAP = new ConcurrentHashMap<>();
     }
 ​
     public static SimpleCache getInstance() {
         if (cache == null) {
             synchronized (SimpleCache.class) {
                 if (cache == null) {
                     cache = new SimpleCache();
                 }
             }
         }
         return cache;
     }
 ​
     /**
      * 存放一个缓存对象,默认保存时间30分钟
      *
      * @param cacheName 缓存名称
      * @param obj       缓存对象
      */
     public void put(String cacheName, Object obj) {
         put(cacheName, obj, CACHE_HOLD_TIME_30M);
     }
 ​
     /**
      * 存放一个缓存对象,保存时间为holdTime
      *
      * @param cacheName 缓存名称
      * @param obj       缓存对象
      * @param seconds   时间
      */
     public void put(String cacheName, Object obj, long seconds) {
         CACHE_MAP.put(cacheName, obj);
         // 设置缓存失效时间
         CACHE_MAP.put(cacheName + SECONDS,
                 System.currentTimeMillis() + seconds);
     }
 ​
     /**
      * 取出一个缓存对象
      *
      * @param cacheName 缓存名称
      * @return 缓存对象
      */
     public Object get(String cacheName) {
         if (checkCacheName(cacheName)) {
             return CACHE_MAP.get(cacheName);
         }
         return null;
     }
 ​
     /**
      * 删除某个缓存
      *
      * @param cacheName 缓存名称
      */
     public void remove(String cacheName) {
         CACHE_MAP.remove(cacheName);
         CACHE_MAP.remove(cacheName + SECONDS);
     }
 ​
     /**
      * 检查缓存对象是否存在,
      * 若不存在,则返回false
      * 若存在,检查其是否已过有效期,如果已经过了则删除该缓存并返回false
      *
      * @param cacheName 缓存名称
      * @return 缓存对象是否存在
      */
     public boolean checkCacheName(String cacheName) {
         Long seconds = (Long) CACHE_MAP.get(cacheName + SECONDS);
         if (seconds == null || seconds == 0L) {
             return false;
         }
         if (seconds < System.currentTimeMillis()) {
             remove(cacheName);
             return false;
         }
         return true;
     }
 }
 ​

注意:以上代码还未经过正式测试。

1.3.2 基于Guava Cache实现的本地缓存

待完善。。。。。。

1.3.3 基于Caffeine实现的本地缓存

待完善。。。。。。

1.3.4 基于Encache实现的本地缓存

待完善。。。。。。

2. 分布式缓存详细介绍及具体实现

待完善。。。。。。

3.多级缓存详细介绍及具体实现

待完善。。。。。。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

火爆全网的人工智能真的有那么强嘛?

&#x1f4cb; 个人简介 &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是阿牛&#xff0c;全栈领域优质创作者。&#x1f61c;&#x1f4dd; 个人主页&#xff1a;馆主阿牛&#x1f525;&#x1f389; 支持我&#xff1a;点赞&#x1f44d;收藏⭐️留言&#x1f4d…

Element-UI中的表单验证

Element是一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的桌面端组件库。   Element为vue-cli 准备了相应的 Element 插件&#xff0c;开发者可以用它们快速地搭建一个基于 Element 的项目&#xff0c;不仅能够快速体验交互细节&#xff0c;还可以使用前端框架封装的代…

Spring AOP 切面增强

背景背景&#xff1a;我们在开发过程中&#xff0c;经常需要做些周边功能&#xff1a; 性能统计、日志、事物管理。我们需要考虑如何解耦这些周边功能开发和核心业务开发区分开达到提升代码质量目的。定义在AOP思想里面定义周边功能定义是&#xff08;性能统计、日志、事务管理…

两种方案解决ERROR in Conflict: Multiple assets emit different content to the same filename index.html 的问题

Ⅰ、所遇问题简述&#xff1a; 1、问题描述&#xff1a; 在用 vue-cli 正常来创建新的基于 vue2 框架 的项目&#xff0c;在安装好 node-modules 之后&#xff0c;运行命令 ‘npm run serve’ ,报错情况如下&#xff1a; ERROR in Conflict: Multiple assets emit different…

用Pytorch构建第一个神经网络模型(附案例实战)

本文参加新星计划人工智能(Pytorch)赛道&#xff1a;https://bbs.csdn.net/topics/613989052 目录 一、Pytorch简介 二、实验过程 2.1数据集介绍 2.2加载数据 2.3数据预处理 2.3.1特征转换 2.3.2缺失值处理 2.3.3样本不平衡处理 2.4特征工程 2.4.1划分训练集和测试集 …

Unity 插件篇 ✨ | 受击插件Feel 详细教程,开发游戏时更好的操控 游戏打击感

&#x1f3ac; 博客主页&#xff1a;https://xiaoy.blog.csdn.net &#x1f3a5; 本文由 呆呆敲代码的小Y 原创&#xff0c;首发于 CSDN&#x1f649; &#x1f384; 学习专栏推荐&#xff1a;Unity系统学习专栏 &#x1f332; 游戏制作专栏推荐&#xff1a;游戏制作 &…

同态加密详解

什么是同态加密 同态加密&#xff08;Homomorphic Encryption&#xff09;是指将原始数据经过同态加密后&#xff0c;对得到的密文进行特定的运算&#xff0c;然后将计算结果再进行同态解密后得到的明文等价于原始明文数据直接进行相同计算所得到的数据结果。 同态加密与一般加…

与C知道的第一次亲密接触

chatG.P.T没向大陆地区开放服务&#xff0c;如要注册除了要邮箱&#xff0c;还要一个已开放服务国家的电话号来接受验证&#xff0c;于是果断放弃注册。还好&#xff0c;CSDN官网提供过网页版的类似聊天机器人&#xff0c;现只在CSDN APP上才有入口。而且现在改名为“C知道”&a…

MQ-2烟雾传感器的使用

一、MQ-2烟雾传感器简介 MQ-2烟雾传感器采用在清洁空气中电导率较低的二氧化锡(SnO2)&#xff0c;属于表面离子式N型半导体。当MQ-2烟雾传感器在200到300摄氏度环境时&#xff0c;二氧化锡吸附空气中的氧&#xff0c;形成氧的负离子吸附&#xff0c;使半导体中的电子密度减少&a…

学习操作系统的必备教科书《操作系统:原理与实现》| 文末赠书4本

使用了6年的实时操作系统&#xff0c;是时候梳理一下它的知识点了 摘要&#xff1a; 本文简单介绍了博主学习操作系统的心路历程&#xff0c;同时还给大家总结了一下当下流行的几种实时操作系统&#xff0c;以及在工程中OSAL应该如何设计。希望对大家有所启发和帮助。 文章目录…

技术瓶颈?如何解决MongoDB超大块数据问题?

目录一、MongoDB服务器管理1、添加服务器2、修改分片中的服务器3、删除分片二、均衡器三、修改块的大小四、超大块1、分发超大块2、分发超大块步骤&#xff1a;3、避免出现超大块4、输出内容详解&#xff1a;五、系统分析器六、一些常见的辅助命令大家好&#xff0c;我是哪吒&a…

阿里巴巴高并发架构,到底如何对抗双十一亿级并发流量

前言 我们知道&#xff0c;高并发代表着大流量&#xff0c;高并发系统设计的魅力就在于我们能够凭借自己的聪明才智设计巧妙的方案&#xff0c;从而抵抗巨大流量的冲击&#xff0c;带给用户更好的使用体验。这些方案好似能操纵流量&#xff0c;让流量更加平稳得被系统中的服务…

【Linux】网络编程套接字(中)

&#x1f387;Linux&#xff1a; 博客主页&#xff1a;一起去看日落吗分享博主的在Linux中学习到的知识和遇到的问题博主的能力有限&#xff0c;出现错误希望大家不吝赐教分享给大家一句我很喜欢的话&#xff1a; 看似不起波澜的日复一日&#xff0c;一定会在某一天让你看见坚持…

【Linux】第一座高山——进程地址空间

大家好我是沐曦希&#x1f495; 文章目录一、什么是进程地址空间二、进程地址空间的管理1.区域划分和调整三、为什么存在进程地址空间四、写在最后一、什么是进程地址空间 我们在学习C/C的动态内存空间&#xff0c;习惯把地址空间划分为几个区域: 但是这并不是真的的地址空间…

2023年网络安全趋势

数据安全越来越重要。 我国《数据安全法》提出“建立健全数据安全治理体系”&#xff0c;各地区部门均在探索和简历数据分类分级、重要数据识别与重点保护制度。 数据安全治理不仅是一系列技术应用或产品&#xff0c;更是包括组织构建、规范制定、技术支撑等要素共同完成数据…

node - 下载安装指定版本

文章目录前言一、什么是Node?二、安装1.打开 Node官网2.点击DOWNLOADS3.点击 All download options4.修改地址栏中对应的版本号修改版本号后选择需要的文件下载即可 ![在这里插入图片描述](https://img-blog.csdnimg.cn/049c33e7ebad4114937f23447c5d8c71.png)前言 node 下载…

python实现——处理Excel表格(超详细)

目录xls和xlsx基本操作1&#xff1a;用openpyxl模块打开Excel文档&#xff0c;查看所有sheet表2.1&#xff1a;通过sheet名称获取表格2.2&#xff1a;获取活动表3.1&#xff1a;获取表格的尺寸4.1&#xff1a;获取单元格中的数据4.2&#xff1a;获取单元格的行、列、坐标5&…

Linux- 系统随你玩之--玩出花活的命令浏览器上

文章目录1、背景2、命令浏览器2.1、命令浏览器介绍2.2、特点2.3 常用功能选项3、实操3.1、使用 wget 下载文件3.2、 断点续传3.3、镜像整个站点4、 总结1、背景 一位友人说他有台服务器&#xff0c;需要下载一个文件&#xff0c;但是没有视窗界面与下载工具&#xff0c;怎么办…

Cobalt Strike----(1)

团队服务器 Cobalt Strike 分为客户端组件和服务器组件。服务器组件&#xff0c;也就是团队服务器&#xff0c;是Beacon payload 的控制器&#xff0c;也是 Cobalt Strike 社会工程功能的托管主机。团队服务器还存储由Cobalt Strike 收集的数据&#xff0c;并管理日志记录。Cob…

猿创征文|【Typescript】搭建TS的编译环境

&#x1f373;作者&#xff1a;贤蛋大眼萌&#xff0c;一名很普通但不想普通的程序媛\color{#FF0000}{贤蛋 大眼萌 &#xff0c;一名很普通但不想普通的程序媛}贤蛋大眼萌&#xff0c;一名很普通但不想普通的程序媛&#x1f933; &#x1f64a;语录&#xff1a;多一些不为什么的…