强烈推荐一个大神的人工智能的教程:http://www.captainai.net/zhanghan
复习指南
- 一、mysql篇
- 二、Redis篇 [传送门](https://www.cnblogs.com/hunternet/p/12742390.html)
- 三、java基础
- 1.[hash、一致性hash、redis的hash槽](https://www.zsythink.net/archives/1182)
- 2.[String、StringBuilder、StringBuffer](https://www.cnblogs.com/dolphin0520/p/3778589.html)
- 3.[List、Map](https://blog.csdn.net/feiyanaffection/article/details/81394745)
- 4.[单例模式](https://www.cnblogs.com/xz816111/p/8470048.html)
- 5.[volatile](https://www.cnblogs.com/jay-huaxiao/p/13473197.html)
- 6.[TCP和UDP](https://www.cnblogs.com/fundebug/p/differences-of-tcp-and-udp.html)
- 7.三大限流算法
- 8.多线程
- 9.[如何构建高可用系统](https://www.cnblogs.com/javaguide/p/12216564.html)
一、mysql篇
1.索引
- 数据结构:传送门
二叉树、平衡二叉树、B树、B+树 - 索引类型
聚簇索引【主键索引,叶子节点存储的是整行数据】
非聚簇索引【非主键索引,叶子节点存储的是该行数据的主键id】
innodb使用的是聚簇索引,因为他是数据和索引在一起的 - 回表
基于普通索引查询,查询到主键id后需要回到主键索引上再查询一次,取到真正的数据 - 覆盖索引
- 前缀索引
- 索引下推:传送门
-- 联合索引(name,age)
select * from tuser where name like '张%' and age=10 and ismale=1;
在MySQL 5.6之后,引入索引下推,在索引内部就可以判断符合条件的
有效减少回表次数,提高检索效率
2.锁
📢:锁不是用完立即释放,而是会等到事务结束才会释放
- 全局锁:锁定整个数据库为只读状态,用于备份过程
- 表锁:MDL读锁和MDL写锁,读读共享,读写互斥,写写互斥
- 行锁:两阶段锁协议,读锁、写锁
- 间隙锁:前后都是开区间,解决了幻读的问题
幻读:一个事务在前后两次查询同一个范围的时候,后一次查询查看到了前一次查询没有查看到的行【新增的行】 - next-key lock【行锁+间隙锁,前开后闭】
加锁规则:
a.原则1:加锁的基本单位是next key lock
b.原则2:查找过程中访问到的对象才会加锁
c.优化1:等值查询,唯一索引加锁时,next-key lock退化为行锁;范围查询,不会退化
d.优化2:非唯一索引上的等值查询,向右遍历时最后一个值不满足等值条件的时候,next-key lock退化为间隙锁
e. bug:唯一索引上的范围查找会查到不满足条件的第一个值为止【例如 id<=15,并不是到15就结束了,而是会继续向后查找知道不为15为止】 - 死锁和死锁检测
3.事务
- 隔离级别
读未提交:一个事务还未提交时,所做的变更可以被其他事务看到【读取行的最新值】
读提交:一个事务提交后,所做的变更可以被其他事务看到【每个sql执行的时候创建的】
可重复读:一个事务在执行过程中看到的数据都和该事务开始时看到的一致【事务开始时创建的视图】
串行化:当出现读写锁冲突时,后一个事务需要等到前一个事务执行完才能执行【通过加锁实现】
4.主从
-
如何保证主从一致
从库执行主库的binlog,binlog格式设置为row,因为设置为statement会出现主从选择索引不一致的情况,就导致范围查找且limit操作的时候,结果不一致delete from t /*comment*/ where a>=4 and t_modified<='2018-11-10' limit 1;
-
主备延迟的原因
从库所在机器性能差
备库压力大,例如:备库执行了一些分析查询等操作
主库执行大事务:binlog是整个事务完成才写 -
主从切换
可靠性切换
步骤二中两个库都是只读状态,会造成不可用,但是数据可靠
可用性切换
立马切换过去,不等binlog同步是否有延迟,可能会造成数据不一致
5.查大表会把内存打爆吗?
有两种方式:存内存的和使用net_buffer_length边读边发【写满了就清理】
6.join如何使用?
join中有两个角色,驱动表和被驱动表。
-
NLK:可以使用上索引的可以作为被驱动表,走索引,扫描次数会少。可以使用MRR算法优化到BKA
-
BNL:被驱动表使用不上索引的时候,小表作为驱动表,驱动表数据会加载到 join_buffer中,当加载不下时,会分段,段越少,扫描被驱动表的次数越少。可以优化到BKA
两种优化方式都是借助buffer,提供批量数据,让被驱动表可以批量匹配
7.临时表
线程自己可见,所以多个线程同时建临时表不用担心重名等问题。线程执行结束后,临时表会自动被删除,所以不用考虑资源回收等问题。
待查问题
-
我们的数据量
6000万数据,32张表,师生关系
5400万数据,128张表,师生间发送的消息 -
分表如何实现的
-
优化器做了哪些优化,传送门
查询条件简化
外连接消除
IN查询 -
主从
-
mysql为什么选择RR
-
可重复读为什么没有解决幻读
关键点:快照读和当前读。在更新的时候是当前读
二、Redis篇 传送门
1. 五种基本数据结构
- String
- Hash:压缩列表(数据小)/hash表(数据大,从前者结构转换为后者结构),rehash采用渐进hash,同时保留两个哈希表,在字典中设置一个计数器hashidx,每次操作新增/查询/删除操作的时候,都会将hash值在hashidx上的rehash到新的哈希表上,直到旧的哈希表全部被rehash,这个计数hashidx会置为-1,旧hash表回收
解决散列冲突:开放寻址法和链表法;开放寻址法需要提前分配内存,链表法是需要的时候再分配,所以链表法内存占用会更少
使用场景:购物车、计数器(签到天数等) - List:顺序,可重复,压缩链表(数据小)/LinkedList/quickList(压缩链表和linkedList的结合)
使用场景: 栈、队列 - Set:整数集合/字典;无序,不重复,可做多个集合的交集、并集、差集,时间复杂度O(N)
使用场景: 标签,共同爱好 - Zset:压缩列表/跳跃表;有序,不重复,时间复杂度O(logN)
使用场景: 点赞排名,考试排名
2. 项目中的使用场景
①:老师给学生的备注名
采用hash存储,设置1小时过期时间,老师设置学生备注的时候做删除动作
②:过去8小时产生的订课,取消课
采用zset存储,操作时间作为score
取数据使用范围取过去8小时到现在时间段内的数据
3.String是如何存储的?
4. incr和decr做接口防刷和次数限制
例如:每秒钟接口访问次数、每秒钟短信验证码访问次数
incr和decr命令内部分两步执行:
key存在,则+1或-1,返回具体结果值
key不存在,则设置初始值为0,然后执行+1或-1,返回具体结果值
通过给key设置过期时间,控制多长时间内访问次数
5. setnx和setex
6.redis如何保证和DB数据一致性
https://blog.csdn.net/wwd0501/article/details/106902856
7.雪崩和缓存击穿
雪崩:大量key同时过期,导致请求都去请求DB,增大DB压力。
------->解决方法:key的过期时间加一个随机时间,减少大批同时过期的情况
缓存穿透:redis也没有数据,DB也没有数据,白白走了很多次DB请求
------->解决方法:不存在的数据查询后也set到缓存,请求的时候请求到空数据直接返回,不请求数据库;布隆过滤器
8.持久化RDB和AOF
RDB: 快照,主进程fork子进程(fork会阻塞),子进程负责生成RDB文件。
存在的问题:生成期间不能保证数据准确性,生成需要时间,生成期间,做的更新会记录到RDB文件中,导致这破坏了某一时刻的数据
AOF: 命令执行时,同时写AOF缓存,之后同步缓存指令到磁盘,接近实时,
同步有三种:
a. 实时同步,性能较差
b. 每秒钟执行一次同步,在极端情况下会丢失1s的数据
c. 不设置同步时间,默认最多间隔30s执行同步
磁盘上AOF文件过大问题解决:AOF重写,将当前读到的key-value写一条命令set到新的AOF文件中,这样之前的多条指令就被整合成了一条,过程期间写入的指令记录到AOF重写缓冲区中,AOF重写完毕后会给主进程发一个信号,主进程收到后将阻塞,然后将AOF重写缓冲区的指令追加到新的AOF文件中;AOF重写缓冲区保证了数据不丢失。
9.redis集群
a. 主从复制:读写分离
b. 哨兵:主节点有问题时,可以自动选主
c. cluster(我们公司使用的这种模式):每个节点上存储的数据不一样,redis共16384个时间槽
10.如何证明redis是单线程的
三、java基础
1.hash、一致性hash、redis的hash槽
2.String、StringBuilder、StringBuffer
3.List、Map
https://zhuanlan.zhihu.com/p/78079598
如何判断有环:https://www.cnblogs.com/hhx626/p/7534615.html
指针追赶法和查表法
- 用hashmap存储1万的数据,需要考虑什么?
考虑初始化容量,避免频繁扩容。hashmap提供了一个构造函数初始化容量。但是这个初始化只是计算了负载因子和table容量,在第一次put的时候才根据table容量初始化,当达到负载因子*table容量的限制时就会触发resize()扩容
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
this.threshold = tableSizeFor(initialCapacity);
}
static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
-
hashmap链表长度达到8会由二叉树转为红黑树
https://www.jianshu.com/p/1286f9144475
https://blog.csdn.net/baidu_37147070/article/details/98785367
二叉树可能会由于插入顺序的影响产生树倾斜,所以树的高度决定了效率。hashmap由数组+链表结构组成,结构转换步骤:
① 在链表高度达到8的时候先会判断数组表容量是否超过64,未超过则扩容
②超过64则转换为红黑树,红黑树是一种近平衡的二叉树,有效减少树倾斜的情况,提高查询效率 -
ArrayList扩容原理是什么
默认大小是10,若初始化时指定了大小,则会根据指定的大小进行初始化,之后每次add新元素时会先校验容量是否足够,当存满的时候会触发扩容,扩为当前容量的1.5倍
//①构造函数初始化
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
//②添加新元素
public boolean add(E e) {
//添加新元素时校验容量是否足够,存满了就触发扩容,扩为当前容量的1.5倍
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
//③确认容量
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//④扩容
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//扩为当前的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
4.单例模式
5.volatile
6.TCP和UDP
TCP三次握手保证数据安全传输到服务端,UDP是无连接的,只管发,不保证能正确的到达服务端