当年,兔子学姐靠这个面试小抄拿了个22k
本文顺序是操作系统(jvm)、网络、数据库(mysql/redis),都是当时兔子的学姐准备面试的时候总结的,学生面试基本不会跑出这个范围,懂行的应该能看出来。
学姐原话:因为我本身的知识是A集合,我觉得一次记不住,需要反复看的,我就写了下来B集合。这些并不一定是所有,特别简单的或者特别生僻的A-B都没写。
所以按我的理解,那些,特别特别基础的,不用学姐说,自己也应该会,然后再看这个文章。
目录
生产消费读者写者
进程vs线程
线程生命周期
进程间通信方式
线程间通信方式
进程调度算法
死锁
页面置换算法
磁盘调度算法
jvmjvmjvmjvm区域
判断对象死亡?引用计数 vs 可达性分析
如何回收对象?垃圾收集算法
垃圾收集器
分配回收策略(老年代新生代)
GC调优
JVM常用参数
Cookie session
网络分层
握手挥手
UDP想可靠
网络访问过程
为什么tcp可靠?
UDP可靠
HTTP and HTTPS
HTTP方法及相互区别
状态码
http结构
Tcp结构
Socket
redisredisredis数据结构
对象
跳表
HyperLogLog
单线程
持久化
Redis和数据库双写一致性
缓存击穿雪崩穿透
解决Redis并发竞争Key问题
Redis缓存策略
哨兵
解决会话
sqlsqlsqlsqllsqlslqslqslq数据类型
mysql特点
存储引擎
增删改
范式
索引优缺点和使用原则
索引分类
索引区别
事务特性:ACID
并发错误
隔离级别
并发控制技术(锁)
死锁
分布式事务
SQL语句性能优化
索引的优化
分库分表
Kafka
Es
生产消费读者写者
读者写者:
多读者/多写者互斥/多读者互斥
消费者生产者:全互斥
生产者:p(empty),p(mutex),v(mutex),v(full)
消费者:p(full),p(mutex)v(mutex)v(empty)
进程vs线程
进程是资源(CPU、内存等)分配的基本单位,它是程序执行时的一个实例。
线程是程序执行时的最小单位,是CPU调度和分派的基本单位。
线程间共享进程的所有资源,每个线程有自己的堆栈和局部变量。
线程由CPU独立调度执行,在多CPU环境下就允许多个线程同时运行。
线程生命周期
(五个状态):新建、就绪、运行、阻塞、死亡
新建状态:线程对象已经创建,还没有在其上调用start()方法
就绪状态:当线程调用start方法,但调度程序还没有把它选定为运行线程时线程
运行状态:线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。(是线程进入运行状态的唯一方式)
阻塞(等待/睡眠)状态:线程仍旧是活的,但是当前没有条件运行。当某件事件出现,他可能返回到可运行状态
死亡状态:当线程的run()方法完成时就认为它死去。线程一旦死亡,就不能复生。 一个死去的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常
进程间通信方式
- 匿名管道:管道的实质是一个缓冲区,该缓冲区是一个循环队列,进程以先进先出的方式从缓冲区存取数据,管道一端的进程顺序的将数据写入缓冲区,另一端的进程则顺序的读出数据。管道的局限性体现在:只支持单向数据流、由于管道没有名字,只能用于有亲缘关系(共同祖先)的进程间通信、缓冲区有限,效率不高
- 有名管道:有名管道不同于匿名管道之处在于它提供了一个路径名与之关联,即使与有名管道的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过有名管道相互通信。
- 消息队列:消息队列是存放在内核中的消息链表,每个消息队列由消息队列标识符表示。与管道(无名管道:只存在于内存中的文件;命名管道:存在于实际的磁盘介质或者文件系统)不同的是消息队列存放在内核中,只有在内核重启(即操作系统重启)或者显示地删除一个消息队列时,该消息队列才会被真正的删除。
- 信号量:信号量是一个计数器,用于多进程对共享数据的访问,信号量的意图在于进程间同步。
- 创建信号量:指定初始值,对于二值信号量通常是1。(2)等待信号量:测试信号量的值,如果小于0就阻塞。也称P操作。(3)挂出信号量:信号量值加1,称V操作。
为了正确地实现信号量,信号量值的测试及减1操作应当是原子操作。为此,信号量通常是在内核中实现的。Linux环境中,有三种类型:Posix(可移植性操作系统接口)有名信号量(使用Posix IPC名字标识)、Posix基于内存的信号量(存放在共享内存区中)、System V信号量(在内核中维护)。这三种信号量都可用于进程间或线程间的同步。
- 套接字:是一种通信机制,客户/服务器系统的开发工作既可以在本地单机上进行,也可以跨网络进行。也就是说它可以让不在同一台计算机但通过网络连接计算机上的进程进行通信。
(套接字位于传输层与应用层之间)
套接字是支持TCP/IP网络通信的基本操作单元,可以看做主机间进程进行双向通信的端点
套节字通信基本过程
线程间通信方式
1.锁机制:包括互斥锁(对应于JAVA中的Synchronized)、条件变量(对应于JAVA中的volatile)
*互斥锁提供了以排他方式防止数据结构被并发修改的方法。
*条件变量可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。
2.信号机制:包括无名线程信号量和命名线程信号量
JAVA中线程通信的具体方式有:1.Volatile和Synchronized 2.wait()和notify()机制 3.管道输入/输出流:PipedReader, PipedWriter 4.Thread.join() 5.TheadLocal类:线程变量,用于线程内部共享数据。以ThreadLocal对象为键,任意对象为值的存储结构 ThreadLocal<String> tl = new ThreadLocal<>();
进程调度算法
先来先服务(FCFS)最简单调度算法。按照进入后备队列的先后次序来加入就绪队列等待执行。是非抢占式,易于实现,效率不高,利于长作业(CPU繁忙)不利于短作业(I/O繁忙)
短作业优先(Short Job First)是非抢占式的,具有很好性能,降低平均等待时间,提高吞吐量。不利于长作业,可能一直处于等待出现饥饿;未考虑作业紧迫程度,不能用于实时系统。
高响应比优先调度算法(Highest Reponse Ratio First, HRRF)(响应比高意味着等待时间长而服务时间短,优先处理)响应比 = (等待 + 服务) / 服务时间 = 等待 / 服务时间 + 1
时间片轮转:用于分时系统的进程调度,抢占式。
基本思想:将CPU时间分为若干时间片(q),进程按到达顺序排列。每次调度队首,执行1个时间片后,该进程移至队尾。能在给定时间响应所有用户请求,达到分时系统的目的。
其性能主要取决于时间片q的大小,q太大,则所有的进程在1个时间片完成;太小则进程频繁切换,系统开销大。
多级反馈队列调度算法:将时间片轮转与优先级调度相结合,把进程按优先级分成不同的队列,先按优先级调度,优先级相同的,按时间片轮转。优点是兼顾长短作业,有较好的响应时间,可行性强,适用于各种作业环境。
死锁
什么是死锁?
当两个以上的运算单元,双方都在等待对方停止运行,以获取系统资源,但是没有一方提前退出时,就称为死锁。
必要条件
禁止抢占 - 资源不能被强制从一个进程中退出
持有和等待 - 一个进程可以在等待时持有系统资源
互斥 – 某个资源在一段时间内只能由一个进程占有
循环等待 - 一系列进程互相持有其他进程所需要的资源,银行家算法-破环循环等待条件,合理分配系统资源
死锁的应对方法
1. 最简单、最常用方法是重新启动,不过代价很大,意味着之前所有进程计算都将付之东流
2. 撤消进程,剥夺资源。终止参与死锁的进程,收回它们占有的资源,从而解除死锁。
分两种情况:一次性剥夺全部资源;或逐步撤消参与死锁的进程,选择逐步撤消目的是撤消代价最小的进程,比如按进程优先级确定代价;考虑进程运行代价和此进程相关作业代价等;
3. 进程回退策略,即让参与死锁的进程回退到没有发生死锁前某一点处。操作起来系统开销大,要有堆栈这样的机构记录进程的每一步变化,以便今后的回退,有时这是无法做到的。
页面置换算法
最佳置换算法(OPT)
最佳(OPT)置换算法所选择的被淘汰页面将是以后永不使用的,或者是在最长时间内不再被访问的页面,这样可以保证获得最低的缺页率。但由于人们目前无法预知进程在内存页面中哪个是未来最长时间内不再被访问的,因而该算法无法实现。
先进先出置换算法(FIFO)
最简单的页面置换算法是先入先出(FIFO)法。这种算法的实质是,总是选择在主存中停留时间最长(即最老)的一页置换,即先进入内存的页,先退出内存。
最近最久未使用(LRU)算法
它的实质是,当需要置换一页时,选择在最近一段时间里最久没有使用过的页面予以置换。
磁盘调度算法
1.FIFO:先来先服务算法;(依次处理)
2.SSTF: 最短寻道时间算法;(距离当前磁道最近的有限处理)
3.SCAN:电梯调度算法;(这样命名很形象,先按一个方向,扫描的过程依次访问要求服务的队列,当扫描到最里层的一个服务序列时就反向扫描)
4.CSCAN: 循环扫描算法(从最里面一个磁道访问完之后,立即返回最外层,也称单向扫描调度算法)
5.FSCAN:分步电梯调度算法(分两个队列)
jvmjvmjvmjvm区域
线程共享的:堆、方法区、直接内存 (非运行时数据区的一部分)
程序计数器是一块较小的内存空间,是当前线程执行字节码的行号指示。
通过改变程序计数器来依次读取指令实现流程控制,如:顺序执行、选择、循环、异常处理。
多线程:记录当前线程执行的位置,线程被切换回来时能够知道该线程上次运行到哪儿了。
虚拟机栈(Java 内存可以粗糙的区分为堆内存(Heap)和栈内存 (Stack))
存放基本数据类型(boolean、byte、char、short、int、float、long、double)和对象引用
会出现两种异常:StackOverFlowError 和 OutOfMemoryError。
StackOverFlowError:不动态拓展, 线程请求栈的深度超过当前虚拟机栈的最大深度
OutOfMemoryError: 动态扩展,且内存用完了,无法扩展
方法区(永久代):存储已加载的类信息、常量、静态变量、即时编译器编译后的代码等。
《Java 虚拟机规范》规定了有方法区和作用,并没实现。(像接口),在不同的 JVM 上方法区的实现肯定是不同的了。 永久代是 HotSpot 虚拟机对方法区的一种实现方式。(像类)
判断对象死亡?引用计数 vs 可达性分析
引用计数算法:给对象添加一个引用计数器,每当有一个地方引用到他,就加1;引用失效就减1。有问题,有可能存在循环引用,导致对象无法被回收。
可达性分析算法:以GC Roots对象为起始点,从这些节点向下搜索
可以作为GC ROOT对象的有:Java虚拟机栈中的引用对象。本地方法栈中JNI(既一般说的Native方法)引用的对象。方法区中类静态属性所引用的对象。方法区中常量所引用的对象。
如何回收对象?垃圾收集算法
首先从根集合进行扫描,对存活的对象进行标记,标记完毕后,再扫描整个空间中未被标记的对象并进行回收(老年代)
垃圾收集器
Serial收集器(复制算法): 新生代单线程收集器,标记和清理都是单线程,优点是简单高效;
Serial Old收集器 (标记-整理算法): 老年代单线程收集器,Serial收集器的老年代版本;
ParNew收集器 (复制算法): 新生代并行收集器,实际上是Serial收集器的多线程版本,在多核CPU环境下有着比Serial更好的表现;
Parallel Old收集器 (标记-整理算法): 老年代并行收集器,吞吐量优先,Parallel Scavenge收集器的老年代版本;
CMS(Concurrent Mark Sweep)收集器(标记-清除算法): 老年代并行收集器,以获取最短回收停顿时间为目标的收集器,具有高并发、低停顿的特点,追求最短GC回收停顿时间。
分配回收策略(老年代新生代)
一般而言,对象主要分配在新生代的Eden区上,少数情况下也可能直接分配在老年代中。
1) 对象优先在Eden分配,当Eden区没有足够空间时,虚拟机将发起一次MinorGC。
2) 大对象直接进入老年代。所谓的大对象是指,很长的字符串以及数组。
3) 长期存活对象进入老年代。在新生代中经历n次(默认15)Minor GC后,晋升老年代。
GC调优
如何将各分区调整到合适的大小,分析活跃数据的大小是很好的切入点。
通过收集GC信息,结合系统需求,确定优化方案,例如选用合适的GC回收器、重新设置内存比例、调整JVM参数等。
根据对象生命周期的分布情况:如果应用存在大量的短期对象,应该选择较大的年轻代;如果存在相对较多的持久对象,老年代应该适当增大。
JVM常用参数
-XX:NewRatio=n:设置年轻代和年老代的比值。如:为3表示年轻代和年老代比值为1:3
Cookie session
Cookie与Session都是用来跟踪浏览器用户身份的会话方式。
Cookie不安全,别人可以分析本地Cookie进行Cookie欺骗,用加密的Cookie或Session。
Session存服务器。占服务器性能,如果减轻负担方面,用Cookie。
单个Cookie在客户端的限制是4K,很多浏览器都限制一个站点最多保存20个Cookie。
网络分层
物理层:在传输媒体上传输数据比特流。屏蔽介质和通信手段差异,使数据链路层感觉不到
数据链路层:主机间有很多链路,为相邻结点间服务。把网络层传来的分组封装成帧。
网络层:为主机间提供传输服务,把运输层传递下来的报文段或数据报封装成分组。
传输控制协议 TCP,提供面向连接、可靠的数据传输服务,数据单位为报文段;
用户数据报协议 UDP,提供无连接数据传输服务,数据单位为用户数据报。
应用层:为特定应用程序提供数据传输服务,例如 HTTP、DNS 等。数据单位为报文。
握手挥手
序号seq :对字节流编号,序号为 301,编号为 301,如携带数据 100 字节,下一个报文段的序号应为 401。用来标记顺序
确认号 ack:期望收到的下一个报文段序号。例如 B 收到 A报文段序号为 501,长度 200 ,因此 B 期望701,B 发送给 A 的确认报文段中确认号就为 701。
确认 ACK :当 ACK=1 时确认号ack字段有效,否则无效。
同步 SYN :在连接建立时用来同步序号。当 SYN=1,ACK=0 时表示这是一个连接请求报文段。若对方同意建立连接,则响应报文中 SYN=1,ACK=1。
终止 FIN :用来释放一个连接,当 FIN=1 时,表示发送方已发送完毕,要求释放连接。
窗口 :窗口值作为接收方让发送方设置其发送窗口的依据。这个限制是因为接收方的数据缓存空间有限
2、第二次握手:服务器收到 SYN 报文之后,会应答一个 SYN+ACK 报文。
3、第三次握手:客户端收到 SYN+ACK 报文之后,会回应一个 ACK 报文。
第二次:seq随机y,ACK=X+1,syn=1,ack=1
第三次:seq=x+1,ACK=Y+1,syn=0,ack=1
第一次握手:客户端发,服务端收到。服务端就能得出结论:客户端的发送能力、服务端接收能力是正常
第二次握手:服务端发,客户端收到。客户端就能得出结论:服务端的接收发送能力,客户端的接收发送能力是正常的。服务器不能确认客户端的接收能力是否正常。
第三次握手:客户端发,服务端收到了。这样服务端就能得出结论:客户端的接收发送能力正常,服务器自己的发送、接收能力也正常。
第三次握手是为了防止失效的连接请求到达服务器,让服务器错误打开连接。
客户端等待一个超时重传时间之后,就会重新请求连接。但是这个滞留的连接请求最后还是会到达服务器,如果不进行三次握手,那么服务器就会打开两个连接。
三次:Server在收到建立连接请求后,可以直接把ACK和SYN放在一个报文发送给Client。
四次:关闭连接时,当收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,己方也未必全部数据都发送给对方了,因此,己方ACK和FIN一般都会分开发送。
客户机:我想和你断开连接,你同意吗?(FIN=1) 服务器:我同意(ACK=1)
服务器:客户机,我想要和你断开连接,你同意吗?(FIN=1) 客户机:我同意。(ACK=1)
短连接表示“业务处理+传输数据的时间 远远小于 TIMEWAIT超时的时间”的连接。
UDP想可靠
网络访问过程
--> 发起TCP的3次握手 拿到域名对应的IP地址之后,浏览器向服务器的WEB程序(tomcat,nginx)80端口发起TCP连接请求。
--> 浏览器解析html代码,并请求html代码中的资源(如js、css、图片)
域名解析:搜索浏览器的DNS缓存 操作系统的DNS缓存 hosts文件C:\Windows 迭代DNS解析请求(根名称/顶级名称/二级名称/权威名称服务器)
为什么tcp可靠?
发送方通过维持一个发送窗口确保不会发生发送方发太快接收方无法及时处理。
1、确认和重传:接收方收到报文就会确认,发送方发送一段时间后没有收到确认就重传。
1)ack:数据丢失或延迟。发送时会起定时器,如果指定时间内没接收到ACK seq + 1,就再发一次。
2)数据乱序:接收方上一个收到的正确数据是seq + 4,它返回seq + 5作为ACK。这时候它收到了seq + 7,因为顺序错了,所以接收方会再次返回seq + 5给发送方。
3)数据错误:每一个TCP数据都会带着数据的校验和。接收方收到数据seq + 3以后会先对校验和进行验证。如果结果不对,则发送ACK seq + 3,让发送方重新发送数据。
3、流量控制:当接收方来不及处理发送方的数据,能提示发送方降低发送的速率,防止包丢失。
接收方发送的确认报文中的窗口字段可以用来控制发送方窗口大小,从而影响发送方的发送速率。将窗口字段设置为0,则发送方不能发送数据。
发送方和接收方各有一个窗口,接收方通过窗口字段告诉发送方自己的窗口大小,发送方根据这个值和其它信息设置自己的窗口大小。
TCP使用累计确认(发送方一次发送多个连续包,接收方只需要确认最后一个包),快速重传(收到3个冗余的ACK包立马重传,不用等待超时)以及选择重传(只对丢失的包进行重传)提高效率
拥塞控制:防止过多的数据注入到网络中,这样可以使网络中的路由器或链路不致过载。
发送方维持一个拥塞窗口 cwnd的状态变量。动态地在变化。发送方让自己的发送窗口等于拥塞。
慢开始:不清楚网络的负荷情况。因此先探测一下,由小到大逐渐增大发送窗口,也就是说,由小到大逐渐增大拥塞窗口数值。
拥塞避免算法:经过一个往返时间RTT就把拥塞窗口cwnd加1,不是加倍。按线性规律缓慢增长,比慢开始算法的拥塞窗口增长速率缓慢得多。
快速恢复算法:发送方认为网络很可能没有发生拥塞,因此而是把cwnd值设置为门限减半后的数值,然后使拥塞窗口线性增大。
UDP可靠
最简单的方式是在应用层模仿传输层TCP的可靠性传输。下面不考虑拥塞处理,可靠UDP的简单设计。
目前有如下开源程序利用udp实现了可靠的数据传输。分别为RUDP、RTP、UDT。
HTTP and HTTPS
1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
2、https则是通过TLS加密后传输。SSL 是“Secure Sockets Layer”的缩写,中文叫做“安全套接层”
3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
对称密钥加密,又称私钥加密,即发送方和接收方用同一个密钥去加密解密。优势是速度快,适合于对大数据量进行加密,但密钥管理困难。
非对称密钥加密,又称公钥加密,需要使用一对密钥分别完成加密和解密,一个公开发布,即公开密钥,另一个由用户自己保存,即私用密钥。信息发送者用公开密钥去加密,而信息接收者则用私用密钥去解密。
从功能角度而言非对称加密比对称加密功能强大,但加密和解密速度却比对称密钥加密慢得多。
SSL/TLS协议,公钥加密法,客户端先向服务器端索要公钥,然后用公钥加密信息,服务器收到密文后,用自己的私钥解密,如何保证公钥不被篡改?
解决方法:将公钥放在数字证书中,只要证书是可信的,公钥就是可信的。
HTTP方法及相互区别
GET请求在URL中传送的参数是有长度限制的,而POST么有
HEAD获取报文首部,和 GET 方法类似,但是不返回报文实体主体部分
状态码
100 Continue :表明到目前为止都很正常,客户端可以继续发送请求或者忽略这个响应。
204 No Content :请求已经成功处理,但是返回的响应报文不包含实体的主体部分。一般在只需要从客户端往服务器发送信息,而不需要返回数据时使用。
206 Partial Content :表示客户端进行了范围请求,响应报文包含由 Content-Range 指定范围的实体内容。
303 See Other :和 302 有着相同的功能,但是 303 明确要求客户端应该采用 GET 方法获取资源。
307 Temporary Redirect :临时重定向,与302相反,当重新发出原始请求时,不允许更改请求方法。
401 Unauthorized :该状态码表示发送的请求需要有认证信息。如果之前已进行过一次请求,则表示用户认证失败
404 Not Found :请求失败,请求所希望得到的资源未被在服务器上发现,但允许用户的后续请求。
500 Internal Server Error :服务器正在执行请求时发生错误
503 Service Unavailable :服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。
504 Gateway Timeout作为网关或者代理工作的服务器尝试执行请求时,未能及时从上游服务器
http结构
一个HTTP请求报文由四个部分组成:请求行、请求头部、空行、请求数据。
1.请求行:由请求方法字段、URL字段和HTTP协议版本字段3个字段组成,它们用空格分隔。比如 GET /data/info.html HTTP/1.1
方法字段就是HTTP使用的请求方法,比如常见的GET/POST
其中HTTP协议版本有两种:HTTP1.0/HTTP1.1 可以这样区别:
指明请求类型(一般是GET或者 POST)。大多数请求头并不是必需的,但post的Content-Length除外。
Accept-Encoding:浏览器能够进行解码的数据编码方式,比如gzip。
Authorization:授权信息,通常出现在对服务器发送的WWW-Authenticate头的应答中。
Referer:客户机通过这个头告诉服务器,它是从哪个资源来访问服务器的(防盗链)。包含一个URL,用户从该URL代表的页面出发访问当前请求的页面。
User-Agent:User-Agent头域的内容包含发出请求的用户信息。浏览器类型,如果Servlet返回的内容与浏览器类型有关则该值非常有用。
Cookie:客户机通过这个头可以向服务器带数据,这是最重要的请求头信息之一。
From:请求发送者的email地址,由一些特殊的Web客户程序使用,浏览器不会用到它。
Connection:处理完这次请求后是否断开连接还是继续保持连接。
同时指定几个范围:bytes=500-600,601-999
响应行一般由协议版本、状态码及其描述组成 比如 HTTP/1.1 200 OK
其中协议版本HTTP/1.1或者HTTP/1.0,200就是它的状态码,OK则为它的描述。
100~199:表示成功接收请求,要求客户端继续提交下一次请求才能完成整个处理过程。
200~299:表示成功接收请求并已完成整个处理过程。常用200
300~399:为完成请求,客户需进一步细化请求。例如:请求的资源已经移动一个新地址、常用302(意味着你请求我,我让你去找别人),307和304(我不给你这个资源,自己拿缓存)
400~499:客户端的请求有错误,常用404(意味着你请求的资源在web服务器中没有)403(服务器拒绝访问,权限不够)
响应头用于描述服务器的基本信息,以及数据的描述,服务器通过这些数据的描述信息,可以通知客户端如何处理等一会儿它回送的数据。
设置Cookie,指定修改日期,指示浏览器按照指定的间隔刷新页面,声明文档的长度以便利用持久HTTP连接,……等等许多其他任务。
Allow:服务器支持哪些请求方法(如GET、POST等)。
Content-Encoding:文档的编码(Encode)方法。只有在解码之后才可以得到Content-Type头指定的内容类型。利用gzip压缩文档能够显著地减少HTML文档的下载时间。
Content-Length:表示内容长度。只有当浏览器使用持久HTTP连接时才需要这个数据。
Transfer-Encoding:告诉浏览器数据的传送格式。
注:设置应答头最常用是HttpServletResponse的setHeader,方法有两个参数:应答头的名字和值。和设置状态代码相似,设置应答头应该在发送任何文档内容之前进行。
setDateHeader方法和setIntHeadr方法专门用来设置包含日期和整数值的应答头,前者避免了把Java时间转换为GMT时间字符串的麻烦,后者则避免了把整数转换为字符串的麻烦。
setContentType:设置Content-Type头。大多数Servlet都要用到这个方法。
addCookie:设置一个Cookie(Servlet API中没有setCookie方法,因为应答往往包含多个Set-Cookie头)。
响应体就是响应的消息体,如果是纯数据就是返回纯数据,如果请求的是HTML页面,那么返回的就是HTML代码,如果是JS就是JS代码,如此之类。
Tcp结构
ACK:表示确认号是否有效,携带ACK标志的数据报文段为确认报文段;
PSH:提示接收端的应用程序应该立即从TCP接受缓冲区中读走数据,为接受后续数据腾出空间;
RST:表示要求对方重新建立连接,携带RST标志位的TCP报文段成为复位报文段;
SYN:表示请求建立一个连接,携带SYN标志的TCP报文段为同步报文段;
FIN:通知对方本端要关闭了,带FIN标志的TCP报文段为结束报文段。
Ack序号,占32位,只有ACK标志位为1时,确认序号字段才有效,Ack=Seq+1。
TCP头部选项是一个可变长的信息,这部分最多包含40字节,因为TCP头部最长60字节,(其中还包含前面20字节的固定部分)。
为什么在TCP首部的开始便是首部长度字段而UDP首部却没有?
因为UDP提供无连接服务,它的数据包包头,是固定长度的8字节,不存在可选字段,可以减少很多传输开销,所以它无需使用首部字段长,因为它的首部就是固定的。
Socket
ServerSocket serverSocket = new ServerSocket(12016);//监听do {Socket socket = serverSocket.accept();//获取IO流InputStream is = socket.getInputStream();OutputStream os = socket.getOutputStream();byte[] cache = new byte[1024];is.read(cache);//读cmd = new String(cache);os.write(returnText.getBytes());//写os.flush();is.close();os.close();} while (!returnText.equals("end"));
redisredisredis数据结构
字符串
相对于c的改进:
获取长度:c遍历字符串,redis直接读len
缓冲区安全:c字符串容易缓冲区溢出,比如:没有分配足够空间就执行拼接操作。
redis会先检查空间是否满足要求,如果不满足会自动扩充。
内存分配:c长度变化总是内存重新分配。
redis:1、预分配:如修改后大于1MB,分配 1MBfree空间。修改长度时检查,够的话就用free空间2、惰性空间释放:缩短时不需要释放空间,用free记录即可,留作以后使用。
二进制安全
c字符串程序读到空字符会误以为是结尾,所以 c字符串不能保存二进制文件。
而redis字符串二进制安全,因为有len记录长度。
链表:双端、无环、带长度记录、多态:用 void* 指针来保存节点值。
字典:数组、大小、掩码(等于size-1),已有节点数量
整数集合:数组、数量、编码方式
升级:计算新空间、转换并放入原来的数、放入新数
降级:无
压缩列表
连锁更新:在一个压缩列表中有多个连续的、长度介于 250 字节到 253 字节之间的节点 ,这时, 如果我们将一个长度大于等于 254 字节的新节点 new 设置为压缩列表的表头节点
对象
对象组成:// 类型// 编码// 指向底层实现数据结构的指针
不为对象关联固定编码,提升灵活性和效率,可以根据使用场景为对象设置不同编码。
列表对象:所有字符串元素长度小于 64 字节;元素数量小于 512 个:ziplist否则链表
哈希对象:所有键值对的字符串长度都小于 64 字节;数量小于 512 :ziplist 否则hashtable
集合对象:所有元素都是整数值;数量不超过 512 个:intset,否则hashtable
有序集合:所有元素的长度都小于 64 字节;元素数量小于 128 个;ziplist否则跳表
跳表
哈希表不是有序的。因此,在哈希表上只能做单个key的查找,不适宜做范围查找。
平衡树的插入和删除操作可能引发子树的调整,逻辑复杂,而skiplist的插入和删除只需要修改相邻节点的指针,操作简单又快速。
从算法实现难度上来比较,skiplist比平衡树要简单得多。
skiplist的key允许重复。这在最开始介绍的经典skiplist中是不允许的。
第1层链表不是一个单向链表,而是一个双向链表。这是为了方便以倒序方式获取一个范围内的元素。
在skiplist中可以很方便地计算出每个元素的排名(rank)。
1)它们的记忆力不是很强。基本上由你决定。更改有关节点具有给定数量级别的概率的参数将使内存密集度低于btree。
2)排序集通常是许多Zrange或Zrevrange操作的目标,即作为链表遍历跳过列表。通过此操作,跳过列表的缓存区域性至少与其他类型的平衡树一样好。
3)它们易于实现、调试等。例如,由于跳过列表的简单性,我收到了一个补丁(已经在redis master中),其中包含在o(log(n))中实现zrank的扩展跳过列表。它只需要对代码稍作修改。
HyperLogLog
是一种概率数据结构,用来估算数据的基数。数据集可以是访客 IP 地址,E-mail 或用户 ID。
这是一个很惊人的结果,以如此小的内存来记录如此大数量级的数据基数。
> PFADD customers alice dan->(integer) 1
> PFCOUNT everyone->(integer) 4
通过Hash函数,将元素转为64位比特串,0 代表反面,1 代表正面,从左往右第1个1
HyperLogLog 对象中的 registers 数组就是桶,它有两种存储结构,分别为密集存储结构和稀疏存储结构,从中我们可以看到 Redis 对节省内存极致地追求。
当 offset_bits 小于等于2时,说明一个桶就在该字节内,只需要进行倒转就能得到桶的值。
offset_bits 大于 2 ,则说明一个桶分布在两个字节内,此时需要将两个字节的内容都进行倒置,然后再进行拼接得到桶的值。
单线程
2、数据结构简单, Redis中的数据结构是专门进行设计的;
3、采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用考虑锁的问题,没有因为可能出现死锁。
持久化
优势:1,数据库将只包含一个文件,这对于文件备份来说非常完美;
2, 恢复容易。我们可以轻松的将一个文件压缩后转移到其它存储介质上;
3, 性能最大化;对于服务进程而言,开始持久化时,它唯一需要做的只是fork出子进程,之后再由子进程完成这些持久化的工作,这样就可以极大的避免服务进程执行IO操作了。
1, 存在数据丢失的可能。此前没有来得及写入磁盘的数据都将丢失。
2, 由于RDB是通过fork子进程来协助完成数据持久化工作的,因此,当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至是1秒钟。
AOF持久化-把每一次数据变化都写入到一个AOF文件中(“全持久化模式”)
1, 该机制可以带来更高的数据安全性。AOF有3种同步策略,即每秒同步、每修改同步和不同步。
缺点:平时运行效率低;AOF文件通常大于RDB文件;恢复速度慢。
Redis和数据库双写一致性
读的时候,先读缓存,缓存没有的话,就读数据库,然后取出数据放入缓存,同时返回响应。
缓存击穿雪崩穿透
缓存击穿是针对缓存中没有但数据库有的数据。场景是,当某个Key失效后,瞬间涌入大量的请求同一个Key,这些请求不会命中Redis,都会请求到DB,导致数据库压力过大
1、设置热点Key,自动检测热点Key,将热点Key的过期时间加大或者永不过期。
2、加互斥锁。当发现没有命中Redis,去查数据库的时候,在执行更新缓存的操作上加锁,当一个线程访问时,其它线程等待,这个线程访问过后,缓存中的数据会被重建,这样其他线程就可以从缓存中取值。
缓存雪崩是指大量Key同时失效,对这些Key的请求又会打到DB上,同样会导致数据库压力过大甚至挂掉。
1)让Key的失效时间分散开,可以在统一的失效时间上再加一个随机值,或者使用更高级的算法分散失效时间。
4)对存储层增加限流措施,当请求超出限制,提供降级服务(一般就是返回错误即可)
缓存穿透: 一些恶意的请求会故意查询redis不存在的key,请求量很大,就会对后端系统造成很大的压力。
1:对查询结果为空的情况也进行缓存,这样,再次访问时,缓存层会直接返回空值。缓存时间设置短一点,或者该key对应的数据insert了之后清理缓存。
解决Redis并发竞争Key问题
多个系统同时对一个 key 进行操作,但是最后执行的顺序和我们期望的顺序不同,这样也就导致了结果的不同!
推荐一种方案:分布式锁(zookeeper 和 redis 都可以实现分布式锁)。(如果不存在 Redis 的并发竞争 Key 问题,不要使用分布式锁,这样会影响性能)
Redis缓存策略
1. volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
版权声明:本文为CSDN博主「一只大白兔兔兔兔兔」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://fantianzuo.blog.csdn.net/article/details/102580321
4. allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key(这个是最常用的).
5. allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
6. no-eviction:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错。
哨兵
1)Sentinel(哨兵) 进程用于监控集群中 Master 主服务器工作的状态
2)在主服务器故障时,可以实现 Master 和 Slave 服务器切换,保证系统的高可用
1)每个 Sentinel(哨兵)进程每秒钟一次向所有服务器和哨兵进程发送一个 PING 命令。
2. 如果一个实例距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值, 这个实例会被 Sentinel(哨兵)进程标记为主观下线。
3. 如果一个 Master 主服务器被标记为主观下线,则正在监视这个 Master 主服务器的所有 Sentinel(哨兵)进程要以每秒一次的频率确认 Master 主服务器的确进入了主观下线状态。
5. 在一般情况下, 每个 Sentinel(哨兵)进程会以每 10 秒一次的频率向集群中的所有Master 主服务器、Slave 从服务器发送 INFO 命令。
解决会话
你可以设置nginx的分配策略,下次同一个还让同一个服务器来处理
但是很显然,这就和分布式和nginx初衷违背了:负载很难保证均衡。
专门一台服务器来解决,存session,其它服务器来这个服务器取session再用。
但是也有问题:你这个服务器挂了怎么办?别的服务器都是依赖这个服务器工作的。我们分布式部署本来就是为了解决性能的瓶颈啊。
更不行,想想就知道,本来就是为了解决分布式部署的问题,你把单独解决session的服务器又搞集群,和之前有什么区别呢?还不如一个服务器存一份简单呢。
最终,我们的主流办法使用nosql数据库,比如redis,来解决这个问题的。
sqlsqlsqlsqllsqlslqslqslq数据类型
整数:1byte是tinyint 2 smallint 3mediumint 4int 8bigint
浮点数:float/double/real。定义方式为:FLOAT(M,D)一共显示M位其中D位小数点后面
字符串:CHAR、VARCHAR、BINARY、VARBINARY、BLOB、TEXT、ENUM和SET。
Char固定长度,varchar随便,BINARY、VARBINARY是二进制,BLOB、TEXT是大的
mysql特点
存储引擎
如果要提供提交、回滚和恢复的事务安全(ACID 兼容)能力,并要求实现并发控制,InnoDB
如果数据表主要用来插入和查询记录,则 MyISAM 引擎提供较高的处理效率。
如果只是临时存放数据,数据量不大,并且不需要较高的数据安全性,在内存的 MEMORY ,MySQL 中使用该引擎作为临时表,存放查询的中间结果。
增删改
insert into student values ('201215128', '陈东', '18', '男', 'IS');
--2. 在表明后指定要插入数据的表名及属性列,属性列的顺序可与表定义中的顺序不一致
insert into student(sno, sname, sage, ssex, sdept)
values ('201215138', '陈东栋', '18', '男', 'CS');
--3. 插入部分列,未显示给出的列按空值计算,当然前提条件是那些列可以为空值
insert into student(sno, sname) values ('201215148', '陈栋');
update student set sage = 92 where sno = '200215121';
where 'CS' in (select sdeptfrom studentwhere sc.sno = student.sno);
Delete from student where sno = '201215148';
范式
1.减少数据冗余(这是最主要的好处,其他好处都是由此而附带的)
第二范式 – 要有主键,并且其他字段依赖于主键(方便唯一确定数据行,并定位其他字段)
BC范式 – 每个表中只有一个候选键(唯一标识表中每一行的键,候选键可以包含多个属性)
例:查询学生以及所在学院名,可以在学生表中不仅存储学院id,并且存储学院名
如果在UI中,只允许用户进行选择,不能自行输入,保证输入一致性
如果是程序员,对于类似学院名这种一般不变的代码表,在修改时直接对两张表都进行修改;如果经常变化,则可以加一个触发器。
2)N对N关系中复制属性,把两张表中经常需要的内容复制到中间关系表中以减少连接
索引优缺点和使用原则
1、所有的MySql列类型(字段类型)都可以被索引,也就是可以给任意字段设置索引
1、创建索引和维护索引要耗费时间,并且随着数据量的增加所耗费的时间也会增加
2、占空间,数据也会有最大上线设置的,大量索引文件可能比数据文件更快到上线
3、对表中的数据进行增加删改时,索引也需要动态的维护,降低了数据的维护速度。
1、对经常更新的表避免过多索引,对经常用于查询的字段应建索引,
2、数据量小的表最好不要使用索引,可能查询全部数据比遍历索引的时间还要短,
3-5、尽量选择区分度高的列,公式是count(distinct col)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少
唯一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就是0
一般需要join的字段我们都要求是0.1以上,即平均1条扫描10条记录
4、最左前缀匹配原则,非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配
5、索引列不能参与计算,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因:存的都是数据表中的字段值,需要所有元素都应用函数才能比较
索引分类
(1)直接创建索引CREATE INDEX index_name ON table(column(length))
`id` int(11) NOT NULL AUTO_INCREMENT ,
`title` char(255) CHARACTER NOT NULL ,
INDEX index_name (title(length))
列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。
(1)创建唯一索引CREATE UNIQUE INDEX indexName ON table(column(length))
特殊的唯一索引,一个表只能有一个主键,不允许有空值。建表同时创建主键索引:
`id` int(11) NOT NULL AUTO_INCREMENT ,
指多个字段上创建的索引,只有在查询条件中使用了创建索引时的第一个字段,索引才会被使用。使用组合索引时遵循最左前缀集合
ALTER TABLE `table` ADD INDEX name_city_age (name,city,age);
主要用来查找文本中的关键字,而不是直接与索引中的值相比较。fulltext索引跟其它索引大不相同,它更像是一个搜索引擎,而不是简单的where语句的参数匹配。
fulltext索引配合match against操作使用,而不是一般的where语句加like。
它可以在create table,alter table ,create index使用,不过目前只有char、varchar,text 列上可以创建全文索引。
在数据量较大时候,现将数据放入一个没有全局索引的表中,然后再用CREATE index创建fulltext索引,要比先为一张表建立fulltext然后再将数据写入的速度快很多。
`id` int(11) NOT NULL AUTO_INCREMENT ,
`content` text CHARACTER NULL ,
聚集索引的叶子节点存储行记录,因此, InnoDB必须要有,且只有一个聚集索引:
(2)如果表没有定义PK,则第一个not NULL unique列是聚集索引;
(3)否则,InnoDB会创建一个隐藏的row-id作为聚集索引;
5.6优化,索引下推:a=1 and b like….and c like…..,之前按查出第一个条件,返回数据,再筛,现在按索引筛完了like再返回查。
索引区别
如果有大量重复键值的情况下,哈希索引的效率会很低,因为存在哈希碰撞问题
局部性原理:当一个数据被用到时,其附近的数据也通常会马上被使用——程序运行期间所需要的数据通常比较集中。磁盘顺序读取的效率很高(不需寻道时间,只需很少的旋转时间)
默认16KB一页,1行数据1K,一页16条数据。假设 bigint=8字节,指针在innodb是6字节,8+6=14,16KB/14=1170,三层可满足千万记录。
事务特性:ACID
原子性(Atomicity):事务作为一个整体执行,对数据的操作要么全被执行,要么都不执行。
隔离性(Isolation):多个事务并发执行时,不互相影响。
持久性(Durability):一个事务一旦提交,他对数据库的修改应该永久保存在数据库中。
并发错误
第一类丢失更新:某一个事务的回滚,导致另外一个事务已更新的数据丢失了。
第二类丢失更新:某一个事务的提交,导致另外一个事务已更新的数据丢失了。
脏读:另一个事务修改了,但尚未提交,本事务读到未被提交的数据。
不可重复读:一个执行中,另一个更新,本事务先后读到的数据不一致。
幻读:某事务对同一个表前后查询到的行数不一致(中间有人插入)
隔离级别
- Read Uncommitted:读取未提交的数据。事务可以读取到另一个事务未提交的修改。
- Read Committed:(有的库默认)读取已提交的数据。事务只能读取另个事务已提交修改。
- Repeatable Read:(mysql默认)可重复读。同一个事务多次读取相同数据返回结果一样。
- Serializable:串行化 事务串行执行。读写数据锁表
并发控制技术(锁)
基本的封锁类型有两种:排它锁(X锁,exclusive locks)、共享锁(S 锁,share locks)
排它锁又称写锁,对A加排它锁之后,其他事务不能对A加 任何类型的锁(排斥读和写)
共享锁又称读锁,对A加共享锁之后,其他事务只能对A加S锁,不能加X锁(只排斥写)
行级锁:mysql默认,开销大、加锁慢、锁冲突概率高、可能死锁
间隙锁(NK):行级,使用范围条件时,对范围内不存在的记录加锁。一是为了防止幻读,二是为了满足恢复和复制的需要
共享锁(S):SELECT … FROM … WHERE … LOCK IN SHARE MODE;
排他锁(X):SELECT … FROM … WHERE … FOR UPDATE;
间隙锁(NK):上述SQL采用范围条件时,InnoDB对不存在的记录自动增加间隙锁。
MVCC的实现,是通过保存数据在某个时间点的快照来实现的。也就是说,不管需要执行多长时间,每个事务看到的数据是一致的。根据事务开始的时间不同,每个事物对同一张表,同一时刻看到的数据可能是不一样的。
悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。
乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。
- 版本号、时间戳等。。。在更新数据前,检查版本号是否发生变化。若变化则取消本次更新,
UPDATE … SET …,VERSION=#{version+1} WHERE … AND VERSION=${version}
是一种无锁的算法,该算法涉及3个操作数(内存值V、旧值A、新值B),当V等于A时,
采用原子方式用B的值更新V的值。该算法通常采用自旋操作,也叫自旋锁。它的缺点是:
• ABA问题:某线程将A该为B,再改回A,则CAS会误认为A没被修改过。
• 自旋操作采用循环的方式实现,若加锁时间长,则会给CPU带来巨大的开销。
死锁
事务1: UPDATE T SET … WHERE ID = 1; UPDATE T SET … WHERE ID = 2;
事务2: UPDATE T SET … WHERE ID = 2; UPDATE T SET … WHERE ID = 1;
1. 一般InnoDB会自动检测到,并使一个事务回滚,另一个事务继续;
2. 设置超时等待参数 innodb_lock_wait_timeout;
1. 不同的业务并发访问多个表时,应约定以相同的顺序来访问这些表;
2. 以批量的方式处理数据时,应事先对数据排序,保证线程按固定的顺序来处理数据;
3. 在事务中,如果要更新记录,应直接申请足够级别的锁,即排他锁;
分布式事务
分布式事务就是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。
分布式事务产生的原因总的来说有两个:1.service产生多个节点2.resource产生多个节点
- C (一致性):对某个指定的客户端来说,读操作能返回最新的写操作。
对于数据分布在不同节点上的数据来说,如果在某个节点更新了数据,那么在其他节点如果都能读取到这个最新的数据,那么就称为强一致,如果有某个节点没有读取到,那就是分布式不一致。
A (可用性):非故障的节点在合理的时间内返回合理的响应(不是错误和超时的响应)。
P (分区容错性):当出现网络分区后,系统能够继续工作。打个比方,这里个集群有多台机器,有台机器网络出现了问题,但是这个集群仍然可以正常工作。
分布式事务常见的方案:2PC(两阶段提交)协调者(都成功才执行)
准备阶段(协调者询问参与者事务是否执行成功,参与者发回事务执行结果)
提交阶段(如果事务在每个参与者上都执行成功,事务协调者发送通知让参与者提交事务;否则,协调者发送通知让参与者回滚事务)
需要注意的是,在准备阶段,参与者执行了事务,但是还未提交。只有在提交阶段接收到协调者发来的通知后,才进行提交或者回滚。
2.单点问题:协调者在 2PC 中起到非常大的作用,发生故障将会造成很大影响。
3.数据不一致:网络异常,只有部分参与者接收到 Commit 消息,只有部分参与者提交了事务,使得系统数据不一致。
SQL语句性能优化
1. 对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。
2. 应尽量避免在 where 子句中使用!=或<>操作符, MySQL只有对以下操作符才使用索引:<,<=,=,>,>=,BETWEEN,IN,以及某些时候的LIKE。
3. 应尽量避免在 where 子句中使用 or 来连接条件, 否则将导致引擎放弃使用索引而进行全表扫描,可以使用UNION合并查询
4. 尽可能的使用 varchar代替 char, 因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。
索引的优化
1.避免有索引但未被用到的情况:(1) Like的参数以通配符开头时(2)where条件不符合最左前缀原则时(3)使用!= 或 <> 操作符时(4)使用or来连接条件
3.对order by语句进行优化:1.重写order by语句以使用索引 2.避免在order by子句中使用表达式。
4.对group by语句进行优化:将不需要的记录在group by之前过滤掉
分库分表
垂直分库就是根据业务耦合性,将关联度低的不同表存储在不同的数据库。
水平切分分为库内分表和分库分表,是根据表内数据内在的逻辑关系,将同一个表按不同的条件分散到多个数据库或多个表中,每个表中只包含一部分数据,从而使得单个表的数据量变小,达到分布式的效果。
Kafka
- 生产者kafkaTemplate.send(topic, data);
- 消费者@KafkaListener(topics = {"test"})
public void handleMessage(ConsumerRecord record) {}
高吞吐量、低延迟:kafka每秒可以处理几十万条消息,它的延迟最低只有几毫秒;
持久性、可靠性:消息被持久化到本地磁盘,并且支持数据备份防止数据丢失;
容错性:允许集群中节点故障(若副本数量为n,则允许n-1个节点故障);
日志收集:一个公司可以用Kafka可以收集各种服务的log,通过kafka以统一接口服务的方式开放给各种consumer;
运营指标:kafka也经常用来记录运营监控数据。包括收集各种分布式应用的数据,生产各种操作的集中反馈,比如报警和报告;
Broker:Kafka节点,一个Kafka节点就是一个broker,多个broker可以组成一个Kafka集群。
Topic:一类消息,消息存放的目录即主题,例如page view日志、click日志等都可以以topic的形式存在,Kafka集群能够同时负责多个topic的分发。
Partition:topic物理上的分组,一个topic可以分为多个partition,每个partition是一个有序的队列
Segment:partition物理上由多个segment组成,每个Segment存着message信息
Consumer : 订阅topic消费message, consumer作为一个线程来消费
import java.util.*;class BoundedBlockingQueue {private int size;private int capacity;private int[] queue;// 指向队头的指针private int last; // 指向队列尾private int first;// JDK 的通知模式private final ReentrantLock lock;private final Condition notFull;private final Condition notEmpty;public BoundedBlockingQueue(int capacity) {this.size = 0;this.last = 0;this.first = 0;this.capacity = capacity;this.queue = new int[capacity+1];this.lock = new ReentrantLock();notFull = this.lock.newCondition();notEmpty = this.lock.newCondition();}// 进队列public void enqueue(int element) throws InterruptedException {// 队列不满的情况下可以入队尾,在队尾添加元素final ReentrantLock lock = this.lock;lock.lockInterruptibly();try{while(size == capacity)notFull.await();// 队列不满的情况下可以入队尾,在队尾添加元素queue[last] = element;last = (last + 1)% capacity;size ++;notEmpty.signal();}finally{lock.unlock();}}// 出队列public int dequeue() throws InterruptedException {// 有元素的情况下才可以出队列,删掉队头的元素final ReentrantLock lock = this.lock;lock.lockInterruptibly();try{while(size == 0)notEmpty.await();int e = queue[first];first = (first + 1) % capacity;size --;notFull.signal();return e;}finally{lock.unlock();}}public int size() {return size;}}
Es
- 发布帖子时,将帖子异步的提交到Elasticsearch服务器。
- 增加评论时,将帖子异步的提交到Elasticsearch服务器。
ES: index(索引)-->type(类型)-->document(文档)-->field(字段)
mysql: database(数据库)-->table(表)-->row(行)-->line(列)
同时ES是分布式文档数据库,每个字段均可被索引,而且每个字段的数据均可被搜索,能够横向扩展至数以百计的服务器存储以及处理PB级的数据。
可以在极短的时间内存储、搜索和分析大量的数据。通常作为具有复杂搜索场景情况下的核心发动机。
public class Trie {private boolean is_string=false;private Trie next[]=new Trie[26];public Trie(){}public void insert(String word){//插入单词Trie root=this;char w[]=word.toCharArray();for(int i=0;i<w.length;++i){if(root.next[w[i]-'a']==null)root.next[w[i]-'a']=new Trie();root=root.next[w[i]-'a'];}root.is_string=true;}public boolean search(String word){//查找单词Trie root=this;char w[]=word.toCharArray();for(int i=0;i<w.length;++i){if(root.next[w[i]-'a']==null)return false;root=root.next[w[i]-'a'];}return root.is_string;}public boolean startsWith(String prefix){//查找前缀Trie root=this;char p[]=prefix.toCharArray();for(int i=0;i<p.length;++i){if(root.next[p[i]-'a']==null)return false;root=root.next[p[i]-'a'];}return true;}}
当年,兔子学姐靠这个面试小抄拿了个22k相关推荐
- 备战金九银十!【Java 面试小抄】涵盖当下最时新热门技术点,学完 offer 得拿到手软~
面试的重要性: 最近替公司面了几个应聘者,结果给我整抑郁了,9 点的面试 9 点 10 分才到!!!这还不是最重要的,重要的是穿着一眼就让人看出来没有对这场面试很重视的感觉!!但是小编还是面完了,很多 ...
- 30万字,579页《面试小抄》
你好,我是田哥 兜兜转转,我的<面试小抄>第三版已封版,本次面试小抄在之前两个版本上做了特别大的改动,主要还是是希望你能通过这一个面试小抄就能搞定面试. 主要内容: 写简历及面试准备方法论 ...
- 卷妹的面试小抄每日更新Day1
卷妹的面试小抄每日更新Day1
- 小白学数据_|_28张小抄表大放送:Python,R,大数据,机器学习
1. Python的数据科学快速入门指南 如果你刚入门Python,那么这张小抄表非常适合你.查看这份小抄表,你将获得循序渐进学习Python的指导.它提供了Python学习的必备包和一些有用的学习技 ...
- 当年,学姐总结奇安信18k常问面试题
她确实拿了18k,真人真事,也不是很高,我没必要编. 黑色字为问题,红色字为答案,空行为一个面试过程 自我介绍 家在哪,工作地 测试需要掌握啥 V模型W模型 最典型的V模型版本一般会在其开始部分对软件 ...
- 学姐的大厂面试总结,想进大厂的必看!!!
一.明确自己的目标 1,疏理目前自己的整体能力. 2,确定自己的期望薪资. 3,明确自己更看重下一份工作的哪些点(离家近?薪资?能力成长空间?) 二.确定和筛选目标公司 1,公司规模(成立时间.人数规 ...
- 快来看看这份 HashMap 面试小抄
对于 JAVA 求职者来说,HashMap 可谓是重中之重,是面试必考点.然而 HashMap 的知识点非常多,复习起来花费精力很大,库森当年校招面试时就经历过这种痛苦.所以,结合自己的面试经验,斗胆 ...
- 你连原理都还没弄明白?快来瞧瞧这份Spring面试小抄
二叉树 定义 二叉树是n(n>=0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两棵互不相交的.分别称为根结点的左子树和右子树组成. 图解 二叉树特点 由二叉树定义以及 ...
- 肝文-MySQL面试小抄
使用覆盖索引减少IO mysql的索引类型主要分为聚集索引和非聚集索引,通过聚集索引可以获取到整行数据,而通过非聚集索引只能获得主键id和当前字段.当我们要查询的字段就是非聚集索引叶子含有的字段(pr ...
最新文章
- java sql编码_java+sql 编码 UTF-8、ISO-8859-1、GBK
- SpringBoot + Elasticsearch7.6实现简单查询及高亮分词查询
- getElementByName????????,????????,
- 《Python核心编程》第二版第36页第二章练习 续一 -Python核心编程答案-自己做的-...
- C#中自定义类数组和结构数组的使用
- Sybase时间日期函数
- 【Flink】Flink各种UDF简介
- linux中sed的用法
- Spark HA 集群搭建【1、基于文件系统的手动HA 2、基于zk的自动HA】
- Bravo.Reporting:使用 .Net 实现基于 ODF 文档格式的报表系统
- xposed框架_免root使用xposed框架的另一种方法!
- 决策树之CART 算法
- 深圳内推 | 深圳计算科学研究院招聘机器学习助理工程师(校招)
- rc时间常数定义_时间常数RC的计算方法
- 【Excel VBA】银行卡信用卡卡号校验功能函数
- java获取指定时间为第几周_Java8根据一年中的第几周获得Monday
- 【C++】C++基础语法
- (复现)CVE-2021-21985 Vmware vcenter远程代码执行RCE
- 使用v-charts报错
- idea设置java-google-style
热门文章
- python分布式框架_高性能分布式执行框架——Ray
- EISCONN的故事
- QString与中文问题
- java 合并单元格 把数据合并没了_合并单元格
- revo uninstaller pro 序列号_iPhone12/pro已下架,在天猫
- java json 返回null,[] Spring4 MVC 返回json格式时候 设置不返回null值属性的有关问题...
- 电脑用linux命令大全,电脑操作时常用的一些Linux命令
- 拼接dem,山地出现平地
- 【转】Wireshark网络抓包(二)——过滤器
- 【转】Magento2 数据库操作