1小时内点赞5次滑动窗口设计(Java可拓展到Redis)
目的很多网站都有对于点赞功能的限制,例如一个小时内点多少赞,或者一篇文章点多少次赞,今天就来设计一下这个功能如何实现,先基于Java的数据结构进行开发,后面可拓展到Redis中的数据结构。一小时内点在5次(可自行修改),为了方便测试,本人设计的60s内限制点赞五次
结构文章ID(passageId)该字段记录点赞的具体文章ID,因为Id具有唯一性,唯一标识一篇文章,所以对该字段进行记录。
用户ID(uid)该字段记录点赞用户的ID,同上述文章ID,都具有唯一性,所以记录该字段。
点赞次数(count)初始化时默认为1,因为该数据结构为点赞一次发生所创建的,默认值为1,后面再次点赞需要对该点赞次数变量进行更改和同步
时间轴(time_history)这也是该数据结构中比较核心的功能
使用 PriorityQueue<Date> 这个优先队列进行存储
由于我们该点赞是基于该点赞时间点前限制一个小时内只能点赞五次,假如我现在1点20分 ,1点22分,1点23分,1点30分,1点40分,这几个时间点 点了5此赞,这时候我们1点45分再去点一次赞就会打印点不了赞(Sout),但是如 ...
缓存击穿,缓存穿透,缓存雪崩
缓存穿透什么是缓存穿透?
我们使用Redis大部分情况都是通过Key查询对应的值,假如发送的请求传进来的key是不存在Redis中的,那么就查不到缓存,查不到缓存就会去数据库查询。假如有大量这样的请求,这些请求像“穿透”了缓存一样直接打在数据库上,这种现象就叫做缓存穿透。
分析:
关键在于在Redis查不到key值,这和缓存击穿有根本的区别,区别在于缓存穿透的情况是传进来的key在Redis中是不存在的。假如有黑客传进大量的不存在的key,那么大量的请求打在数据库上是很致命的问题,所以在日常开发中要对参数做好校验,一些非法的参数,不可能存在的key就直接返回错误提示,要对调用方保持这种“不信任”的心态。
1、把无效的Key存进Redis中。如果Redis查不到数据,数据库也查不到,我们把这个Key值保存进Redis,设置value=”null”,当下次再通过这个Key查询时就不需要再查询数据库。这种处理方式肯定是有问题的,假如传进来的这个不存在的Key值每次都是随机的,那存进Redis也没有意义。
2、使用布隆过滤器。布隆过滤器的作用是某个 key 不存在,那么就一定不存在,它说某个 ...
ConcurrentHashMap
ConcurrentHashMap属性// 最大容量,同 hashmapprivate static final int MAXIMUM_CAPACITY = 1 << 30;// 默认大小 16private static final int DEFAULT_CAPACITY = 16;// 数组的最大值static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;// 并发等级 ,不可修改 默认 16private static final int DEFAULT_CONCURRENCY_LEVEL = 16;// 负载因子 0.75private static final float LOAD_FACTOR = 0.75f;// 转成树链表最大长度 8static final int TREEIFY_THRESHOLD = 8;// 转链表的节点数static final int UNTREEIFY_THRESHOLD = 6;// 最小转换步长 16 常量不可修改 (不太重要)private static fi ...
类加载完整流程
1.加载
“加载”(Loading)阶段是整个“类加载”(Class Loading)过程中的一一个阶段,希望读者没有混淆这两个看起来很相似的名词。在加载阶段,Java虛拟机需要完成以下三件事情:
1)通过一个类的全限定名来获取定义此类的二进制字节流。
2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
3)在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
类被加载到方法区中后主要包含运行时常量池、类型信息、字段信息、方法信息、类加载器的 引用、对应class实例的引用等信息。 类加载器的引用:这个类到类加载器实例的引用对应class实例的引用:类加载器在加载类信息放到方法区中后,会创建一个对应的Class 类型的 对象实例放到堆(Heap)中, 作为开发人员访问方法区中类定义的入口和切入点。
Java类的加载过程_Java类-IT乾坤技术博客 (itqiankun.com)
简洁来说
一个Java文件从编码完成到最终执行,一般主要包括两个过程:编译和运行,其中编译就是把我们写好的java文件,通过jav ...
ThreadPoolExecutor
线程池优点
降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
提高响应速度。当任务到达时,任务可以不需要等到线程池创建就能立即执行。
提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。但是,要做到合理利用线程池,必须对其实现原理了如指掌。
线程池创建public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler)
...
InnoDB存储引擎
Innodb
支持事务,1.2版本支持全文索引
行锁设计,支持外键
文件结构:Mysql可以将每个InnoDB存储引擎的表单独存放到一个独立的ibd文件中(包括索引和数据),该引擎采用了聚集的方式,因此每张表的存储都是按主键的顺序进行存放,若没有显示(人为)地在表定义主键,InnoDB存储引擎会为每一行生成一个6字节的ROWID,并以此作为主键
InnoDB通过使用多版本并发控制(MVCC)来获得高并发性,实现了四种隔离级别,默认为Reapeatable。
处理数据过程,提供插入缓冲,二次写,自适应哈希索引,预读等高性能,高可用功能。
MyISAM
不支持事务,表锁设计,支持全文索引
与其他存储引擎与众不同的是它的缓冲池只缓存索引文件,而不缓冲数据文件
文件结构:MyISAM存储引擎表由MYD,MYI组成,MYD存储数据文件,MYI存储索引文件,非聚集(分离)
InnoDB体系结构InnoDB存储引擎有多个内存块,可以认为这些内存块组成了一个大的内存池,负责维护如下工作:
维护所有进程/线程需要访问的多个内部数据结构
缓存磁盘上的数据,方便快读的读取,同时在对磁盘文件的数据修改之 ...
Semaphore、ArrayBlockingQueue实现哲学家问题和生产者-消费者问题
哲学家问题问题阐述
有5个哲学家共用一张圆桌,分别坐在周围的5张椅子上
在圆桌上有5个碗和5只筷子,他们的生活方式是交替地进行思考和进餐。
平时,每个哲学家进行思考,饥饿时便试图拿起其左右最靠近他的筷子,只有在他拿到两只筷子时才能进餐。
进餐完毕,放下筷子继续思考。
代码及问题版本1public class SmartPersonProblem { static final int total = 5; //哲学家为5人 static class philosophy extends Thread{ int number; //哲学家编号 ArrayList<Semaphore> fork; public philosophy(int i, ArrayList<Semaphore> fork){ this.number = i; this.fork = fork; } public vo ...
JVM经典题
栈内存问题什么情况下会发生栈内存溢出。
思路: 描述栈定义,再描述为什么会溢出,再说明一下相关配置参数,OK的话可以给面试官手写是一 个栈溢出的demo。
解1.栈是线程私有的,他的生命周期与线程相同,每个方法在执行的时候都会创建一个栈帧,用来存储局部变量表,操作数栈,动态链接,方法出口等信息。局部变量表又包含基本数据类型,对象引用类型2.如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常,方法递归调用产生这种结果。3.如果Java虚拟机栈可以动态扩展,并且扩展的动作已经尝试过,但是无法申请到足够的内存去完成扩展,或者在新建立线程的时候没有足够的内存去创建对应的虚拟机栈,那么Java虚拟机将抛出一个OutOfMemory 异常。(线程启动过多)4.参数 -Xss 去调整JVM栈的大小
JVM内存模型问题详解JVM内存模型
思路: 给面试官画一下JVM内存模型图,并描述每个模块的定义,作用,以及可能会存在的问题,如栈 溢出等。
解程序计数器:当前线程执行的字节码的行号指示器,用于记录正在执行的虚拟机字节指令地址,线程私有虚拟机栈: ...
Redis实现分布式锁
分布式锁特征
互斥性
任意时刻,只有一个客户端能持有锁
锁超时释放
持有锁超时,可以释放,防止死锁
可重入性
一个线程获取锁之后,可以再次对其请求加锁
高可用,高性能
加锁和解锁需要开销尽可能低,同时也要保证高可用
安全性
锁只能被持有的客户端删除,不能被其他客户端删除
锁满足条件
互斥性。在任意时刻,只有一个客户端能持有锁。
不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。
具有容错性。只要大部分的Redis节点正常运行,客户端就可以加锁和解锁。
解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。
代码实现首先导入spring-data-redis依赖<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId></dependency& ...
Redis持久化详解(RDB,AOF)
Redis 运行时数据保存在内存中, 一旦重启则数据将全部丢失.
Redis 提供了两种持久化方式:
RDB 持久化: 生成某个时间点的快照文件
AOF 持久化(append only file): 日志追加模式(Redis协议格式保存)
Redis可以同时使用以上两种持久化
RDB 持久化执行 rdb 持久化时, Redis 会fork出一个子进程, 子进程将内存中数据写入到一个紧凑的文件中, 因此它保存的是某个时间点的完整数据。
如有需要,可以保存最近24小时的每小时备份文件,以及每个月每天的备份文件,便于遇到问题时恢复。
Redis 启动时会从 rdb 文件中恢复数据到内存, 因此恢复数据时只需将redis关闭后,将备份的rdb文件替换当前的rdb文件,再启动Redis即可。
优点
rdb文件体积比较小, 适合备份及传输
性能会比 aof 好(aof 需要写入日志到文件中)
rdb 恢复比 aof 要更快
缺点
服务器故障时会丢失最后一次备份之后的数据
Redis 保存rdb时, fork子进程的这个操作期间, Redis服务会停止响应(一般是毫秒级),但如果数据量大且cp ...