JAVA
2021-02-26 17:18:40 1 举报
AI智能生成
面试必看
作者其他创作
大纲/内容
Spring
Spring Core
核心类库,Spring的所有功能都依赖于该类库,Core主要实现IOC功能。
Spring Context
Spring AOP
主要的功能是:日志记录,性能统计,安全控制,事务处理,异常处理等等
Spring DAO
Spring ORM
Spring Web
SpringMVC
计算机网络
分层
应用层
应用层
表示层
会话层
传输层
网络层
数据链路层
数据链路层规定一套协议,专门的给0、1信号进行分组,以及规定不同的组代表什么意思,从而双方计算机都能够进行识别,这个协议就是“以太网协议”
以太网协议
以太网规定,每组的电信号就是一个数据包,每个数据包我们可以成为“帧”。每帧的组成是由标头(Head)和数据(Data)组成。
物理层
顾名思义,用物理手段将电脑连接起来,就像我们上边讲到的计算机之间的物理连线。主要用来传输0、1信号
dubbo
消息队列
kafka
RocketMQ
RabbitMQ
主要组件
ConnectionFactory
创建connection的工厂
Connection
RabbitMQ的socket连接,封装了socket协议的相关信息
Channel
Channel是我们与RabbitMQ打交道的最重要的一个接口,我们大部分的业务操作是在Channel这个接口中完成的,包括定义Queue、定义Exchange、绑定Queue与Exchange、发布消息等。
Queue
Queue(队列)是RabbitMQ的内部对象,用于存储消息,队列结构,消费者可以从Queue中获取消息并消费,多个消费者可以消费同一个Queue,这时Queue中的消息会被平均分摊给多个消费者进行处理,而不是每个消费者都收到所有的消息并处理。
Message acknowledgment
为了避免消费者没有处理完消息就宕机,造成消息丢失,可以要求消费完之后给mq发送一个回执,mq收到回执才会移除消息,如果mq没有收到回执且检测到消费者断开,就会把该消息发给其他消费者。这里没有timeout的概念,一个消费者消费再长时间,只要连接没有断开,消息就会一直堆积在队列中,所以如果开启了ack,消费完之后一定要发送回执。
Message durability
如果希望在rabbitmq重启的时候,也不会丢失消息,那么就需要将queue和Message都设置为持久化,但是依然会有小概率的丢失问题,解决这种小概率的问题需要用到事务
Prefetch count
queue给多消费者分发消息时,设置每次分发的条数
Exchange
生产者将消息发送给Exchange,Exchange将消息分发给queue,分发规则由routing key,ExchangeType,binding key共同决定。
routing key
生产者发送消息给Exchange时,会指定一个routing key,长度限制为255 bytes
Binding
RabbitMQ中通过Binding将Exchange与Queue关联起来,这样RabbitMQ就知道如何正确地将消息路由到指定的Queue了
Binding key
绑定Exchange和queue时,需要指定一个binding key。
binding key中可以存在两种特殊字符“*”与“#”,用于做模糊匹配,其中“*”用于匹配一个单词,“#”用于匹配多个单词(可以是零个)
Exchange Types
fanout
发送到bind的所有队列
direct
发送到binding key和routing key完全匹配的队列
topic
发送到binding key与routing key可以正则匹配的队列
headers
headers不依赖于binding key和routing key,而是发消息时设置一个headers,一般不用
构建流程
声明MessageQueue
为什么要先声明
Consumer无法订阅或者获取不存在的MessageQueue中信息
消息被Exchange接受以后,如果没有匹配的Queue,则会被丢弃。
Exchange
Binding
设计模式
Reactor反应器模式
Reactor反应器线程
负责查询IO事件,当检测到一个IO事件,将其发送给相应的Handler处理器去处理。这里的IO事件,就是NIO中选择器监控的通道IO事件。
Handlers处理器
与IO事件(或者选择键)绑定,负责IO事件的处理。完成真正的连接建立、通道的读取、处理业务逻辑、负责将结果写出到通道等。
Netty
简介
子主题 1
异步事件驱动框架,用于快速开发高性能服务端和客户端
封装了JDK底层的BIO和NIO模型,提供高度可用的API
自带编码器和解码器解决拆包粘包的问题,用户只需关注业务逻辑
精心设计Reactor线程模型支持高并发海量连接
自带协议栈,用户无需关心
JAVA
锁
synchronized
lock
volatile
实现
读
读操作前加LoadLoad屏障,保证自己的读是最新的,读操作后加LoadStore屏障,保证读对之后的写可见
写
写操作前加StoreStore屏障,写操作后加StoreLoad屏障
内存屏障
LoadLoadBarrier
屏障前的读与屏障后的读不能重排序
LoadStoreBarrier
屏障前的读和屏障后的写不能重排序
StoreLoadBarrier
屏障前的写和屏障后的读不能重排序
StoreStoreBarrier
屏障前的写和屏障后的写不能重排序
JVM
程序计数器
一个cpu同一时间只能执行一条线程中的指令,为了在切换线程时可以恢复到正确的位置,每个线程都需有独立的一个程序计数器,计数器记录虚拟机字节码指令的地址。native方法计数器为空,计数器没有OutOfMemoryError
虚拟机栈
每个方法被执行的时候都会创建一个栈帧用于存储局部变量表,操作栈,动态链接,方法出口等信息。每一个方法被调用的过程就对应一个栈帧在虚拟机栈中从入栈到出栈的过程。
异常
线程请求的栈深度大于虚拟机允许的栈深度,将抛出StackOverflowError
比如无穷递归
虚拟机栈空间可以动态扩展,当动态扩展是无法申请到足够的空间时,抛出OutOfMemory异常。
线程过多
本地方法栈
和虚拟机栈类似,但是本地方法栈中入栈的都是native方法
堆
堆是java虚拟机管理内存最大的一块内存区域,因为堆存放的对象是线程共享的,所以多线程的时候也需要同步机制。
逃逸分析
逃逸分析是一种分析手段,通过动态分析对象的作用域,为其他优化手段提供依据
方法逃逸
当一个对象在方法中定义之后,作为参数传递到其它方法中
线程逃逸
如类变量或实例变量,可能被其它线程访问到
优化行为
同步消除
线程同步本身比较耗,如果确定一个对象不会逃逸出线程,无法被其它线程访问到,那该对象的读写就不会存在竞争,则可以消除对该对象的同步锁,通过-XX:+EliminateLocks可以开启同步消除。
标量替换
标量是指不可分割的量,如java中基本数据类型和reference类型
一个数据如果可以继续分解,称为聚合量
如果把一个对象拆散,将其成员变量恢复到基本类型来访问就叫做标量替换
如果逃逸分析发现一个对象不会被外部访问,并且该对象可以被拆散,那么经过优化之后,并不直接生成该对象,而是在栈上创建若干个成员变量
通过-XX:+EliminateAllocations可以开启标量替换, -XX:+PrintEliminateAllocations查看标量替换情况
栈上分配
故名思议就是在栈上分配对象,其实目前Hotspot并没有实现真正意义上的栈上分配,实际上是标量替换
垃圾回收
判断对象是否已死
引用计数算法
方法简单,但是两对象互相引用时会造成无法回收
可达性分析算法
通过一系列的“GC Roots”对象作为起点进行搜索,如果在“GC Roots”和一个对象之间没有可达路径,则称该对象是不可达的,不过要注意的是被判定为不可达的对象不一定就会成为可回收对象。被判定为不可达的对象要成为可回收对象必须至少经历两次标记过程,如果在这两次标记过程中仍然没有逃脱成为可回收对象的可能性,则基本上就真的成为可回收对象了。
可作为GC Root的对象包含四种
虚拟机栈(栈帧中的本地变量表)中引用的对象
方法区中静态属性引用的对象
方法区中常量引用的对象
本地方法栈中(Native方法)引用的对象
判断是否回收的步骤
分支主题
引用
强引用
强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误。
软引用
如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。
弱引用
弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。
虚引用
“虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。
选择垃圾回收算法
标记—清除算法
优点:实现简单,与其他算法配合简单
缺点:容易产生内存碎片,后续需要分配较大对象时可能造成内存空间不够,提前触发GC
复制算法
优点:实现简单,运行高效,不易产生内存碎片
缺点:内存空间浪费,如果存活对象很多,算法效率大打折扣
标记整理算法
该算法类似标记清除,但是标记完成后不直接清除,而是将存活的对象向一端移动,然后清理掉边界以外的内存
选择垃圾回收时间
安全点
在程序执行的时候并非所有时候都能停顿下来开始GC ,只有到达安全点才能暂停。安全点的选择标准:是否具有让程序长时间执行的特征,例如:方法调用,循环跳转,异常跳转等。具有这些功能的指令才会产生安全点SafePoint.
停止策略
抢先中断式(弃用)
当发生GC时让所有线程都停下来,如果有线程没有到安全点,就让该线程继续跑到安全点
主动式中断
当线程执行GC需要中断时,不需要对线程进行操作,而是设置一个标志位,各线程去轮询这个标志位。标志位和安全点在同一个位置,发现标志位为真是就将自己这个线程中断挂起
主动式中断,避免了抢占式:中断—启动—中断的过程
安全区
安全区是指一段代码中不会改变引用关系,在这段代码区域发生GC中断请求是安全的,当线程进入安全区域之后会首先标识自己,在安全区中随便发生GC请求,当线程离开安全区的时候,会先检查是否完成了根节点枚举(或者说整个GC过程),完成的线程才能离开,没完成的完成了才能离开该区域、
选择适当的垃圾清理器收集垃圾
垃圾收集器
老生代
Serial Old收集器
Serial Old收集器
Serial收集器的老年代版本
标记—整理算法
一般用于Client模式
Server模式下,1.5版本之前与Parallel Scavenge收集器搭配使用,1.5之后作为CMS的后备预案
Parallel Old收集器
Parallel Scavenge收集器的老年代版本
标记—整理算法
在注重吞吐量和CPU资源有限的场合,大多使用Parallel Scavenge和Parallel Old搭配使用
CMS收集器
最短回收停顿时间为目标
符合互联网站或者B/S系统的服务端上,重视服务的响应速度,希望系统停顿时间最短的应用
基于“标记—清除”算法实现的
CMS收集器的内存回收过程是与用户线程一起并发执行的
运作过程分为四个步骤
初始标记,“Stop The World”,只是标记一下GC Roots能直接关联到的对象,速度很快
并发标记,并发标记阶段就是进行GC RootsTracing的过程
重新标记,Stop The World”,是为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,但远比并发标记的时间短
并发清除(CMS concurrent sweep)
优点
并发收集、低停顿
缺点
CPU资源敏感,吞吐量低
无法处理浮动垃圾,可能出现“Concurrent Mode Failure”失败而导致另一次Full GC的产生。
新生代
Seral 收集器
单线程串行,STOP THE WORLD
一般用于Client模式
简单高效
复制算法
ParNew收集器
Seral收集器的多线程版本
单CPU下表现不如Seral
目前只有它能与CMS收集器配合工作
Parallel Scavenge收集器
吞吐量优先的收集器
复制算法,并行的多线程收集器
可以较为精确控制吞吐量
G1收集器
JDK9默认垃圾收集器
将整个Java堆划分为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔阂了,它们都是一部分(可以不连续)Region的集合。
优点
并行与并发:充分利用多CPU、多核环境下的硬件优势
分代收集:不需要其他收集器配合就能独立管理整个GC堆
空间整合:“标记—整理”算法实现的收集器,局部上基于“复制”算法不会产生内存空间碎片
可预测的停顿:能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒
运作过程分为四个步骤
初始标记:标记一下GC Roots能直接关联到的对象,需要停顿线程,但耗时很短
并发标记:是从GC Root开始对堆中对象进行可达性分析,找出存活的对象,这阶段耗时较长,但可与用户程序并发执行
最终标记:修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录
筛选回收:对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间来制定回收计划
组合
-XX:+UseSerialGC:年轻串行(Serial),老年串行(Serial Old)
-XX:+UseParNewGC:年轻并行(ParNew),老年串行(Serial Old)
-XX:+UseConcMarkSweepGC:年轻并行(ParNew),老年串行(CMS),备份(Serial Old)
-XX:+UseParallelGC:年轻并行吞吐(Parallel Scavenge),老年串行(Serial Old)
-XX:+UseParalledlOldGC:年轻并行吞吐(Parallel Scavenge),老年并行吞吐(Parallel Old)
方法区
用于存储已被虚拟机加载的类信息、常量、静态变量,如static修饰的变量加载类的时候就被加载到方法区中。
IO
AIO
BIO
1:1同步阻塞模型
M:N同步阻塞模型(使用线程池)
NIO
Channel
SocketChannel
负责连接传输
ServerSocketChannel
负责连接的监听(监听有新的连接接入)
Buffer
Selector
完成IO的多路复用,可以监控多个通道的IO情况,Selector与Channel是监控与被监控的关系
Channel通过register方法注册到Selector
可供监控的四种事件类型
可读:SelectionKey.OP_READ
可写:SelectionKey.OP_WRITE
连接:SelectionKey.OP_CONNECT
接收:SelectionKey.OP_ACCEPT
可选择通道必须是非阻塞并且继承了SelectableChannel
不一定所有的Channel都支持所有事件,可以通过validOps()方法查看通道支持哪些事件
反射
Class
getFields()
可以拿到所有公共属性(包括父类),返回一个不可排序并且没有固定顺序的数组
getDeclareFields()
可以拿到当前类所有属性,包括私有属性,但是无法拿到父类的属性,同样返回一个不可排序且没有固定顺序的数组,若要获取父类属性需要递归获取
对象
创建过程
子主题 1
Redis
持久化
RDB(Redis DataBase)
RDB其实就是把数据以快照的形式保存在磁盘上。
触发机制
save:会阻塞Redis服务器
bgSave:fork出一个子线程进行保存,不会阻塞主线程
fork详细操作
https://www.cnblogs.com/cjjjj/p/12748306.html
自动触发:bgSave模式,需要在redis.conf中配置
①save:这里是用来配置触发 Redis的 RDB 持久化条件,也就是什么时候将内存中的数据保存到硬盘。比如“save m n”。表示m秒内数据集存在n次修改时,自动触发bgsave。
②stop-writes-on-bgsave-error :默认值为yes。当启用了RDB且最后一次后台保存数据失败,Redis是否停止接收数据。这会让用户意识到数据没有正确持久化到磁盘上,否则没有人会注意到灾难(disaster)发生了。如果Redis重启了,那么又可以重新开始接收数据了
③rdbcompression ;默认值是yes。对于存储到磁盘中的快照,可以设置是否进行压缩存储。
④rdbchecksum :默认值是yes。在存储快照后,我们还可以让redis使用CRC64算法来进行数据校验,但是这样做会增加大约10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能。
⑤dbfilename :设置快照的文件名,默认是 dump.rdb
⑥dir:设置快照文件的存放路径,这个配置项一定是个目录,而不能是文件名。
特点
优势
RDB文件紧凑,全量备份,非常适合用于进行备份和灾难恢复。
生成RDB文件的时候,redis主进程会fork()一个子进程来处理所有保存工作,主进程不需要进行任何磁盘IO操作。
RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。
劣势
RDB快照是一次全量备份,存储的是内存数据的二进制序列化形式,存储上非常紧凑。当进行快照持久化时,会开启一个子进程专门负责快照持久化,子进程会拥有父进程的内存数据,父进程修改内存子进程不会反应出来,所以在快照持久化期间修改的数据不会被保存,可能丢失数据。
AOF(Append Only File)
redis会将每一个收到的写命令都通过write函数追加到文件中。通俗的理解就是日志记录。
AOF的方式也同时带来了另一个问题。持久化文件会变的越来越大。为了压缩aof的持久化文件。redis提供了bgrewriteaof命令。将内存中的数据以命令的方式保存到临时文件中,同时会fork出一条新进程来将文件重写。重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似。
触发机制
每修改同步always:同步持久化 每次发生数据变更会被立即记录到磁盘 性能较差但数据完整性比较好
每秒同步everysec:异步操作,每秒记录 如果一秒内宕机,有数据丢失
no:从不同步
特性
优点
AOF可以更好的保护数据不丢失,一般AOF会每隔1秒,通过一个后台线程执行一次fsync操作,最多丢失1秒钟的数据。
AOF日志文件没有任何磁盘寻址的开销,写入性能非常高,文件不容易破损。
AOF日志文件即使过大的时候,出现后台重写操作,也不会影响客户端的读写。
AOF日志文件的命令通过非常可读的方式进行记录,这个特性非常适合做灾难性的误删除的紧急恢复。比如某人不小心用flushall命令清空了所有数据,只要这个时候后台rewrite还没有发生,那么就可以立即拷贝AOF文件,将最后一条flushall命令给删了,然后再将该AOF文件放回去,就可以通过恢复机制,自动恢复所有数据
key删除策略
数据类型
String
字符串
操作
set key value nx(不存在时设置,只能新建),xx(存在的时候设置,只能更新)
mset,mget(批量设置,批量取出)
append(追加)
getrange(取出字符串的一部分,getrange key start end)
正反向索引
如针对hello world,getrange -5 -1即可取出world
setrange(覆盖字符串,setrange key start value),逐个覆盖,如果超出之前长度会自动扩充
srtlen,获取字符串长度
数值
incr
incrby
incrbyfloat
bitmap
setbit
bitcount
bitpos
bitop
list
操作
lpush key value*
rpush key value*
lpop key
rpop key
lrange key start end
lindex key index
lset key index value
lrem key count value
count代表移除几个value,正数从前到后,负数从后到前,0移除所有
linsert key BFORE|AFTER pivot value
如果有两个pivot,只能在第一个前后插入
llen key
ltrim key start end
去除start和end两边的元素
作用
栈
同向命令
队列
反向命令
数组
阻塞,单播队列FIFO
hash
操作
hset/hget key field value
hmset/hmget key field value field value
hkeys key
kvals key
hgetall key
hincrby/hincrbyfloat
set
特征
无序
去重
操作
sadd key member member
scard key
smembers key
srem key member member
sinter key key 交集/ sinterstore dest key key 交集并保存到dest
sunion key key / sunionstore dest key key 并集
sdiff first second 谁在前就是谁的外差集
srandmember key count
count为正,取出不重复元素,如果数量不足,则取已有集合所有
count为负,取出带重复元素的结果,一定会满足数量
count为0,不反悔
spop key count
sorted_set
分值排序,分值相同时按照首字母
物理内存左小右大 不随命令发生变化
跳跃表
元素被插入时,随机造层
操作
zadd key score member score member
zcard key
zcount key min max
zrange key start stop withscores
zrange key start end
zscore key member
zincrby k1 score member
zunionstore dest keycount key key weights weight weight aggregate sum/min/max
redis中的数据存储是二进制安全的
优化
自身优化
Redis数据类型的命令是和数据类型自身绑定的,redis执行命令前会用方法所属的类型和key的类型匹配,如果不匹配就报错,key里记录了value的type信息
key里还记录了encoding信息,可以通过object encoding key查看
内存优化
redisObject对象
分支主题
type字段:
表示当前对象使用的数据类型,Redis主要支持5种数据类型:string,hash,list,set,zset。可以使用type {key}命令查看对象所属类型,type命令返回的是值对象类型,键都是string类型。
encoding字段:
表示Redis内部编码类型,encoding在Redis内部使用,代表当前对象内部采用哪种数据结构实现。理解Redis内部编码方式对于优化内存非常重要 ,同一个对象采用不同的编码实现内存占用存在明显差异。
lru字段:
记录对象最后一次被访问的时间,当配置了 maxmemory和maxmemory-policy=volatile-lru | allkeys-lru 时, 用于辅助LRU算法删除键数据。可以使用object idletime {key}命令在不更新lru字段情况下查看当前键的空闲时间。
Tips:可以使用scan + object idletime 命令批量查询哪些键长时间未被访问,找出长时间不访问的键进行清理降低内存占用。
refcount字段:
记录当前对象被引用的次数,用于通过引用次数回收内存,当refcount=0时,可以安全回收当前对象空间。使用object refcount {key}获取当前对象引用。当对象为整数且范围在[0-9999]时,Redis可以使用共享对象的方式来节省内存。
*ptr字段:
与对象的数据内容相关,如果是整数直接存储数据,否则表示指向数据的指针。Redis在3.0之后对值对象是字符串且长度<=44字节的数据,内部编码为embstr类型,字符串sds和redisObject一起分配,从而只要一次内存操作。
高并发写入场景中,在条件允许的情况下建议字符串长度控制在44字节以内,减少创建redisObject内存分配次数从而提高性能。
缩减键值对象
降低Redis内存使用最直接的方式就是缩减键(key)和值(value)的长度。
key长度:如在设计键时,在完整描述业务情况下,键值越短越好。
value长度:值对象缩减比较复杂,常见需求是把业务对象序列化成二进制数组放入Redis。首先应该在业务上精简业务对象,去掉不必要的属性避免存储无效数据。其次在序列化工具选择上,应该选择更高效的序列化工具来降低字节数组大小。以JAVA为例,内置的序列化方式无论从速度还是压缩比都不尽如人意,这时可以选择更高效的序列化工具,如: protostuff,kryo等。
值对象除了存储二进制数据之外,通常还会使用通用格式存储数据比如:json,xml等作为字符串存储在Redis中。这种方式优点是方便调试和跨语言,但是同样的数据相比字节数组所需的空间更大,在内存紧张的情况下,可以使用通用压缩算法压缩json,xml后再存入Redis,从而降低内存占用,例如使用GZIP压缩后的json可降低约60%的空间。
当频繁压缩解压json等文本数据时,开发人员需要考虑压缩速度和计算开销成本,这里推荐使用google的Snappy压缩工具,在特定的压缩率情况下效率远远高于GZIP等传统压缩工具,且支持所有主流语言环境。
共享对象池
对象共享池指Redis内部维护[0-9999]的整数对象池。创建大量的整数类型redisObject存在内存开销,每个redisObject内部结构至少占16字节,甚至超过了整数自身空间消耗。所以Redis内存维护一个[0-9999]的整数对象池,用于节约内存。 除了整数值对象,其他类型如list,hash,set,zset内部元素也可以使用整数对象池。因此开发中在满足需求的前提下,尽量使用整数对象以节省内存。
共享对象池可以节约多少内存
相同的数据情况,且大小都在0-9999区间内,大约可以节约30%的内存
为什么开启maxmemory和LRU淘汰策略后对象池无效
LRU算法需要获取对象最后被访问时间,以便淘汰最长未访问数据,每个对象最后访问时间存储在redisObject对象的lru字段。对象共享意味着多个引用共享同一个redisObject,这时lru字段也会被共享,导致无法获取每个对象的最后访问时间。如果没有设置maxmemory,直到内存被用尽Redis也不会触发内存回收,所以共享对象池可以正常工作。
为什么共享对象池只有整数
整数对象池复用可能性最大
整数判断相等性成本最小
整数O(1),字符串O(n),list,hash等需要O(n^2),对于单线程的Redis来说,这样的开销显然不合理,因此Redis只保留整数共享对象池。
字符串优化
字符串结构
分支主题
O(1)时间复杂度获取:字符串长度,已用长度,未用长度。
可用于保存字节数组,支持安全的二进制数据存储。
内部实现空间预分配机制,降低内存再分配次数。
惰性删除机制,字符串缩减后的空间不释放,作为预分配空间保留。
预分配机制
1) 第一次创建len属性等于数据实际大小,free等于0,不做预分配。
2) 修改后如果已有free空间不够且数据小于1M,每次预分配一倍容量。如原有len=60byte,free=0,再追加60byte,预分配120byte,总占用空间:60byte+60byte+120byte+1byte。
3) 修改后如果已有free空间不够且数据大于1MB,每次预分配1MB数据。如原有len=30MB,free=0,当再追加100byte ,预分配1MB,总占用空间:1MB+100byte+1MB+1byte。
开发提示:尽量减少字符串频繁修改操作如append,setrange, 改为直接使用set修改字符串,降低预分配带来的内存浪费和内存碎片化。
字符串重构
指不一定把每份数据作为字符串整体存储,像json这样的数据可以使用hash结构,使用二级结构存储也能帮我们节省内存。同时可以使用hmget,hmset命令支持字段的部分读取修改,而不用每次整体存取。
编码优化
Redis对外提供了string,list,hash,set,zet等类型,但是Redis内部针对不同类型存在编码的概念,所谓编码就是具体使用哪种底层数据结构来实现。编码不同将直接影响数据的内存占用和读写效率。使用object encoding {key}命令获取编码类型。
string
raw
embstr
int
hash
hashtable
ziplist
list
linkedlist
ziplist
quicklist
set
hashtable
intset
zset
skiplist
ziplist
注册中心
zookeeper
简介
分布式应用程序的分布式协调服务,公开了一组简单的原语,同步,配置维护以及组和命名
特性
顺序一致性
原子性
可靠性
及时性
配置文件
tickTime:leader和follower之间心跳间隔,默认2000ms
initLimit:follower和leader建立链接时允许心跳失败次数,默认10
syncLimit:leader向follower同步数据时允许失败次数,默认5
dataDir:持久化目录
clientPort:客户端连接使用的端口号
maxClientCnxns:允许最大客户端连接数
客户端操作
ls:查看节点
get/set:对节点取值/赋值
cZxid:创建的事务id,16进制,共64个字节,低32位代表第几次操作,高32位代表第几个leader,客户端连接和断开也会造成该id增加,因为要保存和删除session
ctime:创建时间
mZxid:修改的事务id
mtime:修改时间
pZxid:该节点下最后一创建得节点的创建事务id
ephemeralOwner:当该节点为临时节点时,存储创建它的client对应的sessionId
create path data
-e:创建临时节点
-s:解决并发问题,在节点名后面加一个序号
rmr:删除节点
eureka
MySQL
事务
隔离级别
READ UNCOMMITED
READ COMMITED
REPEATABLE READ(默认)
SERIALIZABLE
特性(ACID)
原子性
持久性
隔离性
隔离性是要管理多个并发读写请求的访问顺序。 这种顺序包括串行或者是并行
隔离性是一场数据的可靠性与性能之间的权衡。
一致性
方案技术
redo log
redo log由两部分组成,重做日志缓冲(redo log buffer)以及重做日志文件(redo log),前者是在内存中,后者在磁盘中。当事务提交之后会把所有修改信息都会存到该日志中。
分支主题
mysql 为了提升性能不会把每次的修改都实时同步到磁盘,而是会先存到Boffer Pool(缓冲池)里头,把这个当作缓存来用。然后使用后台线程去做缓冲池和磁盘之间的同步。
redo log是用来恢复数据的,用于保障已提交事务的持久化特性
undo log
undo log 叫做回滚日志,用于记录数据被修改前的信息。他正好跟前面所说的重做日志所记录的相反,重做日志记录数据被修改后的信息。undo log主要记录的是数据的逻辑变化,为了在发生错误时回滚之前的操作,需要将之前的操作都记录下来,然后在发生错误时才可以回滚。
每条数据变更(insert/update/delete)操作都伴随一条undo log的生成,并且回滚日志必须先于数据持久化到磁盘上。
所谓的回滚就是根据回滚日志做逆向操作,比如delete的逆向操作为insert,insert的逆向操作为delete,update的逆向为update等。
undo log是用来回滚数据的用于保障 未提交事务的原子性
锁技术
共享锁(读锁)
select ... lock in share mode
排他锁(写锁)
select ... for update
record lock(记录锁)
用唯一索引做检索条件时,使用记录锁,只锁定匹配记录
Gap Locks(间隙锁)
Next-Key Locks
通过读写锁,可以做到读读可以并行,但是不能做到写读,写写并行
MVCC
在Mysql中MVCC是在Innodb存储引擎中得到支持的,Innodb为每行记录都实现了三个隐藏字段
6字节的事务ID(DB_TRX_ID)
用来标识该行所述的事务
7字节的回滚指针(DB_ROLL_PTR)
隐藏的ID
MVCC在mysql中的实现依赖的是undo log与read view
undo log中记录的是数据表记录行的多个版本,也就是事务执行过程中的回滚段,其实就是MVCC 中的一行原始数据的多个版本镜像数据。
read view 主要用来判断当前版本数据的可见性。
SELECT
只查找版本早于当前事务版本的数据行,也就是行的系统版本号小于或等于事务的系统版本号,这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或修改的。
行的删除版本要么未定义,要么大于当前事务版本号。这样可以确保事务读取到的行在事务开始之前未被删除。
INSERT
InnoDB为新插入的每一行保存当前系统版本号为行版本号
DELETE
InnoDB为删除的每一行保存当前系统版本号作为行的删除标示。
UPDATE
InnoDB为插入一行新纪录,保存当前系统版本号作为行版本号,同时保存当前系统版本号到原来的行作为行删除标示
SpringCloud
Feign
Feign的作用的替代RestTemplate,性能比较低,但是可以使代码可读性很强。
Ribbon
算法
排序
快速排序
堆排序
子主题 1
数据结构
数组
数组由于是紧凑连续存储,可以随机访问,通过索引快速找到对应元素,而且相对节约存储空间。但正因为连续存储,内存空间必须一次性分配够,所以说数组如果要扩容,需要重新分配一块更大的空间,再把数据全部复制过去,时间复杂度 O(N);而且你如果想在数组中间进行插入和删除,每次必须搬移后面的所有数据以保持连续,时间复杂度 O(N)。
链表
链表因为元素不连续,而是靠指针指向下一个元素的位置,所以不存在数组的扩容问题;如果知道某一元素的前驱和后驱,操作指针即可删除该元素或者插入新元素,时间复杂度 O(1)。但是正因为存储空间不连续,你无法根据一个索引算出对应元素的地址,所以不能随机访问;而且由于每个元素必须存储指向前后元素位置的指针,会消耗相对更多的储存空间。
堆
一般所说的堆都是二叉堆,堆是一颗完全二叉树
分布式事务
CAP理论
一致性(Consistency)
在分布式系统中所有数据备份,在同一时刻是否保持同样的值
可用性(Availability)
在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求
分区容忍性(Partition tolerance)
分区
在一个分布式系统中,节点本来应该是互相连通的。但是如果因为一些故障,节点之间断连,整个网络就划分成了几个区域,数据散布在这些不连通的区域中,就叫分区。
提高分区容忍性
将一个数据项复制到多个节点上,即使出现分区,每个分区都能读到该数据,容忍性就提高了
带来的问题
将数据复制到多节点就会带来一致性的问题,要保证一致性,每次写操作就要等待全部节点写成功,等待就会带来可用性的问题。数据存在的节点越多,分区容忍性就越高,但是每次写操作同步更新的数据越多,一致性就越难保证。要保证一致性,更新的时间就会延长,可用性就会降低
P是必须要保证的,只能在C和A之间做权衡
BASE理论
BASE理论是对CAP中的一致性和可用性做权衡的结果,理论的核心思想是通过牺牲一致性来达到高可用性。用最终一致性代替强一致性
两段式提交
准备阶段
协调者询问参与者事务是否执行成功,参与者发回事务执行结果。
提交阶段
如果事务在每个参与者上都执行成功,事务协调者发送通知让参与者提交事务;否则,协调者发送通知让参与者回滚事务。
缺点
同步阻塞 所有事务参与者在等待其它参与者响应的时候都处于同步阻塞状态,无法进行其它操作。
单点问题 协调者在 2PC 中起到非常大的作用,发生故障将会造成很大影响。特别是在阶段二发生故障,所有参与者会一直等待状态,无法完成其它操作。
数据不一致 在阶段二,如果协调者只发送了部分 Commit 消息,此时网络发生异常,那么只有部分参与者接收到 Commit 消息,也就是说只有部分参与者提交了事务,使得系统数据不一致。
太过保守 任意一个节点失败就会导致整个事务失败,没有完善的容错机制。
三段式提交
TCC
子主题 6
自由主题
0 条评论
下一页