1、JDK1.8的新特性有哪些

一、接口的默认方法

二、Lambda表达式

三、函数式接口

四、方法与构造函数引用

五、Lambda作用域

六、访问局部变量

七、访问对象字段于静态变量

八、访问接口的默认方法

2、Java抽象类和接口有什么区别

语法与语意的区别

什么时候用抽象类,什么时候用接口

抽象类 -> 有一种既有抽象概念(植物、动物)

接口 -> 共同特征(例如具有某种会飞的功能)

相同点:

  • 都不能被实例化
  • 接口的实现类和抽象类的子类都只有在实现了接口或抽象类中的方法以后才能被实例化

不同点:

  • 接口中的方法只有定义(JDK1.8中可以有default方法),抽象类的方法可以定义与实现

  • 关键字不同:implementextends,类可以实现多个接口,但只能继承一个类

  • 接口强调功能的实现,抽象类强调所属关系

  • 接口中的成员变量默认都是 public static final,必须初始化,不可被修改

    抽象类的成员变量默认是default,可以被重新定义,也可以被修改

  • 接口中的成员方法都是 public、abstract

    抽象方法都是要被 abstract修饰的不能被 private、synchronized、native等修饰,不带花括号

3、HashMap

(1)底层原理

https://zhuanlan.zhihu.com/p/79507868

(2)HashMap在扩容上做了哪些处理

JDK1.8不需要再像JDK1.7一样重新计算hash,只需要看原来的hash值新增的那一位bit是1还是0就好

(3)HashMap有哪些线程安全的方式

  • 方法一:

通过Collections.synchronizedMap()返回一个新的Map,这个新的Map就是线程安全的。这个要求大家习惯基于接口编程,因为返回的并不是HashMap,而是一个Map实现。

  • 方法二:

重新改写了HashMap,具体可以查看java.util.concurrentHashMap,是一个支持并发的HashMap。这个方法比方法一有了很大的改进。

4、HashMap和HashTable的区别

  • HashTable线程同步但效率低,HashMap相反但效率高

    线程同步:指的是多线程的时候数据的安全性,synchronized关键字

  • HashTable不允许<key, value>空值,HashMap允许

  • HashTable使用Enumeration,HashMap使用Iterator

  • HashTable(默认为11)扩容方式:2*old+1,HashMap(默认为16)扩容方式为:2的指数倍

  • HashTable继承自Dictionary类,HashMap继承自AbstractMap类

5、Comparetor和Comparable

在Java中当自定义的类需要进行比较的时候,可以通过这两种方式

Comparable是一个比较接口,一旦某个类实现该接口,就代表这个类“支持排序比较”,而Comparator是一个比较器,通过建立一个“该类的比较器”来实现排序

Comparable相当于“内部比较器”,而Comparator相当于“外部比较器”

Comparable实现简单,只需实现接口即可进行比较,但是需要修改源码,但是Comparetor好处是不需要修改源码,只是实现一个比较器,将需要比较的两个对象传禁区就行

6、线程池的实现原理

https://juejin.cn/post/6866054685044768782

线程池的核心参数:

  • corePoolSize:线程池核心线程数量
  • maximumPoolSize:线程池会创建的最大线程数量
  • keepAliveTime:线程池中大于核心线程数那部分线程的最大空闲存活时间
  • workQueue:保存等待执行任务的一个阻塞队列,当线程池所有的线程都在运行的时候,提交的任务会保存在阻塞队列中
  • threadFactory:创建线程的一个工厂,默认为DefaultThreadFactory
  • handler:线程拒绝策略

用多线程只有一个目的,那就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现。说这个话其实只有一半对,因为反应“多角色”的程序代码,最起码每个角色要给他一个线程吧,否则连实际场景都无法模拟,当然也没法说能用单线程来实现:比如最常见的“生产者,消费者模型”。

1、线程实现的方式

  • 继承Thread类
  • 实现Runnable接口
  • 使用Callable和Future

2、Thread中的start()run()有什么区别

start()方法用来启动线程,真正实现了多线程,无需等待run方法执行完毕

run()当作普通方法的方式调用,程序顺序执行,要等run方法执行完毕后才能继续执行下面的方法

3、线程的状态

  • NEW状态

    new了一个Thread对象,并没有启动该线程

  • RUNNABLE状态

    线程对象通过start()方法进入runnable状态,启动的线程不一定会立即得到执行,线程的运行与否要看cpu的调度,我们把这个中间状态叫可执行状态(RUNNABLE)

  • RUNNING状态

    一旦cpu通过轮询货其他方式从任务可以执行队列中选中了线程,此时它才能真正的执行自己的逻辑代码

  • BLOCKED状态

    线程正在等待获取锁

    调用了wait()或者sleep()、进行了某个阻塞IO操作、获得了某个资源的锁

  • TERMINATED状态

    TERMINATED是一个线程的最终状态

    线程运行正常结束、线程运行出错意外结束、JVM Crash 导致所有的线程都结束

线程状态转换

4、Java解决线程安全的四种方式

乐观锁与悲观锁

悲观锁,顾名思义它是悲观的。讲得通俗点就是,认为自己在使用数据的时候,一定有别的线程来修改数据,因此在获取数据的时候先加锁,确保数据不会被线程修改。形象理解就是总觉得有刁民想害朕。

而乐观锁就比较乐观了,认为在使用数据时,不会有别的线程来修改数据,就不会加锁,只是在更新数据的时候去判断之前有没有别的线程来更新了数据。具体用法在下面讲解。

方法一:使用synchronized关键字

一个表现为原生语法层面的互斥锁,它是一种悲观锁,使用它的时候我们一般需要一个监听对象 并且监听对象必须是唯一的,通常就是当前类的字节码对象。它是JVM级别的,不会造成死锁的情况。使用synchronized可以拿来修饰类,静态方法,普通方法和代码块。比如:Hashtable类就是使用synchronized来修饰方法的

方法二:Lock接口下的实现类

方法三:使用线程本地存储ThreadLocal

方法四:使用乐观锁机制

5、高并发中的集合有哪些问题

  • 第一代线程安全集合类

Vector、Hashtable

使用synchronized修饰方法,效率低下

  • 第二代线程非安全集合类

ArrayList、HashMap

线程不安全但是性能好

  • 第三代线程安全集合类

在大量并发情况下如何提高集合的效率和安全呢

Java容器、Java的发展历史

1、Mybatis的优缺点

(1)优点:

  • 1、简单易学,容易上手,基于SQL编程
  • 2、与JDBC相比,减少了代码量,消除了大量冗余代码,不需要手动开关闭
  • 3、与各种的数据库兼容(使用JDBC来连接数据库)
  • 4、提供了很多第三方插件(分页插件/逆向工程:反向生成实体类)
  • 5、与Spring很好的集成
  • 6、相当灵活SQL写在XML文件里,与代码相分离解耦,便于管理与优化,还可以重用
  • 7、提供XML标签,支持编写动态SQL语句
  • 8、提供映射标签,支持对象与数据库的ORM字段关系映射
  • 9、提供对象映射标签,支持对象关系组建维护

(2)缺点

  • 1、SQL语句的编写量巨大
  • 2、SQL语句依赖数据库,导致数据库移植性差,不能随意更换数据库

2、Mybatis中#{}${}区别

  • #{}是预编译处理,${}是字符串替换
  • Mybatis在处理#{}时,会将#{}替换为?,调用PreparedStatementset方法来赋值
  • Mybatis在处理¥{}时,直接将¥{}替换成变量的值
  • 前者可以有效防止SQL注入

一、BeanFactory和ApplicationContext的区别

https://juejin.cn/post/6844903877574131726

二、Spring AOP

Spring AOP 扫盲

1、什么是Spring AOP

面向切面编程(Aspect-oriented Programming,俗称AOP)提供了一种面向对象编程(Object-oriented Programming,俗称OOP)的补充

面向对象编程最核心的单元是类(class),然而面向切面编程最核心的单元是切面(Aspects)

与面向对象的顺序流程不同,AOP采用的是横向切面的方式,注入与主业务流程无关的功能,例如事务管理和日志管理

AOP 是一种编程范式,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术

2、相关概念

  • 切面(Aspect)
  • 连接点(Join Point)
  • 通知(Advice)
  • 切入点(Pointcut)
  • 介绍(Introduction)
  • 目标对象(Target Object)
  • AOP代理(AOP proxy)
  • 织入(Weaving)

3、通知分类

  • 前置通知(Before Advice)

    在目标方法被调用前调用通知功能;相关的类org.springframework.aop.MethodBeforeAdvice

  • 后置通知(After Advice)

    在目标方法被调用之后调用通知功能;相关的类org.springframework.aop.AfterReturningAdvice

  • 返回通知(After-returning)

    在目标方法成功执行之后调用通知功能;

  • 异常通知(After-throwing)

    在目标方法抛出异常之后调用通知功能;相关的类org.springframework.aop.ThrowsAdvice

  • 环绕通知(Around)

    把整个目标方法包裹起来,在被调用前和调用之后分别调用通知功能相关的类

4、织入的三种时期

  • 编译期: 切面在目标类编译时被织入,这种方式需要特殊的编译器。AspectJ 的织入编译器就是以这种方式织入切面的。
  • 类加载期: 切面在目标类加载到 JVM 时被织入,这种方式需要特殊的类加载器( ClassLoader ),它可以在目标类引入应用之前增强目标类的字节码。
  • 运行期: 切面在应用运行的某个时期被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态创建一个代理对象,Spring AOP 采用的就是这种织入方式。

5、两种实现方式

静态织入(AspectJ)和动态织入(动态代理)

(1)静态织入(AspectJ)

(2)动态织入(动态代理)

Spring AOP 是通过动态代理技术实现的,而动态代理是基于反射设计的

Spring AOP 采用了两种混合的实现方式:JDK 动态代理和 CGLib 动态代理

一、页面置换算法

进程运行时,若其访问的页面不在内存而需将其调入,但内存已无空闲空间时,就需要从内存中调出一页程序或数据,送入磁盘的对换区

1、最佳置换算法(OPT)

该算法选择淘汰的页面是以后不会使用的,或者是最长时间内不再被访问的页面,以此来保证最低的缺页率

由于无法预知,所以该算法无法实现

2、先进先出(FIFO)

优先淘汰最早进入内存的页面

将掉入内存的页面按照先后次序排成一个队列,设置一个指针指向总指向最早的界面

有些页面会经常被访问,所以不适用

3、最近最久未使用(LRU)

选择最近最长时间未访问过的页面予以淘汰,它认为过去一段时间内未访问过的页面,在最近的将来可能也不会被访问

为每个页面设置一个访问字段记录自从上次访问以来所经历的时间,淘汰页面时选择访问字段值最大的一个淘汰

性能比较好,但是需要寄存器和栈的支持,实现起来开销比较大

4、时钟(CLOCK)

简单的CLOCK算法是给每一帧关联一个附加位,称为使用位

当某一页首次装入主存时,该帧的使用位设置为1;当该页随后再被访问到时,它的使用位也被置为1

每当需要淘汰页面时就寻找使用位为0的页面,并将经过的页面置为0

二、进程、线程、协程

https://www.cnblogs.com/fanguangdexiaoyuer/p/10834737.html

1、三者的概念

(1)进程

进程是一个具有一定独立功能的程序在一个数据集上的一次动态执行的过程,是操作系统进行资源分配和调度的一个独立单位,是应用程序运行的载体

进程一般有三部分组成:程序、数据集合和进程控制块

  • 程序描述了进程所要完成的功能,是程序执行的指令集
  • 数据集合是程序在执行时候所需的数据和工作区
  • 进程控制块(PCB)描述了进程的描述信息和控制信息,是进程的唯一标识

进程的特征

  • 动态性:进程是一次执行的过程,是有生命期的,动态产生和动态消亡的
  • 并发性:任何进程都可以和其他进程一起并发执行的
  • 独立性:进程是系统进行资源调度和分配的独立单位
  • 结构性:进程由程序、数据和PCB组成

(2)线程

线程是能够拥有资源和独立运行的最小单位,也是程序执行的最小单位

线程是程序执行过程中一个单一的顺序控制流程,是程序执行流中的最小单元

(3)协程

基于线程之上,但是比线程更加轻量级的存在,这种由程序猿自己写程序来管理的轻量级线程叫做“用户空间线程”,对内核来说不可见

是程序猿自己开辟的异步任务

2、通信方式的差异

(1)进程的通信

  • 管道(Pipe)

    匿名管道、有名管道

  • 信号(Signal)

  • 信号量(Semaphore)

    本质是一个计数器

  • 共享内存(Shared Memory)

  • 消息队列(Message Queue)

  • 套接字(Socket)

(2)线程的通信

  • 锁机制:包括互斥锁、条件变量、读写锁

    wait/notify 等待机制

    volatile 共享内存

    countDownLatch 并发工具

    cyclicBarrier 并发工具

  • 信号量机制(Semaphore)

  • 信号机制(Signal)

3、死锁问题

(1)什么是死锁?

死锁是指两个或以上进程在执行过程中,由于资源竞争造成的一种阻塞现象,若无外力作用,将一直阻塞下去。我们称这种现象为系统产生了死锁或者系统处于死锁状态

(2)产生死锁的原因有哪些?

  • 系统资源不足
  • 程序执行顺序不当
  • 资源分配不当

(3)产生死锁的必要条件

  • 互斥条件

    一个资源只能被一个进程使用

  • 请求与保持条件

    请求资源阻塞时,对已获得的资源并不释放

  • 不剥夺条件

    进程已获得的资源,除非自己释放,否则不能被剥夺

  • 循环等待条件

    进程之间形成一种头尾相接的资源等待关系

(4)如何解决死锁?

在系统设计、进程调度等方面注意不要让四个必要条件成立,确定合理的资源分配算法、防止进程处于等待状态下占用资源

4、操作系统四大特性

https://bbs.huaweicloud.com/blogs/323535

https://zhuanlan.zhihu.com/p/106120802

现代操作系统都具有并发、共享、虚拟和异步的特性,其中并发是其它三个特征的前提

共享和并发是操作系统的两个最基本的特征,虚拟以并发和共享为前提,异步是并发和共享的必然结果

(1)并发

并发是指宏观上在一段时间内能同时运行多个程序,而并行则指同一时刻能运行多个指令。并发需要硬件支持,如多流水线或者多处理器

  • 并发性是指两个或多个事件在同一时间间隔内发生;操作系统通过引入进程和线程,使得程序能够并发运行。在多道程序环境下,并发性是指在一段时间内宏观上有多个程序同时运行,但在单处理机系统中,每一时刻只能由一道程序执行,所以微观上程序是分时地交替执行的。
  • 并行性是指两个或多个事件在同一时刻发生

(2)共享

共享是指系统中的资源可以被多个并发进程共同使用

有两种共享方式:互斥共享同时共享

(3)虚拟

虚拟技术把一个物理实体转换为多个逻辑实体

主要有两种虚拟技术:时分复用技术空分复用技术

例如多进程在同一处理器上并发执行使用了时分复用技术

(4)异步

异步指进程不是一次性执行完毕,而是走走停停,以不可知的速度向前推进

产生原因:操作系统允许多个并发进程共享资源,使得每个进程的运行过程受到其他进程制约,使进程的执行不是一气呵成,而是以停停走走的方式运行

有了并发性才会有异步性

1、ACID

(1)ACID是靠什么来保证的

ACID(数据库事务正确执行的四个基本要素的缩写)

原子性、一致性、隔离性、持久性

  • 原子性

undolog

  • 隔离性

MVVC

  • 持久性

redolog

  • 一致性

由其他三个特性来保证的

2、MVVC

(1)MVCC解决的问题到底是啥

MVCC:多版本并发控制(常常用在数据库里的技术)

数据库并发场景有三种:

  • 读读

    不存在任何问题,无需并发控制

  • 读写

    有线程安全,可能会造成事务的隔离性问题,可能遇到脏读、幻读、不可重复读

  • 写写

    有线程安全问题,可能存在更新丢失问题

脏读(读取未提交数据):脏读又称无效数据的读出,是指在数据库访问中,事务T1将某一值修改,然后事务T2读取该值,此后T1因为某种原因撤销对该值的修改,这就导致了T2所读取到的数据是无效的,值得注意的是,脏读一般是针对于update操作的。

幻读(前后多次读取,数据总量不一致):事务A在执行读取操作,需要两次统计数据的总量,前一次查询数据总量后,此时事务B执行了新增数据的操作并提交后,这个时候事务A读取的数据总量和之前统计的不一样,就像产生了幻觉一样,平白无故的多了几条数据,成为幻读。

不可重复读(前后多次读取,数据内容不一致):事务A在执行读取操作,由整个事务A比较大,前后读取同一条数据需要经历很长的时间 。而在事务A第一次读取数据,比如此时读取了小明的年龄为20岁,事务B执行更改操作,将小明的年龄更改为30岁,此时事务A第二次读取到小明的年龄时,发现其年龄是30岁,和之前的数据不一样了,也就是数据不重复了,系统不可以读取到重复的数据,成为不可重复读。

不可重复读和幻读到底有什么区别呢?

(1) 不可重复读是读取了其他事务更改的数据,针对update操作

解决:使用行级锁,锁定该行,事务A多次读取操作完成后才释放该锁,这个时候才允许其他事务更改刚才的数据。

(2) 幻读是读取了其他事务新增的数据,针对insert和delete操作

解决:使用表级锁,锁定整张表,事务A多次读取数据总量之后才释放该锁,这个时候才允许其他事务新增数据。

MVCC为事务分配单项增长的时间戳,为每个修改保存一个版本,版本与事务时间戳关联,读操作只读事务开始前的数据库快照,所以MVCC可以为数据解决以下问题:

​ 1、在并发读写数据库时,可以在读操作时不阻塞写操作,写操作也不阻塞读操作,提高了数据库并发读写的能力

​ 2、解决脏读、幻读、不可重复读等事务隔离问题,但是不能解决更新丢失问题

(2)MVCC实现原理

3、MySQL的隔离级别有哪些?

https://www.cnblogs.com/fengzheng/p/12557762.html

首先我们来看看隔离级别是个玩意

有四种隔离级别

  1. 读未提交(READ UNCOMMITTED)

    所有事务都可以看到未提交事务的执行结果

  2. 读提交 (READ COMMITTED)

    一个事务开始时,只能看到已经提交事务所做的改变

  3. 可重复读 (REPEATABLE READ)

    ???

  4. 串行化 (SERIALIZABLE)

    通过强制事务排序

隔离级别 脏读 不可重复读 幻读
读未提交 可能 可能 可能
读提交 不可能 可能 可能
可重复读 不可能 不可能 可能
串行化 不可能 不可能 不可能

4、MySQL索引

https://www.cnblogs.com/zsql/p/13808417.html

(1)为什么要有索引?

一般的应用系统中,读写比例为10:1,而且插入和一般的更新操作很少出现性能问题

生产环境中,我们遇到最多的也是最容易出问题的,是一些复杂的查询操作

索引就是为了进行高速查询

(2)什么是索引?

索引在MySQL中也叫一种“键”,是存储引擎用于快速找到记录的一种数据结构

索引对于良好的性能非常关键,尤其是当表的数据量愈发巨大时,索引对于性能的影响愈发巨大

索引就相当于字典的音序表

(3)索引的原理

(4)索引的数据结构

MySQL主要用到两种结构:B+Tree索引和Hash索引

Inodb存储引擎默认是B+Tree索引

Memory存储引擎默认是Hash索引

Hash索引

(5)聚簇和非聚簇索引的区别

索引存在于磁盘,MySQL的索引类型与存储引擎是相关的,innobd存储引擎数据文件和索引文件全都放在ibd文件中,而myisam的数据文件全都放在myd文件中,索引放在myi文件中,判断条件:数据和索引是否是分开的

(6)索引分类

按数据结构分类可分为:B+tree索引、Hash索引、Full-text索引
按物理存储分类可分为:聚簇索引、二级索引(辅助索引)
按字段特性分类可分为:主键索引、普通索引、前缀索引
按字段个数分类可分为:单列索引、联合索引(复合索引、组合索引)

5、数据库事务、主键和外键的区别

  • 事务即用户定义的一个数据库操作序列,这些操作要么全做要全不做,是一个不可分割的工作单位,它具有四个特性,即ACID:原子性、一致性、隔离性、持续性
  • 主键是能确定一条记录的唯一标识
  • 外键用于与另一张表的关联,是能确定另一张表记录的字段,用于保持数据的一致性

6、简单介绍having和where的区别

  • 用的地方不一样

    where可以用于selectupdatedeleteinsert into values(select * from table where …)语句中

    having只能用于select语句中

  • 执行的顺序不一样

    where的搜索条件是在执行语句进行分组之前应用

    having的搜索条件是在分组条件后执行的,即如果where和having一起用时,where会先执行,having后执行

  • 子句有区别

    where子句中的条件表达式having都可以跟

    having子句中的有些表达式where不可以跟,例如集合函数(sum、count、avg、max和min)

总之,WHERE 子句用来筛选 FROM 子句中指定的操作所产生的行。GROUP BY 子句用来分组 WHERE 子句的输出。HAVING 子句用来从分组的结果中筛选行

7、SQL优化技巧

SQL优化技巧

MySQL优化的五个原则:

  • 减少数据的访问

    压缩、索引等手段减少磁盘IO

  • 返回更少的数据

    只返回需要的字段和数据分页处理,减少磁盘 IO 及网络 IO

  • 减少交互次数

    批量 DML 操作,函数存储等减少数据连接次数

  • 减少服务器 CPU 开销

    减少数据库排序操作以及全表查询,减少 CPU 内存占用

  • 利用更多资源

    使用表分区,可以增加并行操作,更大限度利用 CPU 资源

总结到SQL:

  • 最大化利用索引
  • 尽可能避免全表扫描
  • 减少无效数据的查询

(1)避免不走索引的场景

  • 避免在字段开头模糊查询,会导致数据库放弃索引全表扫描

    如:SELECT * FROM t WHERE username LIKE '%陈%'

    改为:SELECT * FROM t WHERE username LIKE '陈%'

  • 尽量避免使用 in 和 not in,会导致引擎走全表扫描

  • 如果是子查询,可以用 exists 代替

    如:select * from A where A.id in (select id from B);

    改为:select * from A where exists (select * from B where B.id = A.id);

  • 尽量避免使用 or,会导致数据库引擎放弃索引进行全表扫描

    可以用 union 代替 or,如下:

    SELECT * FROM t WHERE id = 1 OR id = 3改为

    SELECT * FROM t WHERE id = 1 UNION SELECT * FROM t WHERE id = 3

  • 尽量避免进行 null 值的判断,会导致数据库引擎放弃索引进行全表扫描

    如:SELECT * FROM t WHERE score IS NULL

    改为0值判断:SELECT * FROM t WHERE score = 0

  • 查询条件不能用 <> 或者 !=

    使用索引列作为条件进行查询时,需要避免使用<>或者!=等判断条件

  • order by 条件要与 where 中条件一致,否则 order by 不会利用索引进行排序

    如:SELECT * FROM t order by age;

    改为:SELECT * FROM t where age > 0 order by age;

8、联合索引和单独建立索引的区别

1、Redis为什么这么快?

https://juejin.cn/post/6978280894704386079

  • 基于内存实现省去了磁盘IO的消耗

  • 底层实现了高效的数据结构

  • 合理的数据编码

  • 合理的线程模型

    单线程(避免了上下文切换和竞争锁的切换):是指Redis网络IO和键值对读写是由一个线程来完成的,其他诸如持久化、异步删除、集群数据同步由额外的线程执行

  • I/O多路复用

    I/O:网络IO

    多路:多个网络连接

    复用:复用同于一个线程

    是一种同步的IO模型,实现一个线程可以监视多个文件句柄

  • 虚拟内存机制

2、Redis的数据类型有哪些,底层怎么实现?

  • 字符串:整数值、embstr编码的简单动态字符串、简单动态字符串(SDS)
  • 列表:压缩列表、双端队列
  • 哈希:压缩列表、字典
  • 集合:整数集合、字典
  • 有序集和:压缩列表、跳跃表、字典

3、Redis是单线程的,但是为什么这么高效呢?

虽然Redis文件事件处理器以单线程方式运行,但是通过使用I/O多路复用程序来监听多个套接字

文件事件处理器既实现了高性能的网络通信模型,又可以很好地与Redis服务器中其他同样以单线程运行的模块进行对接,这保持了Redis内部单线程设计的简单性

4、如何保证Redis的高可用

  • 单点故障:

    实际生产中,如果 redis 只部署一个节点,当机器故障时,整改服务都不能提供服务了

    部署了多台,一台故障时,整个系统依然对外提供服务,这样就提高了服务的可用性

redis 高可用的三种模式:主从模式、哨兵模式、集群模式

(1)主从模式

优点:

  • 支持主从复制,主机会自动将数据同步到从机,可以进行读写分离
  • 为了分载 Master 的读操作压力,Slave 服务器可以为客户端提供只读操作的服务,写服务依然必须由 Master 来完成
  • Slave 同样可以接受其他 Slaves 的连接和同步请求,这样可以有效地分载 Master 的同步压力
  • Master 是以非阻塞的方式为 Slaves 提供服务。所以在 Master-Slave 同步期间,客户端仍然可以提交查询或修改请求
  • Slave 同样是以阻塞的方式完成数据同步。在同步期间,如果有客户端提交查询请求,Redis 则返回同步之前的数据

缺点:

  • Redis 不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的 IP 才能恢复
  • 主机宕机,宕机前有部分数据未能及时同步到从机,切换 IP 后还会引入数据不一致的问题,降低了系统的可用性
  • 如果多个 Slave 断线了,需要重启的时候,尽量不要在同一时间段进行重启。因为只要 Slave 启动,就会发送 sync 请求和主机全量同步,当多个 Slave 重启的时候,可能会导致 Master IO 剧增从而宕机
  • Redis 较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂
  • Redis 的主节点和从节点中的数据是一样的,降低的内存的可用性

(2)哨兵模式

(3)集群模式

一、堆与栈

1、栈溢出Stack Overflow

(1)什么是栈溢出

栈溢出指的是程序向栈中某个变量中写入的字节数超过了这个变量本身所申请的字节数,因而导致栈中与其相邻的变量的值被改变

(2)栈溢出的原因

  • 局部数组过大

    局部变量是在栈中分配的

  • 递归调用层次太多

    递归函数在执行的时候会进行压栈操作

  • 指针或者数组越界

    例如字符串拷贝的时候