面试知识点整理
每次面试之前都要准备好多知识点,有些知识点平时工作不常用就忘了,一到要准备的时候就全网到处找教程,索性自己整理一下放在一起方便以后看吧
一 HashMap原理
总览:hashmap底层结构为数组+链表+红黑树,线程不安全,异步,支持null,查找插入都是常数时间的时间复杂度。
结构:数组被分为一个个的桶,每个桶存一个或者多个entry对象,每个entry对象包括key value 指向下一个entry的指针 哈希值相同的entry对象就会以链表的形式存储,如果链表长度超过8且数组长度超过64就会转换成红黑树。
扩容:hashmap默认容量16,加载因子默认0.75,扩容阈值默认12,超过阈值则扩容为原来的2倍,最大扩到2的31次方(扩容到2倍可以减少哈希碰撞,散列分布
二 红黑树相关
总览:红黑树是一种自平衡的二分查找树,
结构定义:根节点一定为黑;不能有相连的红;认为叶子节点下方会挂两个空的黑节点;节点到null节点的任意路径的黑色节点数量相同
数据库索引为什么用了b+树而不是b树或红黑树:
b+树是一种多叉树,除了叶子节点存了整个数据之外 其他层的节点都只存了索引值,节约空间,使一次磁盘io可以读出更多数据;而叶子节点之间使用链表形式连接,方便进行范围查找。
b树与b+树不同之处在于b树的每一层都存了数据域,增加空间,减少了io读出的节点数;
红黑树由于层数过多 磁盘io次数也会增加,作为数据库这种数据量巨大的索引时查找速度很慢。
三 分布式锁
在单体程序中 我们可以通过语法自带的锁进行加锁,比如java中的sycronize,c#重的lock,但是在微服务架构中,各个服务之间相互独立,需要一个公共的资源作为锁来实现分布式的锁,分布式锁大致了解到的是数据库锁,redis分布式锁,zookeeper分布式锁。
数据库锁:利用主键索引唯一性,同一时间多条数据同时操作只会有一条生效,想释放的时候直接删除数据即可,但是强依赖于数据库。
redis分布式锁:setnx命令;set if not exeit,如果key不存在则设置成功返回1获取锁成功,否则返回0获取锁失败,释放锁则删除key即可;expire命令:设置过期时间防止死锁
zookeeper没用过,待完善。
四 redis相关
1.数据类型
redis维护了一个redisobject对象存储数据,内部字段包括type,encoding,ptr;type表示这个对象是string,hash,list,set,zset中的一种,encoding表示底层结构,包括
#define REDIS_ENCODING_RAW 0 // 编码为字符串,redis的字符串是redis自己实现的一种简单动态字符串,性能优于c语言的原生字符串。
#define REDIS_ENCODING_INT 1 // 编码为整数
#define REDIS_ENCODING_HT 2 // 编码为哈希表,使用哈希表作为底层实现,具体原理同上文中hashmap实现,同样存在哈希碰撞(冲突),扩容的问题不同之处在于 redis的哈希扩容采用渐进式rehash,防止过多数据一次扩容造成的卡顿
#define REDIS_ENCODING_ZIPMAP 3 // 编码为 zipmap
#define REDIS_ENCODING_LINKEDLIST 4 // 编码为双端链表,redis自己实现了这个结构
#define REDIS_ENCODING_ZIPLIST 5 // 编码为压缩列表,redis为了节省内存开发的一种数据结构,由一段特殊编码的连续内存组成的顺序结构。不是某个压缩算法,是连续内存,节省空间。
#define REDIS_ENCODING_INTSET 6 // 编码为整数集合,存整数集合的
#define REDIS_ENCODING_SKIPLIST 7 // 编码为跳跃表,这个好玩,类似于翻书之前先找目录,哪部,哪章,最后哪页,用于有序链表的查找效率非常高,典型的以空间换时间。
ptr是一个指针表示指向的对象的value;
string:是最常用的数据类型
hash:keyvalue结构,用于存储对象
list:可用作队列
set:集合
zset:有序集合,做排行榜
geo什么什么。。。就是存地理信息那个,可以根据经纬度算距离啥的,功能很好
bitmap:位存储,只存状态,登陆未登录打卡未打卡
hyperlog:基数统计,对数据正确率要求不高的时候用
2.redis持久化
包含两种方式,rdb和aof,前者存快照,可以在配置文件中配置触发时间条件,也可以手动调用save或bgsave,再或者flushall的时候也会触发持久化,主从备份的时候会fork一个子进程,子进程进行数据持久化并发送slave节点,同时会开辟一块内存共享的区域用于及存储这个过程中新增数据,当备份结束的时候slave节点会把这个区域的数据同步过来,完成整个备份过程。
aof会每秒备份一次,存的是操作,可以定期的清理aof文件(大雾,是整理)因为比如set bar 1和set bar 2两条 只有一条是有效的结果。
rdb和aof可以同时配置,默认不开aof,如果同时开,会默认aof备份,因为aof更全。
3.哨兵模式
配置奇数个哨兵模式启动的redis,一主多从,和同样奇数个哨兵节点用于监视redis状态,如果主节点挂掉了,会选举出来一个主哨兵节点,由这个节点决定把哪个从节点升级主节点,等主节点恢复了,会又挂上去成为从节点。(应对主从切换的时候需要手动切换的问题)
4.主从配置
这个该放哨兵模式前边写,懒得改序号了就这样把,就是主从模式,一主多从,主从读写分离,但是无法自动切换挂掉的主节点,这里可以引申出来上边说的redis持久化rdb主从备份的问题,就是slave节点连接到master之后会主动进行一次主从复制。
5.集群模式
这个好,解决了烧饼模式只有一个节点的问题,同时又实现了哨兵模式那种高可用的功能。
就是redis cluster
三主六从的结构,所有主节点会分配16384个哈希槽,从节点可以挂载到主节点上,配置文件写好之后启动所有rediscluster服务,直接用redis cluster replicas 每个redis服务的ip端口分配哈希槽就好了,如果需要扩容就要从原有的redis服务中分出一些哈希槽给新的服务。
所有主节点存了不同数据,可以访问任意端口获得整个集群任意主节点上的数据
6.缓存穿透
缓存和数据库都里没有这条数据,并发访问所有数据都打到数据库上,造成数据库崩溃;可以存空,或者用布隆过滤器
7.缓存击穿
某点大并发,key失效的瞬间请求数据库,缓存里没有数据库里有,单条数据反复查数据库造成崩溃;可以设置热点数据永不过期,可以加分布式锁,只保证一个线程进去
8.缓存雪崩
某时间缓存集体失效过期,数据库里有,和前者不同在于雪崩是多条数据同时查库;设置随机过期时间
9.redis和数据库双写一致性
a.读写放入一个队列(不推荐,串行操作,大并发情况下太慢)
b.读先读缓存,缓存没有就读库,并从库里取出放到缓存里;写的时候先删缓存, 然后更新库,是一种lazy的思想。提高效率
10.redis优化
a.数据结构的选择,该用bitmap用bitmap,该用hash用hash
b.key *少用,key用短点
c.设置过期时间,选择合适的淘汰策略
d.如果不用备份持久化,在配置文件里关闭这个功能
e.可以使用管道功能,减少访问次数提高效率
五 mysql分库分表
使用mycat进行拆分,水平分表是有分表算法的,可以根据id的范围分,可以id取模分,可以对id进行哈希分。
mycat的schma.xml文件重可以设置主库从库。
六 数据库的隔离级别
1.读未提交:一个事务能读到另一个事务未提交的修改数据
2.读已提交:一个事务只能读到另一个事务已经提交的修改数据,解决脏读
3.可重复读:事务执行过程中看到的结果前后一致,解决脏读,不可重复读;mysql用mvccc解决了幻读
所以mvcc是个啥。。。
MVCC:事务开启时会给数据库做一个快照,向innodb引擎申请一个事务id,每行数据有多个版本,每次事务更新行的时候生成一个新的数据库版本,并把这个事务id复制给这个新的版本,作为row trx_id,也就是一行数据可能有多个版本
4.串行读:无需解释
七 数据库优化
软优化
1.加索引
2.sql优化,dml,ddl
3.表结构优化包括数据结构使用等
硬优化
1.硬件优化,网络优化
2.数据库配置优化
3.分库分表
扩展:
索引的原理相关 聚簇 非聚簇 b+树 索引失效原因等
注意小tip:grant之后一般不需要随手加flush privileges,grant操作会同时更新内存和磁盘,如果使用了dml语句更改权限,则要加flush privileges 用于删除内存中的权限,重新加载磁盘中的权限
八 线程与进程
a. 语言相关:
c#中 task的底层实现还是线程,但是task和线程之间不是一对一的关系,也就是说是个task不一定要开十个线程,task对线程的操作更为精确,节省资源。task开启方法有两种,一种是直接new,然后task.start();另一种是用工厂方法task.Factory.StartNew();task中可以用isCancelltiontoken控制task的停止。
b. 操作系统层面
线程是cpu调度的最小单位,进程是系统资源分配的最小单位,一个进程里可以有多个线程。
系统开始一个进程时需要分配内存空间,需要维护一个数据表去管理这个线程中的代码段,堆栈等,而线程是可以共享内存空间的,开销要小于进程
线程之间的通信很方便,因为可以共享内存空间,但是要注意多线程出现的死锁问题,而进程之间则需要进程间通信。
多进程程序会更加健壮,如果一个线程死掉了会拖垮整个进程。
九 jwt
header payload signecture进行曲哈希,取模等运算,最后给出一个token,只能验证身份来源,无法加密 payload设置过期时间
十 restful风格
是一种api的风格,比如post进行新增,get获取,put更改,delete删除;url中添加版本号,httpcode判断响应是否成功。
十一 post和get
post和get都是http请求,根源上来说二者没有本质区别,http协议相当于货运,get和post相当于两种货车,前者把货物放在车顶,后者把货物放在车厢,如果非要把get的车厢里也放货物,post车顶也放货物,顶多就是浏览器或者服务器无法解析多出的货物。
二者表象上的区别如上文所述,get的url最长一般不超过2k,最大的区别是get只发送一个tcp包,post会发两次,第一次发header第二次发body。