- Redis的key和value可以存储的最大值分别是多少?
- 怎么利用Redis实现数据的去重?
- Redis什么时候需要序列化?Redis序列化的方式有哪些?
- MySQL的B+树的高度怎么计算?
- 线程池的状态有哪些?获取多线程并发执行结果的方式有哪些?
- 线程池原理?各个参数的作用。
- ThreadLocal的使用场景有哪些?原理?内存泄漏?
- kafka是如何保证消息的有序性?
- Nacos的选举机制了解嘛?说下Raft算法?
- 聊一聊TCC补偿机制
1、Redis的key和value可以存储的最大值分别是多少?
- 虽然Key的大小上限为
512M
,但是一般建议key的大小不要超过1KB
,这样既可以节约存储空间,又有利于Redis进行检索。 - value的最大值也是
512M
。对于String类型的value值上限为512M
,而集合、链表、哈希等key类型,单个元素的value上限也为512M
。
2. 怎么利用Redis实现数据的去重?
- Redis的
set
:它可以去除重复元素,也可以快速判断某一个元素是否存在于集合中,如果元素很多(比如上亿的计数),消占用内存很大 - Redis的
bit
:它可以用来实现比set内存高度压缩的计数,它通过一个bit设置为1或者0,表示存储某个元素是否存在信息。例如网站唯一访客计数,可以把user_id
作为 bit 的偏移量 offset,如设置为1表示有访问,使用1 MB的空间就可以存放800多万用户的一天访问计数情况。 - HyperLogLog:实现超大数据量精确的唯一计数都是比较困难的,
HyperLogLog
可以仅仅使用 12 k左右的内存,实现上亿的唯一计数,而且误差控制在百分之一左右。 - bloomfilter布隆过滤器:布隆过滤器是一种占用空间很小的数据结构,它由一个很长的二进制向量和一组Hash映射函数组成,它用于检索一个元素是否在一个集合中
- 如果你现在正在学习java,我这有自己整理的一些开发工具,学习手册,PDF的课件资料,可以加入我的Java技术交流圈603835449获取,希望能帮助到孤军前行的你
3. Redis什么时候需要序列化?Redis序列化的方式有哪些?
大家先回忆下Java序列化,什么时候需要序列化?
- 序列化:将 Java 对象转换成字节流的过程。
- 反序列化:将字节流转换成 Java 对象的过程。
为什么需要序列化呢?
打个比喻:作为大城市漂泊的码农,搬家是常态。当我们搬书桌时,桌子太大了就通不过比较小的门,因此我们需要把它拆开再搬过去,这个拆桌子的过程就是序列化。而我们把书桌复原回来(安装)的过程就是反序列化啦。
- 比如想把内存中的对象状态保存到一个文件中或者数据库中的时候(最常用,如保存到redis);
- 再比喻想用套接字在网络上传送对象的时候,都需要序列化。
RedisSerializer接口 是 Redis 序列化接口,用于 Redis KEY 和 VALUE 的序列化
- JDK 序列化方式 (默认)
- String 序列化方式
- JSON 序列化方式
- XML 序列化方式
4. MySQL的B+树的高度怎么计算?(比如有100w的数据,字段为int类型)
InnoDB存储引擎最小储存单元是页,一页大小就是16k。
B+树叶子存的是数据,内部节点存的是键值+指针。索引组织表通过非叶子节点的二分查找法以及指针确定数据在哪个页中,进而再去数据页中找到需要的数据;
假设B+树的高度为2的话,即有一个根结点和若干个叶子结点。这棵B+树的存放总记录数为=根结点指针数*单个叶子节点记录行数。
- 如果一行记录的数据大小为1k,那么单个叶子节点可以存的记录数 =16k/1k =16.
- 非叶子节点内存放多少指针呢?我们假设主键ID为bigint类型,长度为8字节(面试官问你int类型,一个int就是32位,4字节),而指针大小在InnoDB源码中设置为6字节,所以就是8+6=14字节,16k/14B =16*1024B/14B = 1170
因此,一棵高度为2的B+树,能存放1170 * 16=18720条这样的数据记录。同理一棵高度为3的B+树,能存放1170 *1170 *16 =21902400,也就是说,可以存放两千万左右的记录。B+树高度一般为1-3层,已经满足千万级别的数据存储。
5、线程池的状态有哪些?获取多线程并发执行结果的方式有哪些?
线程池和线程的状态是不一样的哈,线程池有这几个状态:RUNNING,SHUTDOWN,STOP,TIDYING,TERMINATED
。
//线程池状态
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
线程池各个状态切换状态图如下:
RUNNING
- 该状态的线程池会接收新任务,并处理阻塞队列中的任务;
- 调用线程池的shutdown()方法,可以切换到SHUTDOWN状态;
- 调用线程池的shutdownNow()方法,可以切换到STOP状态;
SHUTDOWN
- 该状态的线程池不会接收新任务,但会处理阻塞队列中的任务;
- 队列为空,并且线程池中执行的任务也为空,进入TIDYING状态;
STOP
- 该状态的线程不会接收新任务,也不会处理阻塞队列中的任务,而且会中断正在运行的任务;
- 线程池中执行的任务为空,进入TIDYING状态;
TIDYING
- 该状态表明所有的任务已经运行终止,记录的任务数量为0。
terminated()
执行完毕,进入TERMINATED状态
TERMINATED
- 该状态表示线程池彻底终止