文章目录
- redis
-
- redis的特性
- redis的使用场景
- 哪里用到的redis,时效
- redis数据结构
-
- [Stirng 底层实现](#Stirng 底层实现)
- redis遍历key
- redis的淘汰策略
- 缓存击穿,缓存雪崩
- redis的锁机制
- redis分布式锁
- 持久化方案
- redis线程模型
- 部署方式
- kafka
-
- 核心概念
- 使用消息队列的原因
- [kafka 如何解决消息重复](#kafka 如何解决消息重复)
- kafka如何保证消息不丢失
- kafka什么时候进行rebalance
- [kafka 消息积压怎么办](#kafka 消息积压怎么办)
- kafka分区和消费者的关系
- 分区数量
- 消费者分区分配策略
- 其他微服务组件
-
- eureka特性
- eureka注册中心机制
- Eureka
- [feign ribbon eureka](#feign ribbon eureka)
- eureka和nacos的区别
- 限流算法
- 无状态的服务,消费降级
- linux命令
- 其他
redis
redis的特性


redis的使用场景
- 消息队列:使用 LIST 或 PUB/SUB 机制来实现消息传递和队列功能。
- 实时分析:利用 Sorted Sets 和 HyperLogLog 进行实时数据分析和计数。
- 会话存储:存储用户会话数据,例如登录状态和用户偏好。
- 分布式锁:利用 Redis 的 SETNX 命令实现分布式锁,以确保资源的互斥访问。
- 排行榜:使用 Sorted Sets 创建和管理排行榜或评分系统。
- 计数器:使用 INCR 命令实现高效的计数器功能,例如访问计数。
- 数据结构存储:利用 Redis 提供的各种数据结构(如 HASH、SET、LIST 等)来存储复杂数据类型。
哪里用到的redis,时效
- 数据库存储 存储登录信息 token 30分钟 实现value的序列化,存储的对象
- 缓存 路由转发,接口匹配 redismanager默认30天
工作流暂存 10分钟:为了缓解数据库压力,开始的流程是异步进程,但是要全量进程的结果 - 幂等性的实现 在消费消息时,将消息的uuid存储到redis中,防止重复消费
redis数据结构
String 动态字符串,类似于ArrayList
List 双向链表
Hash 数组加链表 等价于Java语言的HashMap
Set HashMap 只不过所有value都指向同一个对象
ZSet 压缩链表或者跳表
Stirng 底层实现
动态字符串sds
sds结构一共有五种header定义,目的是为了满足不同长度字符串使用,节省内存。
header主要包括:
- len长度
- alloc字符串最大容量,不包括header和最后的空终止字符
- flags标志位,第三位表示类型,其余五位未使用
- buf:字符数组
- encoding
- int:可以使用long类型的整数表示的会用long型来存储
- raw:长度大于44字节的字符串,使用sds存储
- embstr:长度小于等于44字节的字符串,效率比较高,且数据都保存在一块内存区域。
ZSet的实现方式:
ziplist压缩链表:
- 元素数量小于128
- 每个元素长度小于64
skiplist跳跃链表:不满足上述条件就采用跳表,具体来说组合了map和skiplist
- map用来存储 member到score的映射, 时间复杂度为O(1)
- skiplist按照从小到大存储分数,每个元素的值都是[score,value]对
跳表就是在有序节点上增加多级索引

n个节点,第一层索引为n/2,第二层索引为n/4,第k层索引为n/2^k
空间换时间的折半查找
redis排行榜 zset
添加
zadd key score value
ZADD broadcast:20210108231 1 lisi
加分
zincrby key increment member
ZINCRBY broadcast:20210108231 2 lisi
排名
ZRANGE broadcast:20210108231 0 -1 WITHSCORES
redis遍历key
keys pattern 一次性匹配所有key,当key比较多时,对内存消耗和redis都是隐患
SCAN cursor [MATCH pattern] [COUNT count] 2.8.0版本之后加入
scan只会返回少量元素,每次调用用上次调用返回的游标,以此延续迭代过程,返回为0标识迭代结束。
count表示从数据集返回多少个匹配元素
redis的淘汰策略
过期键淘汰策略是定期删除和惰性删除
定期删除 是指:redis服务器定期操作redis.c/serverCron函数执行时,redis.c/activeExpireCycle会被调用。actvieExpire函数在规定时间内,分多次遍历服务期内的多个数据库,从过期字典中检查一部分key的过期时间并删除。
current_db记录当前检查数据库,函数处理2号数据库时间超限,返回后下次检查会从3号数据库开始检查。所有数据库检查完毕current_db重置为0,然后再次开启一轮的检查工作。
惰性删除是指用户请求,此时会检查过期,过期清除不会返回
大量key堆积,内存耗尽如何处理?
内存淘汰机制:

缓存击穿,缓存雪崩
- 缓存穿透:缓存和数据库中都没有数据,多次重复访问导致数据库崩溃
解决 :- 业务层校验:对于不合规的入参,直接返回
- 不存在数据设置短过期时间
- 布隆过滤器
- 缓存击穿:热点key失效同时,大量请求进入,从而全部到达数据库,压垮数据库
解决 :- 永不过期
- 定时更新:过期时间1h,每到59min去更新
- 互斥锁:redis根据key获取到的value为空时,先加锁,去数据库加载,加载完毕,释放锁。其他线程请求发现获取锁失败,则睡眠一段时间。
- 雪崩:缓存大面积失效,或者Redis宕机,大量请求进入数据库,压垮数据库
解决 :- 设计有效期均匀分布:避免缓存设置相近的有效期,可以设置有效期时增加随机值,或者统一规划有效期,使得过期时间均匀分布。
- 缓存预热:流量大时,提前访问一遍,将数据存储到redis中并且设置不同的过期时间
- 保证Redis的高可用
redis的锁机制
redis一般用作缓存,多读少写,只支持乐观锁
Redis事务命令主要包括 WATCH, EXEC, DISCARD, MULTI。
事务使用MULTI开启,这时可以执行多条命令,Redis在这些事务中加入命令,当用户执行Exec命令时才真正的执行队列当中的命令。执行discard,丢弃队列中的命令。
Watch命令是Exec执行的条件,watch的key没有修改则执行事务,否则事务不会被执行。
Watch命令可以被调用多次,一个watch命令可以监控多个key。watch命令调用则开启监视功能,直到exec命令终止。
Redis的watch命令给事务CAS机制,如果key在执行exec前有变动,则整个事务被取消。
事务中,采用watch加锁,unwatch解锁,执行EXEC命令或者Discard命令后,锁自动释放,不需要进行unwatch操作。
redis分布式锁
分布式锁需要保证:
1.互斥性:只能有一个客户端获得锁
2.安全性:锁只能被持有该锁的客户端删除
3.死锁:锁超时释放,避免死锁
4.容错:当redis部分节点宕机,客户端仍能获取锁和释放锁
1.一种实现
setnx k v; 成功返回1 失败返回0;
getset k v;返回key的旧值;
expire k secondsl;给key设置超时时间
del key [key ...]
1.1setnx key 当前时间+过期时间
1.2. 失败获取锁失败,成功 expire 超时时间
1.3.执行业务 del key
问题:如果获取锁成功未设置超时时间的时候进行重启,会产生死锁。
(如果不是kill线程,而是shutdown可以用springBean的predestory注解,进行删除key操作)
优化:获取锁失败后,查看当前key的value值,如果不为空,并且当前时间大于该值,说明当前锁失效,通过getset获取值。如果getset获取的值,和一开始的旧值相同,则获取锁成功。
问题:多节点时间需要尽可能的保持一致。getset即便失败也会延长之前锁的时间。
2.使用lua脚本
保证setnx和expire的原子性
3.set扩展命令
SET key value[EX seconds][PX milliseconds][NX|XX]
- NX :表示key不存在的时候,才能set成功,也即保证只有第一个客户端请求才能获得锁,而其他客户端请求只能等其释放锁,才能获取。
- EX seconds :设定key的过期时间,时间单位是秒。
- PX milliseconds: 设定key的过期时间,单位为毫秒
- XX: 仅当key存在时设置值
问题:业务没执行完,锁就释放了;锁被别的线程误删。
4.SET EX PX NX + 校验唯一随机值,再删除
还是会存在业务没有执行完成,锁释放的问题。
5.Redisson框架
开源框架Redisson
加锁后启动一个watchdog线程,每隔10s检查是否持有锁,持有锁就延长,防止锁过期提前释放。
6.redlock+redisson
redis节点全为master,避免master加锁后未同步到slave然后宕机导致加锁失败的情况
按顺序向5个master节点请求加锁
根据设置的超时时间来判断,是不是要跳过该master节点。
如果大于等于3个节点加锁成功,并且使用的时间小于锁的有效期,即可认定加锁成功啦。
如果获取锁失败,解锁!
持久化方案

redis线程模型
redis内部采用事件处理器是单线程的,因此redis叫做单线程模型。采用IO多路复用机制同时监听多个socket,将产生的事件的socket压缩到内存队列中,事件分派器根据事件不同的类型选择对应的事件处理器进行处理。
文件事件处理器的结构
- 多个socket
- IO多路复用程序
- 文件事件分派器
- 事件处理器(连接应答处理器,命令请求处理器,命令回复处理器)
线程模型
多个socket可能并发产生不同的操作,每个操作对应不同的事件,但是IO多路复用程序会监听多个socket,将产生事件的socket放入队列中排队,事件分派器每次从队列中取出一个socket,根据socket对应的事件类型交给对应的事件处理器进行处理。

建立连接
- redis服务端进程初始化时,将server socket 的AE_READABLE事件与连接应答处理器关联。
- 客户端socket01向redis进程serverSocket请求建立连接,此时server socket 产生一个AE_READBLE事件,IO多路复用程序监听到server socket产生的事件后,将socket压入队列。
- 文件事件分派器从队列中获取socket,交给连接应答处理器。
- 连接应答处理器会创建一个能与客户端通信的socket01,并将socket01的AE_READABLE事件与命令请求处理器关联
执行一个set请求 - 客户端发送了一个set key value请求,此时redis中的socket01会产生AE_READABLE事件,IO多路复用程序,将socket01压入队列
- 此时事件分派器从队列中获取到socket01产生的AE_READABLE事件,由于前面socket01的AE_READABLE事件已经和命令请求处理器关联
- 事件分派器将命令交给请求处理器来处理。命令处理器读取k v并在自己内存中完成设置
- 操作完成后,将socket01的AE_WRITEABLE事件与命令回复处理器关联
- 如果此时客户端准备好了接受数据,那么redis中的socket01会产生一个AE_READABLE事件,同样压入队列中
- 事件派分器找到相关联的命令回复处理器,由命令回复处理器对socket01输入本次操作的一个结果,比如ok,之后解除socket01的AE_READABLE事件与命令回复处理器的关联。

为什么redis效率高?
1.纯内存操作
2.核心是基于非阻塞的IO多路复用机制
3.C语言实现,语言更接近操作系统,执行速度相对会快
4.单线程反而避免了多线程的频繁上下文切换问题,预防了多线程可能产生的竞争问题。
redis的吞吐量:
单点TPS达到8万/秒,QPS达到10万/秒
qps是指每秒最大能接受的用户访问量,tps是指每秒钟最大能处理的请求数。
部署方式
单机,主从,哨兵
kafka
核心概念
Producer(生产者):负责将消息发送到 Kafka 集群中的某个主题(Topic)。生产者将数据发布到 Kafka 中,可以并行发送数据。
Consumer(消费者):从 Kafka 中消费消息。消费者可以独立工作,也可以作为消费组的一部分,并行消费同一主题的数据。
Broker(代理):Kafka 集群中的每个节点都被称为一个 Broker。Kafka 集群可以由多个 Broker 组成。它负责接收、存储和转发消息。每个 Broker 都管理一部分分区的数据。
Topic(主题):Kafka 中的消息被按主题分类。每个生产者将消息发送到某个主题,消费者可以订阅某个主题并从中消费消息。主题是 Kafka 消息流的一个分类单位。
Partition(分区):Kafka 中的每个主题可以划分为多个分区。每个分区内的消息有序,并且每个分区的数据是由一个 Broker 来管理的。分区使得 Kafka 可以实现水平扩展,提高吞吐量。
Offset(偏移量):每个消息在分区内都有一个唯一的偏移量。消费者通过这个偏移量来追踪自己消费的位置。偏移量是分区内消息的顺序编号。
ZooKeeper:Kafka 使用 ZooKeeper 来管理和协调集群的元数据,如 Broker 信息、消费者组的偏移量等。ZooKeeper 作为一个集群管理工具,确保 Kafka 集群的高可用性和一致性。
使用消息队列的原因
1.流量削峰(消息过多,处理有限) 落库采用异步消息的方式,减缓数据库压力
2.服务解耦(接口故障不影响生产和消费)消息发出去,发消息的应用down了,也不会影响后序微服务的消费
3.异步(提高响应速度,用户无感知)
kafka 如何解决消息重复
-
生产者:生产者发送的消息没有收到正确的broker响应,导致producer重试。
以及 ack=all 以及 retries > 1 。ack=0,不重试。可能会丢消息,适用于吞吐量指标重要性高于数据丢失,例如:日志收集
启动kafka的幂等性,设置: enable.idempotence=true幂等生产者的原理:每个生产者都有唯一Pid,发次发送消息会累加seq。broker为每个topic的每个分区都维护了一个当前当前写成功的消息最大PID-seq,消息落盘+1,当收到小于当前最大PID-seq时就会丢弃该消息。
-
消费者:
offset手动提交,业务成功处理后,提交offset
幂等性:多次操作结果一致
获取消息的唯一id,会将id存入redis,记录为处理中,以后的消息就不会进行重复处理
kafka如何保证消息不丢失
1.生产者发送给服务器
acks机制 0 发送就成功,1 生产者收到leader分区的响应则认为成功 -1 当所有ISR中的副本全部收到消息,生产者才认为是成功的
2.服务器通过副本保存消息
3.消费者,关闭自动提交,在接收到消息,进行业务处理完毕后再提交偏移量
kafka什么时候进行rebalance
参考Kafka的Rebalance机制可能造成的影响及解决方案
每当有新的消费者加入或者订阅的topic数发生变化,会触发rebalance(再均衡:在同一个消费组当中,分区的所有权从一个消费者转到另一个消费者)机制,Rebalance顾名思义就是重新均衡消费者消费。
过程如下:
1.所有消费者向coordinator发送请求,请求加入comsumer group。一旦所有成员都发送了请求,Coordinator会从中选择一个consumer作为leader,并将组员信息发给leader
2.leader分配消费方案,指定哪个消费者消费哪些topic的哪些分区。发给coordinator,coordinator发送给消费者,组内成员知道自己该消费哪些分区了。
coordinator:每个consumer group会选择一个服务器作为自己的coordinator,负责监控整个消费者组内各个分区的心跳,以及判断是否宕机和开启rebalance的
partition:每个topic分区,备份在不同的服务器上
如何选择coordinator?
对groupid进行hash,然后对_consumer_offsets的分区数量进行取模,默认分区数量为50,可以配置。
发生的时机:
1.分区数量增加。
2.对topic的订阅发生变化
3.消费者组成员的加入或者离开。
影响:1.重复消费;2.集群不稳定;3.影响消费速度
kafka 消息积压怎么办
1.修复消费者,然后停掉所有消费者
2.临时建立10倍或者20倍的queue数量(新建topic,分区是原来的10倍)
3.写一个临时分发消息的consumer,消费挤压数据不作处理,均匀写入临时建好10数量的queue中
4.征用10倍的机器来部署consumer,每一批consumer消费一个临时queue的消息
5.这种做法相当于临时将queue资源和consumer资源扩大10倍,以10倍速度来消费消息
6.消费完成之后,回复原有的部署架构,重新用原来的consumer机器来消费消息。
kafka分区和消费者的关系
1.生产者中的分区合理消费,消费者的线程对象和分区保持一致,多余的线程不会进行消费
2.消费者默认即为一个线程对象
3.消费者服务器数*线程数 = partition个数
分区数量
分区多可以增加吞吐量
分区过多:
- 客户端服务端需要的内存变多,客户端生产者有个参数batchsize,默认16kb。为每个分区缓存消息,满了打包发送。
分区越多,消费者获取数据所需的内存就增多。同时消费者线程数要匹配最大分区数,线程切换开销很大。
服务器端维护许多分区界别的缓存。 - 文件句柄的开销:每个分区在底层文件系统都有属于自己的目录。分区越多,要同时打开的文件句柄数就越多。
- 降低高可用性:kafka通过副本来保证高可用。分区多,服务器上的副本就多,服务器挂掉,在副本之间进行重新选举的消耗就大。
目标吞吐量TT,消费者吞吐量TC,生产者吞吐量TP,分区数= TT/max(TP,TC)
分区和消费者的关系是多对一
一个分区只能对应一个消费者的原因是保证消息顺序性。
因此消息挤压,部署多台消费者实例是不能加快消费的,最多增加到和分区数量一致,超过的组员只会占有资源二不起作用。
消费者分区分配策略
1.range策略
- 将消费者按名称排序,分区按照数字排序
- 分区总数/消费者,除得尽均匀分配,除不尽位于前面的消费多负责分区
2.roundrobin 轮询
为了保证均匀分配,需要满足两个条件
1.同一个消费者组里每个消费者订阅的主题必须相同
2.同一个消费者里面所有消费者的num.streams必须相等
3.sticky分配策略
主要实现两个目的,如果发生冲突,优先实现第一个
1.分区分配尽可能均匀
2.分区的分配尽可能的与上次分配的保持相同
0.11.X版本引入,最复杂最优秀
区别于轮询,在消费者挂掉之后,不会全部分区重新轮询分配,而是将挂掉消费者消费的分区进行重新分配。
其他微服务组件
eureka特性
-
cap
Consistency(一致性)
Availability(可用性)
Partition tolerance(分区容忍性)
任意组合,不能兼得 eureka ap zk cp
注册慢:ap特性,client延迟注册 30s;server 响应缓存 30s;server 缓存刷新 30s
-
自我保护机制: eureka会检查最近15分钟服务正常心跳占比,低于85%出发自我保护模式,不剔除服务。但是服务也会过期,eureka在启动完成后,每隔60s检查服务健康状态,如果被保护的服务过一段时间默认90s还没有恢复就会把服务剔除。
在此期间高于85%,自动关闭自我保护机制。
自我保护原因:避免因为网络通信故障降低可用性。
一般开发关闭,因为开发经常重启。生产开启。生产环境相对稳定,让eureka管理。
-
服务注册:启动注册,发送主机名,端口号等
-
服务续约:30s一次,90s没收到进行服务剔除
-
服务下线:client优雅退出发送cancel命令,server接到命令删除节点
-
获取注册列表:第一次全量更新,后续增量更新,30s一次
eureka注册中心机制
注册中心,包括服务发现,治理等功能。
@EnableEurekaServer作为注册中心,@EnableEurekaClient作为服务的提供者或消费者。
- 注册服务
EurekaClient将服务信息封装成Intanceinfo对象,通过EurekaHttpClient调用register方法发送post请求执行服务注册;
EurekaServer提供了基于Jersey的Rest风格接口,在ApplicationResource类中提供了addInstance方法来接受注册信息。如果注册信息通过校验,将服务信息保存到本地注册表。数据结构为双层HashMap,key为应用名,内层map,Key是应用实例信息编号,value是InstanceInfo
EurekaClient接受并解析注册结果,判断httpResponse的statusCode,如果是204则代表注册成功
调用replicateToPeers方法将此次注册信息复制到对等的Eureka节点 - 定时任务
- 拉取服务器注册实例
任务通过ScheduledExecutorService来实现任务调度,执行周期默认为60秒一次
获取方式有两种,全量获取和增量获取。第一次全量获取,后序增量获取;获取到服务器注册实例信息后,保存或更新到本地- 续约
任务通过ScheduledExecutorService来实现任务调度,执行周期默认为30秒一次
通过Renew方法发起续约请求。将appname,appid以及intanceinfo作为参数,通过EurekaHttpClien发送Http请求
EurekaServer通过renewLease()方法接受续约请求
根据AppName从注册表获取对应的服务信息,并更新一些属性如renewsLastMin,lastUpdateTimestamp
EurekaServer返回结果200或者204
EurekaClient接受续约结果;如果是404重新发起,如果是200则表示续约成功,更LastSuccessHeartBeatTimestamp变量
- 剔除服务
通过 JDK 自带的 Timer 来实现任务调度,通过 evict() 方法执行具体的操作
首先判断是否开启了实例自我保护机制,如果开启自我保护,则不做任何操作
如果未开启,根据 lastUpdateTimestamp 收集已过期的服务,加入到List集合中
通过 internalCancel() 方法,在该方法中从 registry 中剔除已经过期的实例。具体的剔除过程会通过打乱过期服务列表,并通过 Random 随机剔除,保证服务器剔除的均匀性
- 续约
- 拉取服务器注册实例
Eureka
feign ribbon eureka
feign在调用时会被ribbon拦截,RibbonLoadBalancerClient中构建了一个ILoadBalancer来确定应用名对应的真实ip,
内部调用eureka的服务发现获取服务发现缓存的服务信息。
更新是在RibbonClientConfiguration创建定时任务30s一次
eureka和nacos的区别
限流算法
一致性哈希
一致性哈希相较于普通哈希具有更好的可扩展性和容错性。
哈希是对节点个数取模,在进行扩容或者节点下线时,需要重新映射所有数据。
一致性哈希是对2^32取模,将哈希值空间映射到虚拟的闭环上,称为哈希环。取模所得值,进行顺时针寻找到的第一个节点为哈希环中的位置。节点扩容和下线时只需要迁移相邻节点的数据。
当分布不均衡时,采用虚拟节点来解决问题。
无状态的服务,消费降级

linux命令
cat 文件 | grep 关键字 | wc -l
参考 Linux系统中统计文件中某个字符出现次数命令详细教程
- 不分大小写统计
grep -o -i 'a' aaa.txt | wc -l - 统计多个文件中某个字符出现次数总和
grep -o -i 'a' aaa.txt bbb.txt | wc -l
其他
分布式锁
1.数据库
在数据库中以资源唯一号为主键插入数据,处理完成后删除数据
问题:
- 释放锁失败会导致死锁,需要定时任务定时清理
- 锁的可靠性依赖数据库,建议设置备库,避免单点
- 锁是非阻塞的,因为插入失败会报错;如果需要阻塞式的设置for,while循环等
- 非可重入的锁,需要在数据库中添加字段获取主机信息,匹配的话重新分配给他。
乐观锁:查询数据和版本号,进行更新,更新成功加锁成功
问题:
- 对表增加额外字段,增加数据库冗余
- 并发量高的时候,会导致大量请求失败,以及大量请求对数据库的压力。
适合并发量不高,且操作不频繁的场景
悲观锁:增加排它锁 select for update;更新数据。
问题: 每次请求都会都会产生额外的加锁开销并且阻断等待锁的获取,高并发的情况下,容易造成大量请求阻塞,影响可用性。
2.缓存 redis redlock和redission实现,所有节点均为master,加锁轮询请求,超过半数即为加锁成功,否则加锁失败
3.zookeeper:
zk是一个为分布式提供一致性服务的开源软件。
它内部是一个分层的文件系统目录结构,规定同一个目录下只能有一个唯一文件名。
基于zookeeper实现分布式锁的步骤:
- 创建一个目录myLock
- 线程A获取锁就在myLock下创建临时顺序节点
- 获取目录下所有子节点,找到比自己小的,如果不存在,获得锁
- 线程B获取所有节点,判断不是最小节点,设置监听比自己小的节点。
- A处理完毕后,删除节点,线程B监听到变更事件,判断自己是不是最小节点,如果是则获得锁。
可以使用zookeeper第三方库Curator客户端
问题:
- 性能没有缓存高,每次加锁释放锁都需要动态的创建,销毁临时节点来实现锁功能。zk中创建和删除节点都需要通过leader来执行,同步到fllower上。
- zookeeper也可能有并发问题。当获取锁的客户端和zk的session连接断了,就会删除临时节点,其他线程就会获得锁。不常见是因为zk有重试机制,Curator客户端支持多种重试策略的配置。
堆
堆是二叉树,一般的二叉树用链表存储,用数组存储会浪费空间,但是堆是完全二叉树,用数组存储。


已知父节点下标n, 他的右节点为2n+2,左节点2n+1
已知子节点n,他的父节点为(n-1)/2
大根堆和小根堆
大根堆:根节点大于左右孩子节点
小根堆:根节点小于左右孩子节点
优先级队列
java提供这种数据结构,每次添加或者删除元素,都会变成小跟堆。
java
// 默认得到一个小根堆
PriorityQueue<Integer> smallHeap = new PriorityQueue<>();
smallHeap.offer(23);
smallHeap.offer(2);
smallHeap.offer(11);
System.out.println(smallHeap.poll());// 弹出2,剩余最小的元素就是11,会被调整到堆顶,下一次弹出
System.out.println(smallHeap.poll());// 弹出11
// 如果需要得到大根堆,在里面传一个比较器
PriorityQueue<Integer> BigHeap = new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
top-K问题
数组,找出前三个最小元素
1.排序
2.放入小跟堆
3.在大根堆中放三个元素,循环数组每次往里放一个数据,弹出最大的数据,最后堆里就是结果。
数据格式标准化
xml到标准对象 thoughtworks.xstream 流
json到标准对象 hutools工具,底层反射调用set方法
javaBean
java语言中的可重用组件。
满足:1.类是公共的;2.有一个无参的公共构造器;3.有属性,且有对应的get和set方法。
其他开发者通过JSP,Servlet 其他JavaBean,applet程序或者应用来使用这些对象。用户可以认为JavaBean提供了一种随时随地复制粘贴的功能。
javaBean的任务就是一次性编写,任何地方执行,任何地方重用。
synchronized在哪用到
createMaxNo 开启数据库事务,sf for update 增加数据库排他锁
同一个应用有一个缓存,加同步锁,避免并发重复增加步长,造成浪费。
应用之间由数据库的排他锁协调。
ConditionalOnMissingBean注解
在beanFactory中没有指定的bean才能匹配,主要用来做自动配置,当没有指定类的时候,使用默认配置
确保使用注解的bean在指定bean的后面运行,否则会失效。
ConditionalOnBean
指定类存在时,实例化当前Bean
ConditionalOnClass
指定类名在类路径上存在,实例化当前Bean
ConditionalOnMissingClass
指定类名在类路径上不存在,实例化当前Bean
actuator
找到HealthContributor类型bean进行健康注册,健康检查的时候检查这些bean
redis池化
JedisPoolConfig