项目收获总结--本地缓存方案选型及使用缓存的坑
本地缓存方案选型及使用缓存的坑
- 一、摘要
- 二、本地缓存
- 三、本地缓存实现方案
- 3.1 自己编程实现一个缓存
- 3.2 基于 Guava Cache 实现本地缓存
- 3.3 基于 Caffeine 实现本地缓存
- 3.4 基于 Encache 实现本地缓存
- 3.5 小结
- 四、使用缓存的坑
- 4.1 缓存穿透
- 4.2 缓存击穿
- 4.3 缓存雪崩
- 4.4 数据不一致
- 4.5 大key问题
- 4.6 热key问题
- 4.7 命中率问题
一、摘要
在互联网公司面试时,说到缓存,面试官基本上会绕不开的几个话题:项目中哪些地方用到了缓存?为什么要使用缓存?怎么使用它的?引入缓存后会带来哪些问题?
引入缓存,其实主要有两个用途:高性能、高并发。
性能体现在引入缓存之前,以商城网站为例,频繁的从数据库里面获取商品数据,也就需要频繁执行SQL等待结果,若数据量很大同时请求频次逐渐增高,响应就逐渐缓慢;引入缓存之后,将数据库里面查询出来的商品数据信息存入缓存,需要时直接从缓存服务获取结果,效率极大提升。
并发体现在引入缓存之前,以 MySQL数据库为例,单台机器一秒内的请求次数到达 2000 之后就会开始报警;引入缓存之后,比如以 Redis 缓存服务器为例,单台机器一秒内的请求次数支持 110000 次,两者支持的并发量完全不是一个数量级的。
缓存和数据库效率差距大的根本原因:缓存数据存储在内存,数据库数据存储在磁盘,
而计算机中内存的数据读写性能远超磁盘的读写性能。但电脑重启后内存数据易丢失,而磁盘数据不易丢失。
所以数据存储方案不同,造就不同的实践用途。接下来就浅谈缓存,主要是本地缓存的使用。
二、本地缓存
从缓存面向的对象不同,缓存分为:本地缓存、分布式缓存和多级缓存。
(1)本地缓存:在单个计算机服务实例中,直接把数据缓存到内存中进行使用。
(2)分布式缓存:将一个计算机服务,同时在多台计算机里部署,所需数据无法共享(比如session会话)而引入一个独立部署的缓存服务来连接多台服务器的技术实践方案。
(3)多级缓存:在实际的业务中,本地缓存和分布式缓存会同时结合进行使用,当收到访问某个数据的操作时,会优先从本地缓存服务(一级缓存)查询,如果没有,再从分布式缓存服务(二级缓存)里面获取,如果也没有,最后再从数据库里面获取;从数据库查询完成之后,在依次更新分布式缓存服务、本地缓存服务的技术实践方案。
三、本地缓存实现方案
缓存关注点:第一是内存持久化;第二是支持缓存的数据自动过期清除。
3.1 自己编程实现一个缓存
对于简单的数据缓存,完全可以自行编写一套缓存服务。实现思路很简单:采用ConcurrentHashMap作为缓存数据存储服务,然后开启一个定时调度,每隔500毫秒检查一下过期的缓存数据,然后清除。
首先创建一个缓存实体类:
public class CacheEntity {/*** 缓存键*/private String key;/*** 缓存值*/private Object value;/*** 过期时间*/private Long expireTime;//...set、get
}
接着,创建一个缓存操作工具类CacheUtils:
public class CacheUtils {/*** 缓存数据*/private final static Map<String, CacheEntity> CACHE_MAP = new ConcurrentHashMap<>();/*** 定时器线程池,用于清除过期缓存*/private static ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();static {// 注册一个定时线程任务,服务启动1秒之后,每隔500毫秒执行一次executor.scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {// 清理过期缓存clearCache();}},1000,500,TimeUnit.MILLISECONDS);}/*** 添加缓存* @param key 缓存键* @param value 缓存值*/public static void put(String key, Object value){put(key, value, 0);}/*** 添加缓存* @param key 缓存键* @param value 缓存值* @param expire 缓存时间,单位秒*/public static void put(String key, Object value, long expire){CacheEntity cacheEntity = new CacheEntity().setKey(key).setValue(value);if(expire > 0){Long expireTime = System.currentTimeMillis() + Duration.ofSeconds(expire).toMillis();cacheEntity.setExpireTime(expireTime);}CACHE_MAP.put(key, cacheEntity);}/*** 获取缓存* @param key* @return*/public static Object get(String key){if(CACHE_MAP.containsKey(key)){return CACHE_MAP.get(key).getValue();}return null;}/*** 移除缓存* @param key*/public static void remove(String key){if(CACHE_MAP.containsKey(key)){CACHE_MAP.remove(key);}}/*** 清理过期的缓存数据*/private static void clearCache(){if(CACHE_MAP.size() > 0){return;}Iterator<Map.Entry<String, CacheEntity>> iterator = CACHE_MAP.entrySet().iterator();while (iterator.hasNext()){Map.Entry<String, CacheEntity> entry = iterator.next();if(entry.getValue().getExpireTime() != null && entry.getValue().getExpireTime().longValue() > System.currentTimeMillis()){iterator.remove();}}}
}
最后,创建测试main方法:
/ 写入缓存数据,过期时间为3秒
CacheUtils.put("userName", "张三", 3);// 读取缓存数据
Object value1 = CacheUtils.get("userName");
System.out.println("第一次查询结果:" + value1);// 停顿4秒
Thread.sleep(4000);// 读取缓存数据
Object value2 = CacheUtils.get("userName");
System.out.println("第二次查询结果:" + value2);
结果:
第一次查询结果:张三
第二次查询结果:null
3.2 基于 Guava Cache 实现本地缓存
Guava 是 Google 团队开源的一款 Java 核心增强库,包含集合、并发原语、缓存、IO、反射等工具箱,性能和稳定性上都有保障,应用十分广泛。而Guava Cache 很强大,支持很多特性如下:
支持最大容量限制
支持两种过期删除策略(插入时间和读取时间)
支持简单的统计功能
基于 LRU 算法实现
首先pom.xml引入guava依赖:
<!--guava-->
<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>31.1-jre</version>
</dependency>
使用:
// 创建一个缓存实例
Cache<String, String> cache = CacheBuilder.newBuilder()// 初始容量.initialCapacity(5)// 最大缓存数,超出淘汰.maximumSize(10)// 过期时间.expireAfterWrite(3, TimeUnit.SECONDS).build();// 写入缓存数据
cache.put("userName", "张三");// 读取缓存数据
String value1 = cache.get("userName", () -> {// 如果key不存在,会执行回调方法return "key已过期";
});
System.out.println("第一次查询结果:" + value1);// 停顿4秒
Thread.sleep(4000);// 读取缓存数据
String value2 = cache.get("userName", () -> {// 如果key不存在,会执行回调方法return "key已过期";
});
System.out.println("第二次查询结果:" + value2);
输出结果:
第一次查询结果:张三
第二次查询结果:key已过期
3.3 基于 Caffeine 实现本地缓存
Caffeine 是基于 java8 实现的新一代缓存工具,缓存性能接近理论最优,可以看作是 Guava Cache 的增强版,功能上两者类似,不同的是 Caffeine 采用了一种结合 LRU、LFU 优点的算法:W-TinyLFU,在性能上有明显的优越性。
首先pom.xml引入caffeine依赖:
<!--caffeine-->
<dependency><groupId>com.github.ben-manes.caffeine</groupId><artifactId>caffeine</artifactId><version>2.9.3</version>
</dependency>
使用:
// 创建一个缓存实例
Cache<String, String> cache = Caffeine.newBuilder()// 初始容量.initialCapacity(5)// 最大缓存数,超出淘汰.maximumSize(10)// 设置缓存写入间隔多久过期.expireAfterWrite(3, TimeUnit.SECONDS)// 设置缓存最后访问后间隔多久淘汰,实际很少用到//.expireAfterAccess(3, TimeUnit.SECONDS).build();// 写入缓存数据
cache.put("userName", "张三");// 读取缓存数据
String value1 = cache.get("userName", (key) -> {// 如果key不存在,会执行回调方法return "key已过期";
});
System.out.println("第一次查询结果:" + value1);// 停顿4秒
Thread.sleep(4000);// 读取缓存数据
String value2 = cache.get("userName", (key) -> {// 如果key不存在,会执行回调方法return "key已过期";
});
System.out.println("第二次查询结果:" + value2);
输出结果:
第一次查询结果:张三
第二次查询结果:key已过期
3.4 基于 Encache 实现本地缓存
Encache 是一个纯 Java 的进程内缓存框架,具有快速、精干等特点,是 Hibernate 中默认的 CacheProvider。
同 Caffeine 和 Guava Cache 相比,Encache 的功能更加丰富,扩展性更强,特性如下:
支持多种缓存淘汰算法,包括 LRU、LFU 和 FIFO
缓存支持堆内存储、堆外存储、磁盘存储(支持持久化)三种
支持多种集群方案,解决数据共享问题
首先pom.xml引入ehcache依赖:
<!--ehcache-->
<dependency><groupId>org.ehcache</groupId><artifactId>ehcache</artifactId><version>3.9.7</version>
</dependency>
使用:
/*** 自定义过期策略实现*/
public class CustomExpiryPolicy<K, V> implements ExpiryPolicy<K, V> {private final Map<K, Duration> keyExpireMap = new ConcurrentHashMap();public Duration setExpire(K key, Duration duration) {return keyExpireMap.put(key, duration);}public Duration getExpireByKey(K key) {return Optional.ofNullable(keyExpireMap.get(key)).orElse(null);}public Duration removeExpire(K key) {return keyExpireMap.remove(key);}@Overridepublic Duration getExpiryForCreation(K key, V value) {return Optional.ofNullable(getExpireByKey(key)).orElse(Duration.ofNanos(Long.MAX_VALUE));}@Overridepublic Duration getExpiryForAccess(K key, Supplier<? extends V> value) {return getExpireByKey(key);}@Overridepublic Duration getExpiryForUpdate(K key, Supplier<? extends V> oldValue, V newValue) {return getExpireByKey(key);}
}
public static void main(String[] args) throws InterruptedException {String userCache = "userCache";// 自定义过期策略CustomExpiryPolicy<Object, Object> customExpiryPolicy = new CustomExpiryPolicy<>();// 声明一个容量为20的堆内缓存配置CacheConfigurationBuilder configurationBuilder = CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class, String.class, ResourcePoolsBuilder.heap(20)).withExpiry(customExpiryPolicy);// 初始化一个缓存管理器CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()// 创建cache实例.withCache(userCache, configurationBuilder).build(true);// 获取cache实例Cache<String, String> cache = cacheManager.getCache(userCache, String.class, String.class);// 获取过期策略CustomExpiryPolicy expiryPolicy = (CustomExpiryPolicy)cache.getRuntimeConfiguration().getExpiryPolicy();// 写入缓存数据cache.put("userName", "张三");// 设置3秒过期expiryPolicy.setExpire("userName", Duration.ofSeconds(3));// 读取缓存数据String value1 = cache.get("userName");System.out.println("第一次查询结果:" + value1);// 停顿4秒Thread.sleep(4000);// 读取缓存数据String value2 = cache.get("userName");System.out.println("第二次查询结果:" + value2);
}
输出结果:
第一次查询结果:张三
第二次查询结果:null
3.5 小结
对于本地缓存的技术选型,推荐采用 Caffeine,性能上遥遥领先。功能与Guava 类似,而Encache虽支持持久化和集群,但不如分布式缓存中间件Redis。
四、使用缓存的坑
在项目中经常会使用缓存,但用不好的话坑也挺多的:
4.1 缓存穿透
当用户请求的id在缓存中不存在或恶意用户伪造不存在的id发起请求,每次从缓存中都查不到数据,而需要查询数据库,同时数据库中也没有查到该数据,也没法放入缓存。也就是每次这个用户请求过来的时候,都要查询一次数据库。
很显然,缓存根本没起作用,好像被穿透一样,每次都会去访问数据库,而直接请求数据库数量非常多,数据库可能因为扛不住压力而崩溃。
解决方案: 缓存空值
当某个用户id在缓存中查不到,在数据库中也查不到时,也要将该用户id缓存起来,只不过值是空的。这样后面的请求,再拿相同的用户id发起请求时,就能从缓存中获取空数据,直接返回而无需再去查数据库。
比如redis:
redisTemplate.opsForValue().set(key, "", CACHE_NULL_TTL, TimeUnit.MINUTES);
4.2 缓存击穿
在访问热点数据时,该热点在缓存中过期失效,导致这些大量请求短时间都直接怼到数据库,可能会造成瞬间数据库压力过大,而直接挂掉。
解决方案:
(1)加锁。在访问数据库时加锁,防止多个相同keyId的请求同时访问数据库。
try {String result = jedis.set(keyId, requestId, "NX", "PX", expireTime);if ("OK".equals(result)) {return queryInfoById(keyId);}
} finally{unlock(keyId,requestId);
}
return null;
(2)自动续期
在key快要过期之前,用job给指定key自动续期。比如redis使用lua脚本。
(3)永久有效
对于很多热门key,其实是可以不用设置过期时间,让其永久有效的。
4.3 缓存雪崩
而缓存雪崩是缓存击穿的升级版,缓存击穿说的是某一个热门key失效了,而缓存雪崩说的是有多个热门key同时失效。
缓存雪崩目前有两种:
(1)有大量的热门缓存,同时失效。会导致大量的请求,访问数据库。而数据库很有可能因为扛不住压力,而直接挂掉。
(2)缓存服务器down机,可能是机器硬件问题,或者机房网络问题。总之,造成了整个缓存的不可用。
解决方案:
(1) 过期时间加随机数,不要设置相同的过期时间,可以在设置的过期时间基础上,再加个1~60秒的随机数。
实际过期时间 = 过期时间 + 1~60秒的随机数
(2)保证高可用
比如:如果使用了redis,可以使用哨兵模式,或者集群模式,避免出现单节点故障导致整个redis服务不可用的情况。
(3)服务降级
需要配置一些默认的兜底数据。程序中有个全局开关,比如有10个请求在最近一分钟内,从redis中获取数据失败,则全局开关打开。后面的新请求,就直接从配置中心中获取默认的数据。
4.4 数据不一致
数据库和缓存(比如:redis)双写数据一致性问题,是一个跟开发语言无关的公共问题。尤其高并发场景这个问题尤为严重。
解决方案:
先写数据库,再删缓存!
先写数据库,再删缓存!
先写数据库,再删缓存!
除非同时满足:
缓存刚好自动失效。
读请求从数据库查出旧值,更新缓存的耗时,比写请求写数据库,并且删除缓存的还长。
才会出现数据不一致,但系统同时满足上述两个条件的概率非常小。
4.5 大key问题
在使用缓存的时候,特别是Redis,经常会遇到大key问题(缓存中单个key的value值过大)。
项目经历:
在一个风控项目中曾开发过一个分类树查询接口,系统刚上线时,数据量少,在Redis中定义的key比较小,
我在做系统设计时,也没考虑到这个问题。系统运行很长一段时间也没有问题。但随着时间的推移,用户的数据越来越多,
用户的购买行为分类树也越来越大,慢慢形成大key问题。后来某一天之后发现,线上查询客户画像接口耗时越来越长,
追查原因,发现单个用户分类数据涨到上万个,导致该接口出现性能问题,追查发现分类树json串已经接近16MB,而引发大key问题导致的。
解决方案:
(1)缩减字段名
优化在Redis中存储数据的大小,首先需要对数据进行瘦身。只保存需要用到的字段:
@AllArgsConstructor
@Data
public class Category {private Long id;private String name;private Long parentId;private Date inDate;private Long inUserId;private String inUserName;private List<Category> children;
}
这个分类对象中inDate、inUserId和inUserName字段是可以不用保存的。
然后,修改自动名称:
@AllArgsConstructor
@Data
public class Category {/*** 分类编号*/@JsonProperty("i")private Long id;/*** 分类层级*/@JsonProperty("l")private Integer level;/*** 分类名称*/@JsonProperty("n")private String name;/*** 父分类编号*/@JsonProperty("p")private Long parentId;/*** 子分类列表*/@JsonProperty("c")private List<Category> children;
}
由于在一万多条数据中,每条数据的字段名称是固定的,他们的重复率太高,由此,可以在json序列化时,改成一个简短的名称,以便于返回更少的数据大小。
(2)压缩数据
由于在Redis中保存的key/value,其中的value我是存储json格式的字符串,但是占用内存很大,所以需要对存储的数据做压缩。
由于RedisTemplate支持,value保存byte数组,因此先将json字符串数据用GZip工具类压缩成byte数组,然后保存到Redis中。
在获取数据时,将byte数组转换成json字符串,然后再转换成分类树。
这样优化之后,保存到Redis中的分类树的数据大小减少10倍,从而解决大key问题。
4.6 热key问题
二八原理描述:80%的用户经常访问20%的热点数据。引发数据倾斜,不能均匀分布,尤其是高并发系统中问题比较大。
比如有个促销系统,有几款商品性价比非常高,这些商品数据在Redis中按分片保存的,不同的数据保存在不同的服务器节点上。
如果用户疯狂抢购其中3款商品,而这3款商品正好保存在同一台Redis服务端节点。
这样会出现大量的用户请求集中访问同一天Redis服务器节点,该节点很有可能会因为扛不住这么大的压力,而直接down机。
解决方案:
(1)拆分key:提前做好评估,将热点数据分开存储在不同redis服务器来分摊压力。
(2)增加本地缓存:对于热key数据,可以增加一层本地缓存(见前文),能够提升性能的同时也能避免Redis访问量过大的问题。但可能会出现数据不一致问题。
4.7 命中率问题
前面的情况都影响缓存的命中率问题,因为可能会出现缓存不存在,或者缓存过期等问题,导致缓存不能命中。
解决方案:
(1)缓存预热
在API服务启动之前,可以先用job,将相关数据先保存到缓存中,做预热。
这样后面的请求,就能直接从缓存中获取数据,而无需访问数据库。
(2)合理调整过期时间
(3)增加缓存内存
相关文章:
项目收获总结--本地缓存方案选型及使用缓存的坑
本地缓存方案选型及使用缓存的坑 一、摘要二、本地缓存三、本地缓存实现方案3.1 自己编程实现一个缓存3.2 基于 Guava Cache 实现本地缓存3.3 基于 Caffeine 实现本地缓存3.4 基于 Encache 实现本地缓存3.5 小结 四、使用缓存的坑4.1 缓存穿透4.2 缓存击穿4.3 缓存雪崩4.4 数据…...
java使用poi-tl模版引擎导出word之if判断条件的使用
文章目录 模版中if语句条件的使用1.数据为False或空集合2.非False或非空集合 模版中if语句条件的使用 如果区块对的值是 null 、false 或者空的集合,位于区块中的所有文档元素将不会显示,这就等同于if语句的条件为 false。语法示例:{{?stat…...
扩散的魔法:如何打造未来生物打印?
生物打印技术正在快速发展,它允许我们将生物材料、细胞和生长因子等生物活性成分精确地打印成具有特定形状和功能的结构。而扩散现象在生物打印中扮演着至关重要的角色,它影响着打印结构的特性、机械性能、生物功能和形态。为了更好地利用扩散现象&#…...
Bag of mice(概率dp)
https://www.luogu.com.cn/problem/CF148D 思路: 概率dp,设f[a][b]为白鼠为a个,黑鼠为b个时,赢的期望。 f[i][0]1; 1.当先手取到白鼠时 a/(ab); 2.当先手未取到白鼠,先手要向赢,后手也不能取到白鼠&am…...
Python的基础语法——持续更新版
1、type查看数据类型 # 直接输出结果 print(type("Hello")) # 先用变量存储 string_type type("Hello") print(string_type) 2、 类型转化 任何类型可以转化为字符串,但字符串不可以随意转化,要求字符串类内容都是数字 # 类型…...
百度智能云将大模型引入网络故障定位的智能运维实践
物理网络中,某个设备发生故障,可能会引起一系列指标异常的告警。如何在短时间内从这些告警信息中找到真正的故障原因,犹如大海捞针,对于运维团队是一件很有挑战的事情。 在长期的物理网络运维工作建设中,百度智能云通…...
晚上定时编译android系统
1、问题 可能偶然想晚上定时编译android系统 2、解决 at.sh #!/bin/sh# at -f at.sh now1min # at -lset -e set -xecho $SHELLecho at build begin /bin/date >> at_build.log/bin/bash -c source build/envsetup.sh >> at_build.log 2>&1; lunch xxx-us…...
轻薄鼠标的硬核选购攻略,很多人都在“高性价比”鼠标上栽跟头了
轻薄款设计的鼠标是目前鼠标市场的出货大头, 也是价格最卷的一类鼠标。 比游戏鼠标或许更卷一些。 这和当前的移动办公趋势关系很大。 这类鼠标主要跟笔记本和iPad搭配。 核心的使用场景是办公。 因此轻薄和静音是这类鼠标的核心卖点。 同时用户并不愿意付出太…...
Python制作签到系统
import datetime sign_in_records {} def sign_in(username): today datetime.date.today() if username not in sign_in_records: sign_in_records[username] [] sign_in_records[username].append(today) print(f"{username} 签到成功&#…...
面试题007-Java-Spring
面试题007-Java-Spring 目录 面试题007-Java-Spring题目自测题目答案1. 简单介绍一下Spring?2. Spring有哪些模块?3. 什么是Spring IoC ?4. 什么是依赖注入?有哪几种方式可以进行依赖注入?5. 什么是Spring AOP ?6. 什…...
后端之路——登录校验前言(Cookie\ Session\ JWT令牌)
前言:Servlet 【登录校验】这个功能技术的基础是【会话技术】,那么在讲【会话技术】的时候必然要谈到【Cookie】和【Session】这两个东西,那么在这之前必须要先讲一下一个很重要但是很多人都会忽略的一个知识点:【Servlet】 什么是…...
【蓄势·致远】 同为科技(TOWE)2024年年中会议
2024年7月2日-8日,同为科技(TOWE)召开2024年年中工作会议。会议回顾上半年总体工作情况,分析研判发展形势,规划部署下半年工作。 为期一周的工作会议,由同为科技(TOWE)创始人、董事长…...
通过git将文件push到github 远程仓库
1.先git clone 代码地址 git clone htttp://github.com/用户名/test.git 2. 添加文件 例如:touch 1.txt 3.将文件添加到暂存区 git add 1.txt 4.提交 git commit -m "commit 1.txt" 5.与远程仓库建立关联 git remote add 远程仓库名 远程仓库…...
如何判断服务器是否被攻击
如何判断服务器是否被攻击 一、异常流量模式 一种判断服务器是否遭到攻击的方法是监控网络流量。异常的流量模式,例如流量突然剧增或减少,都可能是攻击的迹象。通常,大量的入站流量表明分布式拒绝服务(DDoS)攻击的可能…...
泽众一站式性能测试平台P-One监控指标的意义
在当今数字化和信息化高度发展的时代,企业把保障系统稳定运行、优化业务流程和提升用户体验摆在首要位置。然而,在现如今复杂的分布式系统中,各个组件和服务之间的交互频繁且紧密,当系统出现性能瓶颈时,传统的监测手段…...
前端Canvas入门——一些注意事项
创建渐变的三种方法: createLinearGradient() - 线性渐变 createRadialGradient() - 径向渐变(放射性渐变) createConicGradient() - 锥形渐变 这三种的核心观点都是: 创建一个gradient对象,然后调用addColorStop()方法…...
移动互联安全扩展要求测评项
安全物理环境-无线接入点的位置选择 应为无线接入设备的安装选择合理位置,避免过度覆盖和电磁干扰。 无线接入设备的安装位置选择不当,易被攻击者利用,特别是攻击者会通过无线信号过度覆盖的弱点进行无线渗透攻击,因此要选择合理…...
【代码随想录】【算法训练营】【第64天】 [卡码117]软件构建 [卡码47]参加科学大会
前言 思路及算法思维,指路 代码随想录。 题目来自 卡码网。 day 64,周三,继续ding~ 题目详情 [卡码117] 软件构建 题目描述 卡码117 软件构建 解题思路 前提: 思路: 重点: 代码实现 C语言 [卡码…...
【python算法学习1】用递归和循环分别写下 fibonacci 斐波拉契数列,比较差异
问题: fibonacci 斐波拉契数列,用递归和循环的方法分别写,比较递归和循环的思路和写法的差别 最直接的思路,是写递归方法 循环方法的稍微有点绕,我觉得问题主要是出在,总结循环的通项公式更麻烦,难在数学…...
【邀请函】庭田科技邀您第五届中国国际复合材料科技大会
第五届中国国际复合材料科技大会暨第七届国际复合材料产业创新成果技术展示(ICIE7-新疆)将于7月25-27日在新疆乌鲁木齐-国际会展中心举行。上海庭田信息科技有限公司将携多款仿真模拟软件亮相本次大会,诚挚欢迎各位到场咨询了解! …...
win32:第一个窗口程序-应用程序入口点(part.6)
第一个窗口程序的最后一部分:应用程序入口函数wWinMain;这是Windows应用程序的主函数,负责初始化应用程序、注册窗口类、创建主窗口并进入消息循环处理消息。 int APIENTRY wWinMain(_In_ HINSTANCE hInstance,_In_opt_ HINSTANCE hPrevInst…...
c++ 多边形 xyz 数据 获取 中心点方法,线的中心点取中心值搞定 已解决
有需求需要对。多边形 获取中心点方法,绝大多数都是 puthon和java版本。立体几何学中的知识。 封装函数 point ##########::getCenterOfGravity(std::vector<point> polygon) {if (polygon.size() < 2)return point();auto Area [](point p0, point p1, p…...
ext_errno:拓展errno
类似于C库的errno机制,报告错误发生的原因以及所在的位置,通过查询来获取。...
【CUDA】 Trust基本特性介绍及性能分析
Trust简介 Thrust 是一个实现了众多基本并行算法的 C 模板库,类似于 C 的标准模板库(standard template library, STL)。该库自动包含在 CUDA 工具箱中。这是一个模板库,仅仅由一些头文件组成。在使用该库的某个功能时,包含需要的头文件即可。该库中的所有类型与函数都在命名空…...
颈肩肌筋膜炎中医治疗
颈肩肌筋膜炎,又称颈肩肌纤维织炎或肌肉风湿症,是一种涉及筋膜、肌肉、肌腱和韧带等软组织的无菌性炎症。以下将分别从症状和治疗两方面进行详细介绍。 一、颈肩肌筋膜炎的症状 颈肩肌筋膜炎的主要症状包括: 1、肩背部疼痛:患者…...
Java 通配符 在短信发送之中 通配符参数动态获取解决方案
目录 1、通配符应用场景 2、实现方案分析 2.1、可能针对不同模板中核定参数硬编码到程序之中写死 2.2、通配置模板之中动态获得对应的参数 3、通过正则表达式验证与替换参数${}参考示例 4、参考文章 1、通配符应用场景 我们在使用通配符场景,主要是应用于短信…...
Mybatis-Plus中LambdaQueryWrapper
基本用法 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; // 假设有一个 User 实体类 LambdaQueryWrapper<User> queryWrapper new LambdaQueryWrapper<>(); // 添加查询条件 queryWrapper.eq(User::getName, "John&quo…...
C++ 入门05:类和对象
往期回顾: C 入门02:控制结构和循环-CSDN博客C 入门03:函数与作用域-CSDN博客C 入门04:数组与字符串-CSDN博客 一、前言 在前面文章的学习中,我们了解了 C 的基本结构、变量、输入输出、控制结构、循环、函数、作用域…...
4G LTE教程
整体架构 物理层(第 1 层) 物理层通过空中接口传输来自 MAC 传输信道的所有信息。负责 RRC 层的链路自适应 (AMC)、功率控制、小区搜索(用于初始同步和切换目的)和其他测量(LTE 系统内部和系统之间)。 介…...
C++:哈希表
哈希表概念 哈希表可以简单理解为:把数据转化为数组的下标,然后用数组的下标对应的值来表示这个数据。如果我们想要搜索这个数据,直接计算出这个数据的下标,然后就可以直接访问数组对应的位置,所以可以用O(1)的复杂度…...
免费10g网站空间/google搜索引擎免费入口
我知道你已经开始工作了,但我想补充一点,我已经查看了PHP7的源代码,并且没有理由说它为什么不能每次都工作.PHP_MINIT_FUNCTION(hash){// ...zend_hash_init(&php_hash_hashtable, 35, NULL, NULL, 1);// ...php_hash_register_algo("sha224", &php_hash_sha…...
专注赣州网站建设/百度上打广告怎么收费
public function searchWordsByInitial(){//从AJAX发出的URL中获得参数:用户点击的字母和点击的页码$initial htmlentities($_POST[initial], ENT_QUOTES,"UTF-8");$page htmlentities($_POST[page], ENT_QUOTES,"UTF-8");$words $this->…...
wordpress o2o插件/石景山区百科seo
上次讲到的是CSVread函数来获取测试数据的参数化,这次使用randomstring 有的时候有些参数是不断变化的,我们如果利用csv去做,还是要准备很多不同的数据,但是我们如果用randomstring的话,那么就可以减少这个问题的干扰。 步骤: 准备好csV格式的数据准备…...
网站空间 默认文档/网络营销有哪些推广方法
文章目录 数据结构算法题Java 基础Java 高级三大框架数据库操作系统计算机网络分布式/集群等高级主题技术开放题数据结构 HashMap的原理,内部数据结构? 底层使用哈希表(数组 + 链表),当链表过长会将链表转成 红黑树以实现 O(logn) 时间复杂度内查找讲一下 HashMap 中 put …...
山东省招投标信息网/扬州seo优化
动态菜单: 不同用户登录后,应该看到不同菜单结构 1、 修改 index.html 加载基本菜单 url 路径 2、 在 MenuAction 添加 showMenu 方法 3、 编写业务层 调用 DAO ”from Menu m inner join fetch m.roles r inner join fetch r.users u where…...
做网站需要公司备案/关键词排名优化易下拉软件
今天晚上我迫不急待的打开电脑,一阵撕心裂肺的猫叫之后,我开始了网上的旅程。 没搞错,上不去我的网页,怎么搞的,我可是网页的版主哦,这就像因太晚没回家而被老婆关在门外的可怜人一样,只…...