一、常识类

1. 位运算

  • \<< 左移 首先转换成二进制,然后左移,低位补0。符号位参与向左移动,在移动后的结果中,最左位可能是1 或者 0。

    • 假如在整型数 (32位) 上移动的位数是 32 位,无论是否带符号位以及移动方向,均为本身, 即35>>1 与35>>33是一样的结果。 长整型(64位)同理。
  • >> 有符号右移 如果是正数,则前面补0,如果是负数,则前面补1。

  • >>> 无符号右移 无论是正数还是负数,前面都补0。

  • & (与) 两个操作数中位都为1,结果才为1,否则结果为0。

    • 逻辑与(&&) 有短路功能,而按位与没有。既在逻辑与(&&) 中,前一个为false,不执行后面那个表达式。
  • | (或) , 两个位只要有一个为1,那么结果就是1,否则就为0。

    • 逻辑或 有短路功能,按位或没有短路功能
  • ~ (非),按位取反,如果位为0,结果是1,如果位为1,结果是0。

  • ^ (异或),两个操作数的位中,相同则结果为0,不同则结果为1。

    • 键盘 6 上方的数字,1 ^ 1 = 0, 0 ^ 0 = 0, 1 ^ 0 = 1, true ^ ture = false, ture ^ false = ture

    image-20190321125707943

image-20190321125744020
image-20190321125744020

2. CPU与内存

CPU ( Central Processing Unit )是一块超大规模的集成电路板,是计算机的核心部件,承载着计算机的主要运算和控制功能,是计算机指令的最终解释模块和执行模块。

2.1 cpu 的内部结构

总的来说,就是由控制器和运算器组成的,内部寄存器使这两者协同更加高效。

image-20190321164423790
image-20190321164423790
  1. 控制器

    控制器由图 1-7 中所示的控制单元、指令译码器、指令寄存器组成。其中控制单元是 CPU 的大脑,由时序控制和指令控制等组成; 指令译码器是在控制单元的协调下完成指令读取、分析并交由运算器执行等操作; 指令寄存器是存储指令集,当前流行的指令集包括 X86、SSE、MMX 等。控制器有点像一个编程语言的编译器,输入 0与1 的源码流,通过译码和控制单元对存储设备的数据进行读取,运算完成后,保存回寄存器,甚至是内存。

  2. 运算器

    运算器的核心是算术逻辑运算单元,即 ALU,能够执行算术运算或逻辑运算等各种命令,运算单元会从寄存器中提取或存储数据。相对控制单元来说,运算器是受控的执行部件。任何编程语言诸如 a+b 的算术运算,无论字节码指令,还是汇编指令,最后一定会以 0 与 1 的组合流方式在部件内完成最终计算,并保存到寄存器,最后送出 CPU。平时理解的枝与堆,在 CPU 眼里都是内存。

  3. 寄存器

    最著名的寄存器是 CPU 的高速缓存 L1、 L2,缓存容量是在组装计算机时必问的两个 CPU 性能问题之一。缓存结构和大小对 CPU 的运行速度影响非常大,毕竟 CPU的运行速度远大于内存的读写速度,更远大于硬盘。基于执行指令和热点数据的时间局部性和空间局部性, CPU 缓存部分指令和数据,以提升性能。但由于 CPU 内部空间狭小且结构复杂,高速缓存远小于内存空间。

​ CPU 是一个高内聚的模块化组件,它对外部其他硬件设备的时序协调、指令控制、 存取动作,都需要通过操作系统进行统一管理和协调。所谓的 CPU 时间片切分,并非 CPU 内部能够控制与管理 。 CPU 部件是一个任劳任怨的好公民代表,只要有指令就会马不停蹄地执行,高级语言提供的多线程技术和并发更多地依赖于操作系统的调配,并行更多依赖于 CPU 多核技术。多核 CPU 即在同一块基板上封装了多个 Core。 还有一种提升 CPU 性能的方式是超线程,即在一个 Core 上执行多个线程 。如图 1-8所示为 2个Core,但是有 4个逻辑CPU,并有对应独立的性能监控数据。

image-20190323155026953

2.2 cup 与 内存

CPU与内存的执行速度存在巨大的鸿沟,如图1-8所示的L2和L3分别是 256KB和 4MB,它们是CPU和内存之间的缓冲区,但并非所有的处理器都有 L3缓存。 越往CPU 核心靠近,存储越贵,速度越快。越往下,存储越便宜,速度越慢。

image-20190321170607455
image-20190321170607455

3. TCP/IP

TCP/IP ( Transmission Control Protocol / Internet Protocol ) 中文译为 传输控制协议 / 因特网互联协议,这个大家族里的其他知名协议还有 HTTP、 HTTPS、 FTP、SMTP、 UDP、ARP、 PPP、 IEEE 802.x等。 TCP / IP 是当前流行的网络传输协议框架,从严格意义上讲它是一个协议族,因为 TCP、IP 是其中最为核心的协议,所以就把
该协议族称为 TCP/IP。

3.1 TCP/IP 协议分层框架
image-20190321182814762
image-20190321182814762
  • 链路层:单个 0、1是没有意义的,链路层以字节为单位把 0 与 1 进行分组,定义数据帧,写入源和目标机器的物理地址、数据、校验位来传输数据。如下为 以太网 的帧协议。

    image-20190321184606467

    MAC 地址长 6 个字节共 48 位,通常使用十六进制数表示。使用 ifconfig -a 命令即可看到 MAC 地址。如我的mac 地址 8c:85:90:ae:59:b5, 即前 24 位由管理机构统一分配, 后 24 位由厂商自己分配,保证网卡全球唯一。网卡就像家庭地址 一样 ,是计算机世界范围内的唯一标识。

  • 网络层:根据 IP 定义网络地址,区分网段。子网内根据 地址解析协议( ARP ) 进行 MAC 寻址 , 子网外进行路由转发数据包,这个数据包即 IP 数据包。

  • 传输层:数据包通过网络层发送到目标计算机后,应用程序在传输层定义逻辑端口,确认身份后,将数据包交给应用程序,实现端口到端口间通信。最典型的传输层协议是 UDP 和 TCP。 UDP 只是在 IP 数据包上增加端口等部分信息 ,是面向无连接的,是不可靠传输,多用于视频通信、电话会议等(即使少一帧数据也无妨)。与之相反,TCP 是面向连接的。所谓面向连接,是一种端到端间通过失败重传机制建立的可靠数据传输方式,给人感觉是有一条固定的通路承载着数据的可靠传输。

  • 应用层:传输层的数据到达应用程序时,以某种统一规定的协议格式解读数据。比如,E-mail 在各个公司的程序界面、操作、管理方式都不一样,但是都能够读取邮件内容,是因为 SMTP 协议就像传统的书信格式一样,按规定填写邮编及收信人信息。

    ​ 总结一下, 程序在发送消息时,应用层 接既定的协议打包数据,随后由传输层加上双方的端口号 ,由网络层 加上双方的 IP 地址,由链路层 加上 双方的MAC 地址 , 并将数据拆分成数据帧 , 经过多个路由器 和 网关后 , 到达目标机器。简而言之, 就是按 “端口→ IP 地址→ MAC 地址 ” 这样的路径进行数据的封装和发送 , 解包的时候反过来操作即可。

3.2 IP 协议

IP是面向无连接、无状态的,没有额外的机制保证发送的包是否有序到达。IP 首先规定出 IP 地址格式,该地址相当于在逻辑意义上进行了网段的划分,给每台计算机额外设置了一个唯一的详细地址。

IP地址属于网络层,主要功能在 WLAN 内进行路由寻址,选择最佳路由。IP报文格式如图 1-15 所示,共 32 位 4 个字节,通常用十进制数来表示。IP 地址的掩码 0xffffff00 表示 255.255.255.0,掩码相同,则在同一子网内。 IP 协议在 IP 报头中记录 源 IP 地址和目标 IP 地址,如图 1-15 所示。

image-20190324150749760

IP是 TCP/IP 的基石,几乎所有其他协议都建立在 IP所提供的服务基础上进行传输,其中包括在实际应用中用于传输稳定有序数据的 TCP。

3.3 TCP 建立连接

传输控制协议( Transmission Control Protocol, TCP ),是一种面向连接、确保数据在端到端间可靠传输的协议。面向连接是指在发送数据前,需要先建立一条虚拟的链路,然后让数据在这条链路上“流动”完成传输。为了确保数据的可靠传输,不仅需要对发出的每一个字节进行编号确认,校验每一个数据包的有效性,在出现超时情况时进行重传,还需要通过实现滑动窗口和拥塞控制等机制,避免网络状况恶化而最终影响数据传输的极端情形。每个TCP 数据包是封装在 IP 包中的,每个 IP 头的后面紧接着的是TCP头, TCP报文格式如图 1-16所示。

image-20190324151510091
image-20190324151510091

协议第一行的两个端口号各占两个字节,分别表示了源机器和目标机器的端口号。这两个端口号与 IP 报头中的 源 IP 地址和目标IP 地址所组成的 四元组 可唯一标识一条TCP 连接。由于 TCP 是面向连接的 ,因此有服务端和客户端之分。需要服务端先在相应的端口上进行监昕,准备好接收客户端发起的建立连接请求。当客户端发起第一个请求建立连接的 TCP 包时,目标机器端口就是服务端所监听的端口号。

协议第二行和第二行是序列号,各占 4 个字节。前者是指所发送数据包中数据部分第一个字节的序号,后者是指期望收到来自对方的下一个数据包中数据部分第一个字节的序号。

三次握手

三次握手指的 是建立连接的三个步骤

  1. A 机器发出一个数据包并将 SYN 置 1 ,表示希望建立连接。这个包中的序列号假设是 x。

  2. B 机器收到 A 机器发过来的数据包后,通过 SYN 得知这是一个建立连接的请求,于是发送一个响应包并将 SYN 和 ACK 标记都置 1。假设这个包中的 序列号是 y,而确认序列号必须是 x+1 ,表示收到了 A 发过来的 SYN。 在 TCP 中, SYN 被当作数据部分的一个字节。

  3. A 收到 B 的响应包后需进行确认,确认包中将 ACK 置 1,并将确认序列号 设置为 y+1,表示收到了来自 B 的 SYN。

image-20190324160140040
image-20190324160140040

这里为什么需要第 3 次握手?它有两个主要目的:信息对等和防止超时。先从信息对等角度来看,如表 1-6 所示,双方只有确定4类信息,才能建立连接。在第2次 握手后 , 从 B 机器视角看还有两个红色的 NO 信息无法确认。在第 3 次握手后, B 机 器才能确认自己的发报能力和对方的收报能力是正常的。

image-20190324160810389

3.4 TCP 断开连接

TCP 是全双工通信,双方都能作为数据的发送方和接收方,但 TCP 连接也会有断开的时候。所谓相爱容易分手难,建立连接只有三次,而挥手断开则需要四次。

四次挥手
image-20190324164701806
image-20190324164701806
3.5 连接池

服务器可以快速创建和断开连接,但对于高并发的后台服务器而言,连接的频繁创建与断开,是非常重的负担。在客户端与服务端之间可以事先创建若干连接并提前放置在连接池中,需要时可以从连接池直接获取,数据传输完成后,将连接归还至连接池中,从而减少频繁创建和释放连接所造成的开销。例如, RPC 服务集群的注册中心与服务提供方、消费方之间,消息服务集群的缓存服务器和消费者服务器之间,应用后台服务器和数据库之间,都会使用连接池来提升性能。

数据库连接池

当应用启动时,连接池初始化最小连接数( MIN ); 当外部请求到达时,直接使用空闲连接即可。假如并发数达到最大( MAX ),则需要等待,直到超时。如果一直未拿到连接,就会抛出异常。

如果MIN 过小,可能会出现过多请求排队等待获取连接,如果 MIN 过大,会造成资源浪费。如果 MAX 过小,则峰值情况下仍有很多请求处于等待状态;如果 MAX 过大,可能导致数据库连接被占满,大量请求超时,进而影响其他应用,引发服务器连环雪崩。

从经验上来看,在数据库层面的请求应答时间必须在 100ms 以内,秒级的 SQL 查询通常存在巨大的性能提升空间,有如下应对方案:

  1. 建立高效且合适的索引。 索引谁都可以建,但要想建好难度极大。因为索引既有数据特征,又有业务特征,数据量的变化会影响索引的选择,业务特点不一样,索引的优化思路也不一样。通常某个字段平时不用,但是某种触发场景下命中“索引缺失”的字段会导致查询瞬间变慢。所以,要事先明确业务场景,建立合适的索引。
  2. 排查连接资源未显式关闭的情形。 要特别注意在 ThreadLocal 或流式计算中使用数据库连接的地方。
  3. 合并短的请求。根据 CPU 的空间局部性原理,对于相近的数据, CPU 会一起提取到内存中。另外,合并请求也可以有效减少连接的次数。
  4. 合理拆分多个表join的SQL, 若是超过三个表则禁止join。 如果表结构建得不合理,应用逻辑处理不当,业务模型抽象有问题 , 那么三表 join 的数据量由于笛卡儿积操作会呈几何级数增加,所以不推荐这样的做法。另外,对于需要 join 的字段,数据类型应保持绝对一致。多表关联查询时,应确保被关联的字段要有索引。
  5. 使用临时表。 某种情况下,该方法是一种比较好的选择。曾经遇到一个场景不使用临时表需要执行 1 个多小时 , 使用临时表可以降低至 2 分钟以内。因为在不断的嵌套查询中,已经无法很好地利用现有的索引提升查询效率,所以把中间结果保存到临时表, 然后重建索引, 再通过临时表进行后续的数据操作。
  6. 应用层优化。 包括进行数据结构优化、并发多线程改造等。
  7. 改用其他数据库。因为不同数据库针对的业务场景是不同的,比如 Cassandra、 MongoDB 。