2021-3-18 V1.0:是10家左右一线和二线互联网企业的面试题集合。这次只是把题目挂上来,有些捎带了不完善的答复,答案仅供参考,既不完善也不权威哈。后面我再逐个完善答案上来。
2021-4-29 V1.1:增补答案
比如,hr问你,你对薪资的预期多少?你直接回答一个确定的薪资,或薪资范围,其实就是把自己能谈薪资的机会给封死了。正确的回答应该是:先反问对方,公司的薪资结构是怎样的?其中,薪资结构包括:1.五险一金2.每个月的工资构成(底薪+绩效+餐补+交通补等等)3.12个月的基本薪资+季度奖金+年终奖等等4.涨薪机制(一年有多少次考核晋升调薪机会、KPI考核)、期权等等。HR在回答你时,起码包括这四项,如果对方没有全部说出来,则可以根据具体情况,再进行详细提问。在聊到五险一金时,如果你想了解的更细,还可以继续问对方五险一金的缴纳比例。对于休假、加班制度等等如果聊到时也可以顺带了解更多的信息,看看公司的薪资结构是否完善。最后,根据所了解到的信息、自己的年收入预算和岗位在招聘岗位上挂的薪资范围,进行算估值,然后和对方说:我希望的薪资是税前xx-xx(一个区间)来进行回答(这个范围比自己预估的高一些,避免HR有意压价而吃亏)这样谈薪资的才是完整的,也能体现出你的职业性。
这要从InnoDB的索引说起, InnoDB的索引是B+Tree 和 聚簇索引。
对主键索引来说:它只有在叶子节点上存储数据,它的key是主键,并且value为整条数据。
对辅助索引来说:key为建索引的列,value为主键。
这给我们两个信息:
(1) 根据主键会查到整条数据
(2)根据辅助索引只能查到主键,然后必须通过主键再查到剩余信息。
所以如果要优化count()操作的话,我们需要找一个短小的列,为它建立辅助索引。
MyISAM引擎把一个表的总行数存在了磁盘上,因此执行count()的时候会直接返回这个数,效率很高(没有where查询条件)。
InnoDB引擎并没有直接将总数存在磁盘上,在执行count()函数的时候需要一行一行的将数据读出来,然后累计总数。
参考文章:https://www.cnblogs.com/Chenjiabing/p/12625559.html
首先 MyISAM会把 count() 每次都算好存下来,查的时候直接返回,非常快;而 InnoDB 因为考虑到并发事务导致 count(*) 可能算的不准的问题,所以都是查的时候才算的,
【Innodb的主键索引是聚簇索引(包含了KEY,除了KEY之外的其他字段值,事务ID和MVCC回滚指针)所以主键索引一定会比二级索引(包含KEY和对应的主键ID)大,也就是说在有二级索引的情况下,一般COUNT()都不会通过主键索引来统计行数,在有多个二级索引的情况下选择占用空间最小的。一个优化方案就是预先建一个小字段并建二级索引(唯一索引)专门用来统计行数,极端情况下这种优化速度提高上千倍也是正常的。—— 知乎】
【当表存在主键或者唯一索引的时候count(),count(1)效率相同。count(唯一键索引字段)比count(非唯一键索引字段)效率高。
count(非唯一键索引,列存在null)与count(非唯一键索引,列不存在null)的效率差不多,不过count(唯一键索引,列存在null)的值比count()小,因为null的值不会被记录在索引中。】
综上,无脑选 count(*) 不会错。
(1)轮询(默认)
(2)加权重(weight)
(3)客户端 IP 地址hash方式(将客户端IP做哈希,保证相同IP过来的请求发到相同服务器,避免session跨服务器,但是毛病是当有服务器需要剔除时必须手工down掉)
(4)最少连接方式(least_conn)
(5)响应时间方式(第三方插件)
(6)url_hash(第三方插件,使相同的URL定向到同一台服务器,同一个资源只在一台服务器热点缓存以避免多台都缓存了且访问分散)
(1)缓存(尤其是热点数据缓存)
(2)排行榜(sortedSet)
(3)计数器/计时器(过期清理)
(4)充当消息队列(队列可以实现同步转异步)
(5)Session共享
(6)充当集合:交集/并集/差集
堆(新生代【新生代:老年代=1:2,有Eden区[8/10]、from区[1/10]、to区[1/10]】、老年代、永久代【jdk8及以后被元空间[直接内存]代替】)、栈、方法区、程序计数器
(1)是否开启逃逸分析,逃逸分析决定是否栈上分配;
(2)大对象(如大数组)直接进入老年代
(3)小对象进入新生代的Eden区
(4)……
"hello"在编译器已确定为常量,所以会被存放在常量池中。而 str 是"hello"常量池的地址(即引用),该引用是存于堆空间中。
(1)作为搜索、排序或分组的列才创建索引,仅作输出显示的不用
(2)将区分度高的列作为索引,因为区分度很低的索引,会被优化器优化成全表扫描
(3)尽可能选用短小的列作为索引
(4)只索引字符串的前缀或后缀(为了后缀匹配可以将存一个倒序的字段,比如卡号的conv),区分度足够高即可,无需全字符索引
(5)尽可能用组合索引替代多个的单独索引,并且建组合索引时尽量将区分度高的放在前面
(6)不要建太多的索引(增加索引重排的耗时、拖慢查询优化器和执行计划生成的速度、甚至可能导致MySQL无法使用最好的索引)
(7)order by 如果有多个索引字段的 order by,那么这些字段的排序方向(顺序)需要一致,不然也没法走索引。
ulimit用于shell启动进程所占用的资源.
2,类别:
shell内建命令
3,语法格式:
ulimit [-acdfHlmnpsStvw] [size]
4,参数介绍:
-H 设置硬件资源限制.
-S 设置软件资源限制.
-a 显示当前所有的资源限制.
-c size:设置core文件的最大值.单位:blocks
-d size:设置数据段的最大值.单位:kbytes
-f size:设置创建文件的最大值.单位:blocks
-l size:设置在内存中锁定进程的最大值.单位:kbytes
-m size:设置可以使用的常驻内存的最大值.单位:kbytes
-n size:设置内核可以同时打开的文件描述符的最大值.单位:n
-p size:设置管道缓冲区的最大值.单位:kbytes
-s size:设置堆栈的最大值.单位:kbytes
-t size:设置CPU使用时间的最大上限.单位:seconds
-v size:设置虚拟内存的最大值.单位:kbytes 5,简单实例:
CAS,Compare Ans Swap,比较替换算法,是乐观锁思想的一种实现。主要思路是先记录变量值,然后等到本事务要修改该变量值的时候,先比较一下当前变量值和之前记录的是否一致,如果一致则修改,如果不一致则说明有其他事务在修改,所以当前事务进入自旋等待状态。
1、解题思路
避免跳坑(比如每行之间收个数字大小关系是不确定的)
2、继续优化:行上查找,因为都是有序的,可以用二分查找。
平均复杂度是 O(nlgn)
CPU使用率/利用率:指CPU当前时间被占用的百分比
CPU负载:指某段时间占用CPU的进程和等待CPU的进程数
……
优先用组合索引,并且按前缀匹配原则,将区分度高的放前面(学号唯一,区分度最高,挂最前面,入学时间次之,性别查询区分度低:非男即女,区分度低的索引可能会被MySQL解析器和优化器认为还不如回到全表扫描中)……
劫持了DNS流量(请求),并向客户端返回了自己伪造的流量。
防范:对DNS解析的请求进行加密;客户端直接指定固定的DNS地址,不走公网的链路;客户端直接改 hosts 文件,写死某个域名对应的服务器 IP
版本、头部长度、总长度、TTL(生存时间)、偏移量(滑动窗口)、源地址、目标端地址、报文体(用户数据)、可选选项、填充位……
CSRF:跨站请求伪造。用户访问正常网站,然后又访问了攻击网站,攻击网站含恶意代码/脚本,用户访问攻击网站就会执行该脚本,然后不知不觉向正常网站发送了请求。
防范策略:(1)Referer(请求的来源地址)检查;(2)在 url 或 http header 中添加 随机数token 并验证
XSS:跨站脚本攻击;攻击者提前在正常网站上注入恶意脚本,当用户访问正常网站然后执行该恶意脚本,这个脚本可以把用户cookie之类的发送到攻击者位置。
防范策略:对用户输入进行过滤,尤其是脚本相关的关键字过滤。
TTL:Time To Live 生存时间值,指定IP包被路由器丢弃之前允许通过的最大网段数量(即最大允许跳多少级路由)。
《深入浅出HTTPS》《高性能MySQL》《TCP/IP详解》《HTTP权威指南》《深入理解Java虚拟机》《MySQL技术内幕 Innode存储引擎》《微服务架构与实践》
MVVM:Model-View-ViewModel,Model 是后端传递的数据,View就是页面,ViewModel是连接 View 和 Model 的桥梁,一方面负责将 Model 转化成 View ,通过数据绑定实现,另一方面负责将 View 转换成 Model,通过 Dom 事件监听实现,这两个方面就叫做双向绑定。
Linux 文本处理命令三剑客:grep、awk、sed
awk 用过来做文本替换、文本输出
……
mkdir、find、mv、cp、grep、file【通过探测文件内容判断文件类型】、dos2unix、du -sh、ls
堆(新生代【eden、from、to】、老年代、永久代)、栈(虚拟机栈、本地方法栈)、方法区(静态方法、常量池)、程序计数器
内存分配失败,触发 GC,分代回收【Minor GC】 -->full GC,存活对象记录 GC存活次数 从 Eden 和 To 区挪入 From 区,From 区进入老年代 --> full GC 失败 触发 OOM
微服务源于单体应用缺点的改进:
(1)维护成本增加:
(2)交付周期长(不易于开发测试);
(3)新人培养周期长;
(4)技术更新成本高;
(5)可伸缩性差/效率低;
(6)不利于构建全功能团队:单体应用的开发模式往往以技能为单位来分工,比如 UX 团队、服务端团队、数据库团队。这样的分工可能会导致任何功能改变都需要跨团队的沟通协调。
好处:去中心化、解耦【代码逻辑解耦(开发人员解耦)、功能解耦、数据库解耦】、方便扩容【可扩展性】、服务监控及治理、支持多语言实现。
“微”有粒度之分。
坏处:业务执行耗时损失、运维压力
微服务的特点:
(1)小:代码行数小、重写时间小、团队小、独立的业务单元、团队能够自治、服务的职责单一
(2)独立——独立的进程
(3)轻量——轻量级通信机制:采用基于 HTTP JSON 的 REST 通信机制,比 RPC 更语言无关、平台无关
(4)松耦合:功能的开发、测试、构建、部署耦合性更低
CAP定理:Consistent 一致性、Avalibility 可用性、Partition Tolerance 分区容忍性,三者最多只能实现两个。
两阶段提交:投票阶段 + 事务提交阶段。事务协调器询问事务的所有参与者【请求阶段】是否可以提交事务、协调器在收到所有参与者都同意提交事务时,才通知所有参与者提交,否则通知取消事务。缺点:数据不一致问题(第二阶段的 commit 请求发出之后可能有节点失败);协调器的单点故障;同步阻塞。
三阶段提交:预询问+预提交+事务提交:三阶段是:CanDoCommit询问、PreCommit请求(事务状态对齐,用于第三阶段超时自动commit)、DoCommit请求。下一个阶段的前提是前一阶段所有参与者都回复同意,有任一节点超时都会导致全盘回滚。XA三阶段提交(强一致性)在两阶段提交的基础上增加了CanCommit阶段,并且引入了超时机制。一旦事物参与者迟迟没有接到协调者的commit请求,会自动进行本地commit。
更好的算法:Paxos(Zookeeper采用)、其他解决方案:引入MQ做三阶段提交的最后一个阶段
TCC 事务(最终一致性):Try、Commit、Cancel
阿里Seata
定义:
在同一个事务下,连续执行两次同样的 SQL 语句可能导致不同的结果,第二次的 SQL 语句可能会返回第一次执行时不存在的行。
解决办法:
将两行记录间的空隙加上锁,阻止新记录的插入;这个锁称为间隙锁。或者就是用 InnoDB 使用的 next-key locking。
新生代收集器:Serial、ParNew、Parallel Scavenge
老年代收集器:CMS、Serial Old、Parallel Old
整堆收集器: G1
雪花算法:第 1 Bit 不用,因为固定填0,符号数中 0表示正数,1表示负数,所以固定填0;
41位时间戳,10 bit 机器 id(能部署 2^10 =1024 台机器),12 bit 序列号(由雪花算法的服务器管理自增)。
AIO 是彻底的异步通信。
NIO 是同步非阻塞通信。
有一个经典的举例。烧开水。
假设有这么一个场景,有一排水壶(客户)在烧水。
AIO的做法是,每个水壶上装一个开关,当水开了以后会提醒对应的线程去处理。(类似于Java回调机制中的异步回调)
NIO的做法是,叫一个线程不停的循环观察每一个水壶,根据每个水壶当前的状态去处理。(一直监听)
BIO的做法是,叫一个线程停留在一个水壶那,直到这个水壶烧开,才去处理下一个水壶。
注意是交替地打印 A B
先把数组排序,然后二分查找
(1)首先业务上这种查询就不合理,应该首先考虑业务上是否可以避免翻很多记录很多页。
(2)使用 id 限定,根据查询到第几页,以及每页记录数,来确定当前应该查询的 id 范围(比如 id > 100001),然后再从这个范围中取一页数据,但这个方法仅适用于连续递增id:
select * from orders_history where id >= 1000001 limit 100;
(3)记录上一页返回的最后一个 id,在下一页查询时带进去作为过滤条件,
(4)拆分出子查询,先通过覆盖索引获取到符合区间条件的主键,再通过这些主键关联原表获得所需数据。这个思路其实简单来理解就是,我一样是翻这么多记录丢弃这么多记录,但是我丢弃的是体积更小的叶子节点,那么我Page就可以容纳更多记录,那我翻页就可以更快,然后我再通过这些小Page翻出来对应的区间主键之后,再到原表中直接翻找这些主键的记录作为返回。
【当能通过检索索引就可以读取想要的数据,那就不需要再到数据表中读取行了。如果一个索引包含了(或覆盖了)满足查询语句中字段与条件的数据就叫 做覆盖索引】
增加Lambda表达式、HashMap优化(增加树化的过程)、永久代变Metaspace
泛型擦除
拆出来索引表【把列表字段拆到索引表,目的是对于不走索引的字段,需要把整条记录读取到Page中然后进行比较,若字段太多记录太大会导致Page IO过多,而加了索引表对于这种非索引字段,记录Page 就能容纳更多记录,减少IO,所以更快】
使用 set 方法创建对象(反射),而不是使用构造函数。
Boostrap ClassLoader --> Extension ClassLoader --> Application ClassLoader --> 自定义加载器。
双亲委派的作用:主要考虑的是安全问题,针对JDK自带/底层的类,防止有人恶意定义了同名的类进行加载。
懒汉方式、饿汉方式、静态内部类、双重校验锁、枚举
单例模式(getInstance)、工厂模式、代理模式、生产者消费者模式(场次)
使用 Mybatis-Spring 插件依赖
内存池存的东西:缓冲池(索引页、数据页、undo页、插入缓冲、自适应哈希索引(InnoDB存储引擎会自动根据访问的频率和模式来自动地为某些热点页建立哈希索引)、锁信息、数据字典信息。InnoDB缓冲池的页大小默认为 16KB。
存储算法:LRU List,最频繁使用的页放在 list 最前,淘汰时先淘汰末尾的页。
(1)MySQL 有查询优化分析器,SQL 语句的索引字段顺序没关系,会被优化成:‘where A=a and B>b and C>c’;
(2)首先看 A 是不是 varcha 然后 a 是不是少了引号【varchar 索引字段少了引号会导致索引失效】;
(3)其次 A = a 走不走索引取决于其区分度,区分度低的会走全面扫描去了;
(4)A = a能走到索引的话,B > b 同样要看区分度,然后因为 B > b 使用了范围查询,所以 C > c 肯定走不了索引【某列使用了范围查询,后面的列不能使用索引】。
ElasticSearch 更常见是用于全文索引(倒排索引、分词)。对于复杂查询比如多表关联,当然用 MySQL,并且 ES不支持事务。
MySQL 和 DB2 不同,视图不支持(不走)索引;运维说DB2:MySQL=10:1的性能差异;DB2 支持但 MySQL 不支持跳跃索引(最新版本的MySQL好像已经支持跳跃索引了);
HashMap加载因子是0.75,加载因子是说当我目前使用空间占总空间多大比例时,我就要进行扩容,比如 0.75 就是说使用率达到了 75%就需要进行扩容了,选择 0.75 是 哈希冲突 和 空间使用率 二者权衡的结果,数字太小空间使用率太低,数字太大哈希冲突的概率又会上升。
HashMap是拉链法存储结构。
HashMap初始大小为16,达到 75%使用率时,会将空间扩一倍。
而当“拉链法”的单条链超过了树化阈值(8)【也就是说产生了超过8次的哈希冲突】时,为了避免线性链表导致的性能损耗,HashMap会进行树化操作,转换成红黑树(所有节点非红即黑,叶子节点都是黑色,红色节点的子节点都是黑色,从一个节点到叶子节点走任何路径所经过的黑色节点数都是一样的)。
死锁:事务之间互相等待对方(AB-BA问题)。
排查死锁:用锁的信息链表、事务等待链表画成图,若图存在回路【深度优先遍历算法判断回路】则说明死锁。
解决:按DFS判出回路后,把 undo 量最小的事务给回滚掉。
LeetCode:你能从盒子里获得的最大糖果数
[实战讲解高并发和秒杀抢购系统设计][https://blog.csdn.net/Java_fenxiang/article/details/86516970]
(1)高并发
并发量评估
(2)时间短
用户响应时间
(3)系统容量预估
QPS、TPS、单机TPS容量、如何优化程序减少单个订单处理耗时
(4)好的分布式方案
方便扩充(线性增长最理想)
(5)关注系统的瓶颈
处理速度:程序内数据读写 > Redis > MySQL > 磁盘;单机网络请求 > 局域网内请求 > 跨机房请求
(6)什么语言更适合这类系统
当然,像是用golang, ngx_lua可能在高并发和性能方面会更有优势。
如果使用java、php当然也是可以的,作为一个系统,语言只是工具,更好的设计和优化,才能达到最终想要的效果。
(7)实际问题1:避免库存超卖
需要保证原子性,可以用 数据库、分布式锁(用 Redis 加锁,程序执行完成再 del 这个锁)、消息队列、Redis递减
(8)实际问题2:集群怎么规划
(9)多个集群的数据怎么保证一致性
(10)机器人抢购 -类似DDOS:
运营策略上,可以严格控制用户注册,必须登录,提交订单的时候引入图像验证码,问答,交互式验证等
/*
问题1:用Java语言实现单例
要求:
多种方案
格式正确
*/
// 1 懒汉-线程不安全
public Class Singleton {
private static Singleton Singleton;
private Singleton(){}
public static Singleton getInstance() {
if (null == Singleton) {
return new Singleton();
}
return Singleton;
}
}
// 2 懒汉-线程安全
public Class Singleton {
private static Singleton Singleton;
private Singleton(){}
public synchronized static Singleton getInstance() {
if (null == Singleton) {
return new Singleton();
}
return Singleton;
}
}
// 3 饿汉-线程安全
public Class Singleton {
private static Singleton Singleton = new Singleton();
private Singleton(){}
public static Singleton getInstance() {
return Singleton;
}
}
// 4 双重校验锁-线程安全
public Class Singleton {
// 加 volatile 防止 JVM 指令重排优化而导致多线程环境下可能有线程获得一个未初始化的实例
private static volatile Singleton Singleton;
private Singleton(){}
public static Singleton getInstance() {
if (null == Singleton) {
synchronized(Singleton.class) {
if (null == Singleton) {
return new Singleton();
}
}
}
return Singleton;
}
}
// 5 静态内部类-线程安全
public Class Singleton {
private static final Singleton Singleton;
private Singleton(){}
// final 方法拒绝子类 Override 重写
public static final Singleton getInstance() {
if (null == Singleton) {
return new Singleton();
}
return Singleton;
}
}
// 6 枚举-线程安全
public enum Singleton {
Singleton;
}
我们用的是 UseParallelGC(新生代Parallel Scavenge + 老年代 Serial Old 组合)。
(1)Serial GC:采用“复制”回收算法。单线程收集,且GC时必须停止其他所有的工作线程,简单高效,但会导致用户线程阻塞;
(2)ParNew GC:Serial 多线程版,采用“复制”回收算法。只是垃圾收集多线程了,但是仍然没有解决GC时必须停止其他所有工作线程的问题;
(3)Parallel Scavenge:采用“复制”回收算法。比 ParNew 多了“GC 自适应调节策略”,通过设置“最大垃圾收集停顿时间 -XX:MaxGCPauseMillis” 或 “吞吐量大小 -XX:GCTimeRatio”;
(4)Serial Old:Serial 的老年代版本,采用“标记-整理”回收算法。
(5)Parallel Old:Parallel Scavenge 的老年代版本,采用“标记-整理”回收算法。
(6)CMS(Concurrent Mark-Sweep GC):采用“标记-清除”回收算法。最大优点是 GC 线程可以和用户线程并发执行;缺点是虽然用户线程不停顿,但 GC 线程占用了一部分线程(CPU资源)而导致总的吞吐量降低,并且也有“标记-清除”算法天生的空间碎片问题。
(7)G1:整堆收集,与用户线程并发;基于“标记-整理”;分“初始标记”–>“并发标记”–>“最终标记”–>“筛选回收”四大阶段。
查看 JDK 默认的垃圾回收器命令:java -XX:+PrintCommandLineFlags -version
查看具体某个 JVM 进程使用的垃圾回收器:** jmap -heap PID **
Minor GC / Young GC:新生代 GC;Old GC:老年代 GC; Major GC / Full GC:全年代。
第一范式 1NF:字段不可再分。比如“地址”,拆分成省份、城市、详细地址多个字段分别存储,这样对地址某一部分进行操作时将非常方便。
[数据库设计三大范式][https://www.cnblogs.com/linjiqin/archive/2012/04/01/2428695.html]
第二范式 2NF:主键依赖,就是一张表里面的字段,必须是跟主键相关的,不能把无关的数据放进来。确保表中的每列都和主键相关(不能只与主键的某一部分相关【针对联合主键】)。也就是说在一个数据库表中,一个表中只能保存一种数据,不可以把多种数据保存在同一张数据库表中。比如要设计一个订单信息表,因为订单中可能有多个商品,所以需要将订单编号和商品编号作为数据库表的联合主键,这样就产生一个问题,这个表是以订单编号和商品编号作为联合主键,这样在该表中商品名称、单位、商品价格等信息不与该表的主键相关,而仅仅与商品编号相关,这就违反了第二范式原则。按第二范式应该拆分成三张表:订单信息表(包含订单本身的信息)、商品信息表(包含商品本身的信息)、订单项目表(每个订单对应的商品编号及其数量)。
第三范式 3NF:在2NF的基础上,任何的非主属性不依赖于其他非主属性(在第二范式基础上消除传递依赖)。就是不能重复存储相同的信息。这个情况,其实是在一个对象里引用了另外一个对象,这个时候,存一个引用就够了,而不是重复的存储这个对象的多个副本。从而避免冗余。
2NF和3NF的本质是,对象的属性依赖对象。
分析CPU冲高:
(1)>top 命令看哪个进程冲高
(2)>top -Hp PID 看该进程的哪个线程冲高;
(3)>printf %x TID 将线程 ID 转成16进制
(4)>jstack PID | grep -A 200 xxxx (xxxx填线程16进制ID)查看调用栈信息
(5)分析代码
保证消息有序主要思路有两种:1、单线程消费来保证消息的顺序性;2、对消息进行编号,消费者处理时根据编号判断顺序。
[详解Redis中两种持久化机制RDB和AOF][https://baijiahao.baidu.com/s?id=1654694618189745916&wfr=spider&for=pc]
RDB:保存全量快照。优点:文件紧凑、全量备份、恢复速度比 AOF 快;缺点:因为是全量备份所以慢,并且持久化期间修改的数据不会被保存可能会导致丢数据。
AOF:定期追加日志的方式。优点:写入快;缺点:AOF 文件相比 RDB 数据快照文件更大;影响 QPS;恢复相对 RDB 慢。
局部变量表、操作数栈、动态链接、方法返回地址。
泄漏的原因本质上是长期存活对象引用短期存活对象,导致短期存活对象占用的对象无法回收。
(1)静态对象
(2)各种连接:数据库连接、网络连接、IO连接;当不再使用连接时,应该 close 掉。尤其是数据库的 resultSet结果集、Connection、Statement 需要 close 掉。
(3)变量不合理的作用域:一个变量定义的作用范围大于其使用范围,当其使用完了之后,因为作用域的关系仍不能回收。
(4)内部类持有外部类:怎么理解?
(5)改变哈希值:改变了哈希值导致再也无法寻址到
(6)监听器和回调
首先我们不可能把上亿的用户记录全放在同一张表,
[乐观锁悲观锁的实现][https://www.jianshu.com/p/4ff4d3f21d1c]
悲观锁:“一锁二查三更新”,通过常用的select … for update操作来实现悲观锁。具体如下:在对任意记录进行修改前,先尝试为该记录加上排他锁;如果加锁失败,说明该记录正在被修改,那么当前事务可能要进行等待或抛出异常(具体响应方式开发者根据实际决定);如果成功加锁,那么可以对记录做修改,事务完成后就会解锁了,其间如果有其他对该记录做修改或加排他锁的操作,都会等待我们解锁或者直接抛出异常。
在JAVA应用上,Synchronized 就是悲观锁。
乐观锁:CAS机制->ABA 问题 + 版本号机制。
前缀匹配原则+区分度差全表扫描,如果A=的区分度特别差A就不走索引了,按照前缀匹配原则和不支持跳跃索引,B C也不会走索引。
InnoDB 索引数据结构是 B+ 树,相比 B 树,有两大区别(最关键在于去除了非叶子节点的记录指针降低了树的高度):
(1)B 树每个节点都存了记录的指针,而 B+ 树只有叶子节点才会存储记录的指针,这样可以使得每个节点能容纳更多的 key (以前节点用来存记录指针的空间可以用来存其他 key 值),节点能容纳更多 key 值也就代表可以把索引树的高度压地更低,高度越低查找就越快。
(2)B+ 树的叶子节点连接成链表形式了,更方便顺序遍历。
用的是 MySQL 默认的 Repeatable Read(可重复读)。可重复读是为了解决脏读(脏读是未提交读【事务可以读取未提交的数据】)和提交读/不可重复读(一个事务从开始直到提交之前,所做的任何修改对其他事务是不可见的,这样的话事务并发性就太差了)的问题。
但可重复读会带来幻读/幻行的问题,可以通过间隙锁解决(比如 InnoDB 的 next-key 锁),MySQL 的 MVCC 机制。
并行期通过请求回放实现,记录 EJB 入口出的请求并记录到文件中,通过脚本到 MySQL 新应用通过 JNI 调用 EJB接口实现请求回放。回放不保证顺序,本身老应用下事务也存在顺序问题,业务场景本身对顺序不敏感。
分布式事务(业务规避)、多表 union all 查询排序的问题、分页查询(解决不了,业务缩减查询范围减少查询总记录数)
[Mysql索引为啥要用B+树?][https://my.oschina.net/chener/blog/1603098]
二叉树和红黑树的都是只有双子节点,分叉太少,导致高度偏高,高度多了就代表磁盘IO多了,所以不适合。
trie树死在了开始,无疑AVL树在查询方面是最出色的,但是在删除的时候可能会引起噩梦,这样看来好像是红黑树最适合咯,虽然他牺牲了一部分查询性能,但是使删除性能在大部分情况保持了常数的时间复杂度。但是,有一个最重要的问题是,mysql的数据是放在外部存储的,也就是说磁盘IO才是性能瓶颈的关键,所以我们需要的是减少树的深度,所以我们需要更多分叉的树 ,还需要更适合磁盘操作特性的数据结构。
聚簇索引的叶子节点 Key 是主键,value 是整条记录;二级索引的叶子节点 key 是二级索引的键值,Value 是聚簇索引的键值(用于回表查询)。二级索引的字段如果能覆盖 select 的字段的话(这种现象叫“覆盖索引”),就不需要再进行回表查询了。
如下描述。
[深入理解Java并发之synchronized实现原理][https://blog.csdn.net/javazejian/article/details/72828483]
[深入理解Synchronized实现原理][https://www.jianshu.com/p/46a874d52b71]
首先, Java 线程是映射到操作系统的原生线程上的,Synchronized 是基于对象头+ monitor 锁实现的,而 monitor 锁又是基于操作系统本身的互斥锁 Mutex Lock 实现的,每次获取锁和释放锁操作都会带来用户态和内核态的切换(开销:线程上下文保存;切换到内核线程时的安全检查;内核线程执行完返回过程有很多额外工作比如检查是否需要调度等),增加性能开销,所以 Synchronized 成为重量级锁。
java1.6之后synchronized的优化
jdk1.6对锁的实现引入了大量的优化技术来减少锁操作的开销,如:
(1)自旋锁、
(2)适应性自旋锁:动态调整自旋次数、
(3)锁消除:结合方法逃逸【变量只在方法内有效不存在逃逸】分析决定是否要消除掉锁、
(4)锁粗化:针对细粒度的锁,比如锁分段等,有时候都加小锁还不如合并为一个更大范围的锁、
(5)偏向锁:大多数情况下锁不仅不存在多线程竞争而且总是由同一线程获得,为了减少每次获取锁的代价(CAS同步操作等)而引入偏向锁,即如果一个线程获得了锁,那么锁就进入偏向模式,此时 对象头 的 Mark Word 的结构也变为偏向锁的结构,当同一个线程再次请求锁时,无需做任何同步操作,即可获得锁。但对于锁竞争比较激烈的情况,会进行锁升级,但不是直接升级为重量级锁,而是先升级为轻量级锁。
(6)轻量级锁:无实际的锁竞争(比如通过乐观锁CAS-Compare And Swap 实现),只允许短时间的锁竞争(比如通过自旋实现,宁可让线程自旋也不做内核态的切换),如果自旋过多或等待时间太长,自旋带来的损耗反倒比重量级锁更严重,所以会升级为重量级锁。
就绪(等待调度)、执行状态(分配到时间片进行调度)、阻塞(等待 I/O,I/O 完成后回到就绪状态)。
BIO-Block I/O:同步阻塞I/O(只有等 I/O 操作结束后,用户线程才会继续执行)
NIO:Non-Block I/O 同步非阻塞 I/O(用户线程不阻塞,而是执行其他事情,但是会时不时的主动去询问 I/O 是否完成,定期询问就带来了CPU的浪费)
AIO:异步非阻塞 I/O,为了解决 NIO 定期查探 I/O 导致 CPU 浪费的情况,专门加入了回调机制,当 I/O 结束时通过回调函数通知用户线程 I/O 完成了(类似于 Java 的回调机制【在类中加一个回调方法】,其他类执行完任务后调用该类的回调方法通知类)
(1)限流算法:
计数器算法:对请求数进行计数,到了阈值就拒绝后续请求,缺陷在于会拒掉超载请求、
漏桶算法:类似于漏斗,底下小口流速固定,没来得及处理的先放在桶里,问题在于漏斗也会满然后拒掉超载请求、
令牌桶算法:增设一个存放固定数量令牌的桶,并且以固定速率生成令牌到这个令牌桶中(生成满了就丢弃令牌),每个请求进来都要先拿令牌,然后才行往下处理。
(2)服务熔断:对微服务链路的保护机制,防止“雪崩效应【一段链路的缓慢或不可用导致整个链路的缓慢或崩溃】”。
(3)服务降级:服务缓慢或不可用时,取而代之执行其他替代程序,比如去访问一个本地的伪装者而不是真实的服务。
[HTTP应答码 ][https://www.cnblogs.com/qianjinyan/p/11413528.html]
200-OK
302-临时重定向
301-永久重定向
304-Not Modified(当用户第二次请求index.html时,在请求中包含一个名为If-Modified-Since请求头,如果未修改,服务端返回 304,如果有修改则返回200)
4xx - 客户端错误(例如 400-Bad Request HTTP 请求出现语法错误比如 header 和 body 之间少了空行)
5xx - 服务器错误(500-Internal Server Error、504-GateWay Timeout 网关超时、503-Service Unavailable 服务不可用【在维护或负载过高】)
主键是一种约束,唯一索引是一种索引,两者在本质上是不同的。
主键创建后一定包含一个唯一性索引(注意是包含,因为主键可以是多个字段的联合主键),唯一性索引并不一定就是主键。
唯一性索引列允许空值,而主键列不允许为空值。
主键列在创建时,已经默认为空值 + 唯一索引了。
主键可以被其他表引用为外键,而唯一索引不能。
一个表最多只能创建一个主键,但可以创建多个唯一索引。
主键更适合那些不容易更改的唯一标识,如自动递增列、身份证号等。
//评测题目: 无
// a = 10
// b =30
// c = 20
//
// a -> b 10
// b->c 39
//
// a = 3
// b = 4
// c= 6
日终对账
a b c
Class Acct {
int beginBalance;
int endBalance;
// TODO
}
Class Tran {
Acct creditAcct;
Acct debitAcct;
int transAt;
poublc Acct getCreditAcct(){}
poublc Acct getDeditAcct(){}
// TODO
}
public static boolean checkBalance(Tran[] trans) {
// 账户初始额
int beginA, beginB, beginC;
/n/ 日终时账户余额
int endA, endB, endC;
Acct a, b, c;
for (Tran tran: trans) {
tran.getCreditAcct()
tran.getDeditAcct()
tran.getCreditAcct().add(tran.getTransAt);
tran.getDeditAcct().cost(tran.getTransAt);
}
if (a.getEndBalance == endA && b.getEndBalance == endB && c.getEndBalance == endC) {
return true;
}
else {
// TODO 记录哪个账户对账错误
return false;
}
}
(1)Client Hello Server Hello 密码套件协商
(2)客户端验证服务端,密钥交换
(3)服务端验证客户端,密钥交换
(4)完成
公司流程制度,本应专业的人做专业的事,改变不了环境就只能改变自己,杂事若必须要做那就以更高效的方式去做,比如为什么会出现那么多支持的问题–>系统设计的不好?入网流程设计的不好?文档不够?–>开发工具摆脱重复性劳动 --> 文档积累,Q&A 分享给运维自查。
TCP 四次挥手中,客户端发送关闭连接的请求给服务端,服务端先应答 ACK,然后隔了 Close Wait 时间后,主动给客户端发关闭连接的请求,客户端收到之后先应答过去,但是隔了 TIme Wait 时间后才会真正关闭连接,服务端则收到应答就关闭连接
可参考:[MySQL 主从复制][https://www.jianshu.com/p/faf0127f1cb2]
(1)MySQL 的主从复制是通基于 binlog 复制实现的,前提是作为主服务器角色的数据库必须开启 binlog;
(2)主服务器上面的任何修改都会通过自己的 I/O thread (I/O 线程)保存在 binlog 中;
(3)从服务器上也开启一个 I/O thread ,通过配置好的用户名和密码,连接到主服务器上面请求读取二进制日志,监听主服务器的 binlog 修改,并写到本地的 Relay log(中继日志,也是二进制的)中;
(4)从服务器上同时开启一个 SQL thread (SQL 线程)定时检查 Relay log,如果发现有更新立即把更新的内容在本机的数据库上执行一遍。
(5)每个从服务器都会收到主服务器二进制日志的全部内容的副本,从而保证主从数据的强一致性(这个定论有个前提:binlog就是主服务器的所有数据修改,对于已经写到binlog的都会写到主库上,还没写到binlog的从库也不需要考虑;所以保证从库的 Relay log 完整的复制了主库 binlog 的修改即可)。
(6)从服务器设备负责决定应该执行二进制日志中的哪些语句。
(7)除非另行指定,否则主从二进制日志中的所有事件都在从站上执行。
(8)如果需要,您可以将从服务器配置为仅处理一些特定数据库或表的事件。
重要: 您无法将主服务器配置为仅记录特定事件。
每个从站(从服务器)都会记录二进制日志坐标:
文件名
文件中它已经从主站读取和处理的位置。
由于每个从服务器都分别记录了自己当前处理二进制日志中的位置,因此可以断开从服务器的连接,重新连接然后恢复继续处理。
(9)如果没必要,而又要提升从库性能和降低数据延迟(读写分离从库读的压力大),可以把从库的 binlog 关闭,降低因从库性能问题导致主从数据同步的延迟。
(10)一主多从的场景:
摘录:
如果一主多从的话,这时主库既要负责写又要负责为几个从库提供二进制日志。此时可以稍做调整,将二进制日志只给某一从,这一从再开启二进制日志并将自己的二进制日志再发给其它从。或者是干脆这个从不记录只负责将二进制日志转发给其它从,这样架构起来性能可能要好得多,而且数据之间的延时应该也稍微要好一些。
后面都好理解,比如专门用一台从作为中继节点,甚至都可以关闭其 binlog,纯粹负责数据搬运。但第一句话这里我就有个疑问了,为什么不能多个从库同时监听主库的 binlog,只是给别人监听为什么会增加主库的压力。
房间号,广播,用消息队列做发送和订阅消息