java面试题

java基础

java 有哪些数据类型?

String、StringBuffer和StringBuilder的区别

String

大小固定,不可变

StringBuffer

大小可变,线程安全(有锁),同步,效率低,适用于多线程,低并发

StringBuilder

大小可变,线程不安全(无锁),不同步,效率高,适用于单线程,高并发

== 和 equals 的区别是什么?

==     比较的是::引用是否指向同一块内存

euqals比较的是:: 引用指向内存中的

Java 中的 Math. round(-1. 5) 等于多少

等于 -1,因为在数轴上取值时,中间值(0.5)向右取整,所以正 0.5 是往上取整,负 0.5 是直接舍弃。

多线程

创建线程有哪几种方式

创建线程有三种方式:

继承 Thread 重写 run 方法;

实现 Runnable 接口;

实现 Callable 接口。

runnable 和 callable 有什么区别

runnable 没有返回值,callable 可以拿到有返回值,callable 可以看作是 runnable 的补充

线程有哪些状态

NEW 尚未启动

RUNNABLE 正在执行中

BLOCKED 阻塞的(被同步锁或者IO锁阻塞)

WAITING 永久等待状态

TIMED_WAITING 等待指定的时间重新被唤醒的状态

TERMINATED 执行完成

sleep() 和 wait() 有什么区别

类的不同:sleep() 来自 Thread,wait() 来自 Object。

释放锁:sleep() 不释放锁;wait() 释放锁

用法不同:sleep() 时间到会自动恢复;wait() 可以使用 notify()/notifyAll()直接唤醒

创建线程池有哪几种方式

线程池创建有七种方式,最核心的是最后一种:

newSingleThreadExecutor():它的特点在于工作线程数目被限制为 1,操作一个无界的工作队列,所以它保证了所有任务的都是被顺序执行,最多会有一个任务处于活动状态,并且不允许使用者改动线程池实例,因此可以避免其改变线程数目;

newCachedThreadPool():它是一种用来处理大量短时间工作任务的线程池,具有几个鲜明特点:它会试图缓存线程并重用,当无缓存线程可用时,就会创建新的工作线程;如果线程闲置的时间超过 60 秒,则被终止并移出缓存;长时间闲置时,这种线程池,不会消耗什么资源。其内部使用 SynchronousQueue 作为工作队列;

newFixedThreadPool(int nThreads):重用指定数目(nThreads)的线程,其背后使用的是无界的工作队列,任何时候最多有 nThreads 个工作线程是活动的。这意味着,如果任务数量超过了活动队列数目,将在工作队列中等待空闲线程出现;如果有工作线程退出,将会有新的工作线程被创建,以补足指定的数目 nThreads;

newSingleThreadScheduledExecutor():创建单线程池,返回 ScheduledExecutorService,可以进行定时或周期性的工作调度;

newScheduledThreadPool(int corePoolSize):和newSingleThreadScheduledExecutor()类似,创建的是个 ScheduledExecutorService,可以进行定时或周期性的工作调度,区别在于单一工作线程还是多个工作线程;

newWorkStealingPool(int parallelism):这是一个经常被人忽略的线程池,Java 8 才加入这个创建方法,其内部会构建ForkJoinPool,利用Work-Stealing算法,并行地处理任务,不保证处理顺序;

ThreadPoolExecutor():是最原始的线程池创建,上面1-3创建方式都是对ThreadPoolExecutor的封装。

线程池都有哪些状态

RUNNING:这是最正常的状态,接受新的任务,处理等待队列中的任务。

SHUTDOWN:不接受新的任务提交,但是会继续处理等待队列中的任务。

STOP:不接受新的任务提交,不再处理等待队列中的任务,中断正在执行任务的线程。

TIDYING:所有的任务都销毁了,workCount 为 0,线程池的状态在转换为 TIDYING 状态时,会执行钩子方法 terminated()。

TERMINATED:terminated()方法结束后,线程池的状态就会变成这个。

在 Java 程序中怎么保证多线程的运行安全

方法一:使用安全类,比如 Java. util. concurrent 下的类。

方法二:使用自动锁 synchronized。

方法三:使用手动锁 Lock。

synchronized 和 Lock 有什么区别

synchronized

lock

可以给类、方法、代码块加锁

只能给代码块加锁

不需要手动获取锁和释放锁,使用简单,发生异常会自动释放锁,不会造成死锁

需要自己加锁和释放锁,如果使用不当没有 unLock()去释放锁就会造成死锁

无法知道有没有成功获取锁

通过 Lock 可以知道有没有成功获取锁

异常

常见的异常有哪些

NullPointerException 空指针异常

ArrayIndexOutOfBoundsException 索引越界异常

InputFormatException 输入类型不匹配

SQLException SQL异常

IllegalArgumentException 非法参数

NumberFormatException 类型转换异常

ClassNotFoundException 指定类不存在

IndexOutOfBoundsException 数组下标越界异常

ClassCastException 数据类型转换异常

FileNotFoundException 文件未找到异常

NoSuchMethodException 方法不存在异常

IOException IO 异常

SocketException Socket 异常

这些类以Throwable为顶层父类。又派生出Error类和Exception类。

异常要怎么解决

try()catch(){}    或  Throw  或  throws

容器

Java 容器都有哪些?

Java 容器分为 Collection 和 Map 两大类,其下又有很多子类,如下所示:

Collection

List

ArrayList; LinkedList; Vector; Stack

Set

HashSet;  LinkedHashSet;  TreeSet

Map

HashMap

LinkedHashMap

TreeMap

ConcurrentHashMap

Hashtable

哪些集合类是线程安全的

线程安全

Vector、Hashtable、Stack,

ConcurrentHashMap

非线程安全

Hashmap、ArraylistLinkedList
HashSet、TreeSet、TreeMap

Vector:   只要是关键性的操作,方法前面都加了synchronized关键字,来保证线程的安全性

Hashtable:使用了synchronized关键字,所以相较于Hashmap是线程安全的。

ConcurrentHashMap:使用锁分段技术确保线性安全,是一种高效但是线程安全的集合。

Stack:     栈,也是线程安全的,继承于Vector。

Collection 和 Collections 有什么区别

Collection 是一个集合接口,它提供了对集合对象进行基本操作的通用接口方法,所有集合都是它的子类,比如 List、Set 等。

Collections 是一个包装类,包含了很多静态方法,不能被实例化,就像一个工具类,比如提供的排序方法:Collections. sort(list)。

List、Set、Map 之间的区别是什么

元素有序

允许元素重复

List

Set

AbstractSet

HashSet

TreeSet

是(用二叉树排序)

Map

AbstractMap

Key 值必须唯一,value可重复

HashMap

TreeMap

是(用二叉树排序)

HashMap 和 Hashtable 有什么区别

HashMap

Hashtable

存储

Key和value 可为null

 Key和value 都不能是null

线程安全

非线程安全的

线程安全的

使用

在单线程环境下使用 HashMap ,多线程下使用 ConcurrentHashMap

HashMap 的实现原理

HashMap 基于 Hash 算法实现的。当传入 key 时,HashMap 会根据 key. hashCode() 计算出 hash 值,根据 hash 值将 value 保存在 bucket 里。当计算出的 hash 值相同时,我们称之为 hash 冲突,HashMap 的做法是用链表和红黑树存储相同 hash 值的 value。当 hash 冲突的个数比较少时,使用链表否则使用 红黑树(一种自平衡二叉搜索树

Hashmap存储结构

初始化:默认定义加载因子为0.75

当我们第一次添加KV键值对时,如果数组此时为空,则会默认扩容为16。
加入元素时,如果链表长度大于阈值(默认为8)并且数组长度小于6,就会产生数组扩容。
添加元素后,当HashMap中的元素个数超过【数组大小×加载因子】时,原数组扩容2倍。例如:加载因子的默认值为0.75,数组容量默认为16,当HashMap中的元素个数超过16×0.75=12时,数组容量扩容16×2=32。
HashMap加入新元素时,如果链表长度大于8时,会尝试将当前链表转换为红黑树。在转换红黑树之前,会判断数组长度,如果小于64,会产生数组扩容。如果数组长度大于64,才会将链表转换为红黑树。

(简单理解如下:

->kv  : 默认扩容=16

->kv  : if  连表长度> 8 && 数组长度<16 => 扩容成原来的两倍

       else if 元素个数>【数组大小×加载因子】&& 数组长度<6 => 扩容成原来的两倍

         else if  连表长度> 8 && 数组长度>64 => 连表转成红黑树

)

HashSet 的实现原理

HashSet 是基于 HashMap 实现的,HashSet 底层使用 HashMap 来保存所有元素,因此 HashSet 的实现比较简单。

相关 HashSet 的操作,基本上都是直接调用底层 HashMap 的相关方法来完成,HashSet 不允许重复的值

ArrayList 和 LinkedList 的区别是什么

数据结构实现:ArrayList 是动态数组的数据结构实现,而 LinkedList 是双向链表的数据结构实现。

随机访问效率:ArrayList 比 LinkedList 在随机访问的时候效率要高,因为 LinkedList 是线性的数据存储方式,所以需要移动指针从前往后依次查找。

增加和删除效率:在非首尾的增加和删除操作,LinkedList 要比 ArrayList 效率要高,因为 ArrayList 增删操作要影响数组内的其他数据的下标。

综合来说,在需要频繁读取集合中的元素时,更推荐使用 ArrayList,而在插入和删除操作较多时,更推荐使用 LinkedList

如何实现数组和 List 之间的转换

数组转 List:使用 Arrays. asList(array) 进行转换。

List 转数组:使用 List 自带的 toArray() 方法。

    怎么确保一个集合不能被修改

可以使用 Collections. unmodifiableCollection(Collection c) 方法来创建一个只读集合,这样改变集合的任何操作都会抛出 Java. lang. UnsupportedOperationException 异常。

序列化

什么是 Java 序列化?什么情况下需要序列化

 Java序列化是指将Java对象转换为字节流的过程,以便在网络传输或持久化存储时使用。

 以下情况需要使用 Java 序列化:

想把的内存中的对象状态保存到一个文件中或者数据库中时候;

想用套接字在网络上传送对象的时候;

想通过RMI(远程方法调用)传输对象的时候。

代理

动态代理是什么?有哪些应用?

它能够在运行时动态地生成代理类,实现对目标对象的代理访问和控制。

应用:

  1. 实现AOP编程(事务管理、性能监控,日志管理)
  2. 实现远程方法调用 (常用于实现分布式系统和微服务架构)
  3. 实现IOC容器(动态代理可以在运行时动态生成对象,并将其注入到IOC容器中,常用于实现Spring等框架的IOC功能。)
怎么实现动态代理

JDK 原生动态代理和 cglib 动态代理。JDK 原生动态代理是基于接口(InvocationHandler)实现的,而 cglib 是基于继承当前类的子类实现的(实现MethodInterceptor )。

使用:对于singleton的代理对象或者具有实例池的代理,因为无需频繁的创建代理对象,所以比较适合采用CGLib动态代理,反正,则比较适用JDK动态代理。

srpingboot框架

Springboot 的启动流程

1:启动类 上使用@springbootApplication 注解(包含了@springbootConfiguration,@EnableAutoConfiguration,@ComponentScan)。Springboot通过扫描包和子包,自动装配配置类和bean定义。

2:加载配置文件:springboot启动程序默认从application.properties(yml) 中加载配置属性,

也可以通过在启动类上使用@PropertySource注解指定其他的属性文件。

3:创建springboot容器,springboot使用springApplication类创建spring容器。SrpingApplication是spring boot的核心类,他提供了各种配置和管理spring应用程序的方法。Springapplication会创建一个嵌入式的tomcat或jetty服务器或者使用外部的tomcat或jetty服务器,

4:加载自动配置 springboot通过@EnableAutoConfiguration注解和spring-boot-autoconfigure项目提供了大量的自动配置

根据classpath中的jar包和bean的装配情况,自动装配响应的bean。

5:运行springboot应用程序,当spring容器准备就绪后,springboot就会启动嵌入式的web服务器并运行应用程序。如果使用的是外部的web服务器,springboot就会将应用程序打包成一个可执行的jar文件,并启动外部服务器

spring是如何简化开发的。

1:简化了配置和管理,spring使用依赖注入来管理对象之间的依赖关系,减少了手动编写和管理配置文件的复杂性。同时,spring框架提供了大量的API和工具,以帮助开发人员轻松管理应用程序的配置和管理。

2:提高了应用程序的可测试性,spring支持面向接口编程,这使得应用程序中的各个组件更加松耦合,这种松散耦合的设计使得开发人员能够更容易地编写测试单元。

3:提高了应用程序的可维护性,Spring的模块化设计使得应用程序中的各个组件更容易维护。每个模块都有一个明确的责任这使得开发人员能够更容易地了解和维护应用程序中的各个组件

四,提供了大量的集成支持,Spring框架提供了大量的集成支持包括与Hibernate、MyBatis等ORM框架的集成,以及与各种消息队列、搜索引擎和云服务的集成。这使得开发人员可以更容易地将应用程序集成到现有的技术栈中。

Srping  bean的生命周期

1: 实例化:Spring容器根据配置文件或注解创建一个Bean定义,这个定义描述了Bean的类,依赖关系等。然后容器使用Java反射机制创建一个Bean的实例

2: 属性赋值:Spring容器将在Bean实例化后,通过调用Settel方法等方式来将属性赋值给Bean。这个过程可以通过XML配置或注解来实现。

3 初始化:在Bean的属性赋值完成后Spring容器会调用Bean的初始化方法,这个方法可以是自定义的,需要在Bean的配置文件或注解中进行定义。

4:使用:Bean初始化完成后,就可以使用了。这个时候Bean已经被完全构建并准备好被其他组件使用。

5: 销毁:当Bean不再需要使用时,Spring容器会调用Bean的销毁方法,这不方法可以是自定义的,需要在Bean的配置义件或注解中进行定义。

Spring ioc的理解

是指 控制反转。好处

1:降低组件之间的耦合度,传统的开发方式中,对象之间的依赖关系是由对象自己进行管理的,这样会导致组件之间的耦合度非常高。而使用Spring lOC容器来管理对象之间的依赖关系,则能够将组件之间的耦合度降到最低。

2:提高代码的重用性,SpringlOC容器能够自动管理对象之间依赖关系。这样就能够将一些通用的组件抽象出来,从而提高代码的重用性。

3:简化代码的编写,在传统的开发方式中,对象之间的依赖关系需要开发人员自己进行管理,这样会导致代码量非常庞大。而使用SpringlOC容器,则能够将对象之间的依赖关系交给容器来管理,从而简化了代码的编写。

4、提高系统的灵活性,使用Springl0C容器可以将对象之间的依赖关系动态地配置到配置文件中,这样就能够在不修改源代码的情况下,修改对象之间的依赖关系,从而提高系统的灵活性。

spring ioc 往里面注入一个类用哪个注解,@Autowired 与@Resource的区别

@Autowired: Spring提供的注解 采取的策略为按照类型注入 @Qualifier告诉spring具体去装配哪个对象

@Resource 由J2EE提供 默认按照ByName自动注入

aop

sql

关系型和非关系型数据库的区别

关系型数据库的优点

  • 容易理解,因为它采用了关系模型来组织数据。
  • 可以保持数据的一致性。
  • 数据更新的开销比较小。
  • 支持复杂查询(带 where 子句的查询)

非关系型数据库(NOSQL)的优点

  • 无需经过 SQL 层的解析,读写效率高。
  • 基于键值对,读写性能很高,易于扩展
  • 可以支持多种类型数据的存储,如图片,文档等等。

扩展: 关系型数据库有 mysql,oracle。非关系型数据库: mongoDB,Redis, oracle nosql, HBase

-----  存储引擎 ------------->

MySQL 存储引擎介绍

  • InnoDB  : 是事务型数据库的首选引擎,支持事务安全表 (ACID),支持行锁定和外键。MySQL5.5.5 之后,InnoDB 作为默认存储引擎
  • MyISAM  : 基于 ISAM 的存储引擎,并对其进行扩展。它是在 Web、数据存储和其他应用环境下最常用的存储引擎之一。MyISAM 拥有较高的插入、查询速度,但不支持事务。在 MySQL5.5.5 之前的版本中,MyISAM 是默认存储引擎
  • MEMORY  : 存储引擎将表中的数据存储到内存中,为查询和引用其他表数据提供快速访问

MyISAM 和 InnoDB 实现 B 树索引方式的区别是什么

  • InnoDB 存储引擎:B+ 树索引的叶子节点保存数据本身,其数据文件本身就是索引文件。
  • MyISAM 存储引擎:B+ 树索引的叶子节点保存数据的物理地址,叶节点的 data 域存放的是数据记录的地址,索引文件和数据文件是分离的

InnoDB 为什么设计 B+ 树索引

哈希索引虽然能提供O(1)复杂度查询,但对范围查询和排序却无法很好的支持,最终会导致全表扫描。

B 树能够在非叶子节点存储数据,但会导致在查询连续数据可能带来更多的随机 IO。

B+ 树的所有叶节点可以通过指针来相互连接,减少顺序遍历带来的随机 IO

-------  索引 ------------->

mysql的几种索引

普通索引 、主键索引、唯一索引 、全文索引、 组合索引

索引的底层实现原理

索引 的底层实现原理主要依赖于B树和B+树数据结构

扩展: 在所有的叶子结点中增加了指向下一个叶子节点的指针,因此 InnoDB 建议为大部分表使用默认自增的主键作为主索引

哪些操作会导致索引失效

  • 对索引使用左或者左右模糊匹配,也就是 like %xx 或者 like %xx% 这两种方式都会造成索引失效。原因在于查询的结果可能是多个,不知道从哪个索引值开始比较,于是就只能通过全表扫描的方式来查询。
  • 对索引进行函数/对索引进行表达式计算,因为索引保持的是索引字段的原始值,而不是经过函数计算的值,自然就没办法走索引。
  • 对索引进行隐式转换相当于使用了新函数。(如 varchar 不加单引号的话可能会自动转换为 int 型)
  • WHERE 子句中的 OR语句,只要有条件列不是索引列,就会进行全表扫描

-----sql---->

MySQL 语句执行的步骤

Server 层按顺序执行 SQL 的步骤为:

  • 客户端请求 -> 连接器(验证用户身份,给予权限)
  • 查询缓存(存在缓存则直接返回,不存在则执行后续操作)
  • 分析器(对 SQL 进行词法分析和语法分析操作)
  • 优化器(主要对执行的 SQL 优化选择最优的执行方案方法)
  • 执行器(执行时会先看用户是否有执行权限,有才去使用这个引擎提供的接口)-> 去引擎层获取数据返回(如果开启查询缓存则会缓存查询结果)

-----锁---->

MySQL 中有哪几种锁

1)表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最 高,并发度最低。

(2)行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最 低,并发度也最高。

(3)页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表 锁和行锁之间,并发度一般。

-----事务---->

SQL 标准定义的四个隔离级别为:

隔离级别

脏读(Dirty Read)

不可重复读(NonRepeatable Read)

幻读(Phantom Read)

未提交读(Read uncommitted)

可能

可能

可能

已提交读(Read committed)

不可能

可能

可能

可重复读(Repeatable read)

不可能

不可能

可能

可串行化(SERIALIZABLE)

不可能

不可能

不可能

MySQL 支持事务吗

在缺省模式下,MySQL 是 autocommit 模式的,所有的数据库更新操作都会即时提交,所以在缺省情况下,MySQL 是不支持事务的。

短时间提高 MySQL 性能的方法

  • 第一种方法:先处理掉那些占着连接但是不工作的线程。或者再考虑断开事务内空闲太久的连接。 kill connection + id
  • 第二种方法:减少连接过程的消耗:慢查询性能问题在 MySQL 中,会引发性能问题的慢查询,大体有以下三种可能:索引没有设计好;SQL 语句没写好;MySQL 选错了索引(force index)

--- 优化 -->

MySQL 数据库作发布系统的存储,一天五万条以上的增量,预计运维三年,怎么优化?

找规律分表,减少单表中的数据量提高查询速度。

MySQL 库主从读写分离。

设计良好的数据库结构,允许部分数据冗余,尽量避免 join 查询,提高效率。

选择合适的表字段数据类型和存储引擎,适当的添加索引。

添加缓存机制,比如 memcached,apc 等。

sql语句优化

应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引

应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描

应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描

应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描

like '%abc%'

应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描

不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引

索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率

任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段

尽量避免使用游标,因为游标的效率较差

-- 其他-->

MySQL 中有哪些不同的表格

共有 5 种类型的表格:

(1)MyISAM

(2)Heap

(3)Merge

(4)INNODB

(5)ISAM

redis

什么是Redis

Redis本质上是一个Key-Value类型的内存数据库,整个数据库统统加载在内存当中进行操作,定期通过 异步操作 把数据库数据 flush到硬盘上 进行保存。

优点:性能高(每秒可以处理超过 10万次读写操作)支持保存多种数据结构,此外单个value的最大限制是1GB

作用:List来做FIFO双向链表,实现一个轻量级的高性 能消息队列服务;Set可以做高性能的tag系统。

缺点:数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,

适合的场景:主要局限在较小数据量的高性能操作和运算上。

Redis支持的Java客户端都有哪些?官方推荐用哪个?

Redisson、Jedis、lettuce等等,官方推荐使用Redisson。

Redis支持哪几种数据类型

String、List、Set(无序集合,没有重复元素)、Sorted Set(集合中每个元素关联一个分数(score),根据分数升序排序)、hashes

一个字符串类型的值能存储最大容量是多少

512M

Redis中的数据状态

redis数据可以通过TTL指令获取其状态,TTL返回的值有三种情况:正数,-1,-2。

  • 正数:代表该数据在内存中还能存活的时间
  • -1:永久有效的数据
  • 2 :已经过期的数据 或被删除的数据 或 未定义的数据

时效性数据的存储结构

过期数据是一块独立的存储空间,Hash结构,field是内存地址,value是过期时间,保存了所有key的过期描述,在最终进行过期处理的时候,对该空间的数据进行检测, 当时间到期之后通过field找到内存该地址处的数据,然后进行相关操作。

已过期的数据是真的就立即删除了吗?

其实也不是,我们会有多种删除策略,是分情况的,在不同的场景下使用不同的删除方式会有不同效果

数据删除策略

定时删除:创建一个定时器,当key设置有过期时间,且过期时间到达时,由定时器任务立即执行对键的删除操作

节约内存,无占用; 不分时段占用CPU资源,频度高; 拿时间换空间

惰性删除:数据到达过期时间,不做处理。等下次访问该数据时,我们需要判断

内存占用严重; 延时执行,CPU利用率高  ;拿空间换时间

定期删除:定期删除就是周期性轮询redis库中的时效性数据,采用随机抽取的策略,利用过期数据占比的方式控制删除频度

内存定期随机清理;  每秒花费固定的CPU资源维护内存; 随机抽查,重点抽查

Redis有哪几种数据淘汰策略?

淘汰策略主要是指当内存达到最大配置时(maxmemory),Redis如何选择哪些数据淘汰以释放内存。

  1. noeviction: 不进行淘汰,当内存不足时,新写入操作会报错。

  2. allkeys-random: 在内存达到最大限制时,随机淘汰键。

  3. volatile-random: 在使用了expire的键中,随机淘汰。

  4. allkeys-lru: 在内存达到最大限制时,基于最少最近使用算法(LRU)淘汰键。

  5. volatile-lru: 在使用了expire的键中,基于最少最近使用算法(LRU)淘汰键。

  6. allkeys-lfu: 在内存达到最大限制时,基于最少频繁使用(LFU)算法淘汰键。

  7. volatile-lfu: 在使用了expire的键中,基于最少频繁使用(LFU)算法淘汰键。

修改配置不重启Redis会实时生效吗?

如为升级 Redis 程序到新的版本,或者当你需要修改某些目前 CONFIG 命令还不支持的配置参数的时候,重新启动是必须的。

Redis集群之间是如何复制的

异步复制

Redis集群最大节点个数是多少?

16384个

Redis集群会有写操作丢失吗?为什么?

Redis并不能保证数据的强一致性,这意味这在实际中集群在特定的条件下可能会丢失写操作。

Redis常见性能问题和解决方案

(1) Master最好不要做任何持久化工作,如RDB内存快照和AOF日志文件

(2) 如果数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一次

 (3) 为了主从复制的速度和连接的稳定性,Master和Slave最好在同一个局域网内

(4) 尽量避免在压力很大的主库上增加从库

 (5) 主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3...这样的结构方便解决单点故障问题,实现Slave对Master的替换。如果Master挂了,可以立刻启用Slave1做Master,其他不变。

Redis提供了哪几种持久化方式?

RDB持久化方式能够在指定的时间间隔能对你的数据进行快照存储.

AOF持久化方式记录每次对服务器写的操作

Redis持久化数据和缓存怎么做扩容?

(描述的不太好)

如果Redis被当做缓存使用,使用一致性哈希实现动态扩容缩容

如果Redis被当做一个持久化存储使用,必须使用固定的keys-to-nodes映射关系,节点的数量一旦确定不能变化。否则的话(即Redis节点需要动态变化的情况),必须使用可以在运行时进行数据再平衡的一套系统,而当前只有Redis集群可以做到这样。

缓存预热 (摘抄)

场景:“宕机”

服务器启动后迅速宕机

问题排查

1.请求数量较高,大量的请求过来之后都需要去从缓存中获取数据,但是缓存中又没有,此时从数据库中查找数据然后将数据再存入缓存,造成了短期内对redis的高强度操作从而导致问题

2.主从之间数据吞吐量较大,数据同步操作频度较高

解决方案:

  • 前置准备工作:

1.日常例行统计数据访问记录,统计访问频度较高的热点数据

2.利用LRU数据删除策略,构建数据留存队列例如:storm与kafka配合

  • 准备工作:

1.将统计结果中的数据分类,根据级别,redis优先加载级别较高的热点数据

2.利用分布式多服务器同时进行数据读取,提速数据加载过程

3.热点数据主从同时预热

  • 实施:

4.使用脚本程序固定触发数据预热过程

5.如果条件允许,使用了CDN(内容分发网络),效果会更好

总的来说缓存预热就是系统启动前,提前将相关的缓存数据直接加载到缓存系统。避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!

缓存雪崩(摘抄)

场景:数据库服务器崩溃,一连串的场景会随之儿来

1.系统平稳运行过程中,忽然数据库连接量激增

2.应用服务器无法及时处理请求

3.大量408,500错误页面出现

4.客户反复刷新页面获取数据

5.数据库崩溃

6.应用服务器崩溃

7.重启应用服务器无效

8.Redis服务器崩溃

9.Redis集群崩溃

10.重启数据库后再次被瞬间流量放倒

问题排查

1.在一个较短的时间内,缓存中较多的key集中过期

2.此周期内请求访问过期的数据,redis未命中,redis向数据库获取数据

3.数据库同时接收到大量的请求无法及时处理

4.Redis大量请求被积压,开始出现超时现象

5.数据库流量激增,数据库崩溃

6.重启后仍然面对缓存中无数据可用

7.Redis服务器资源被严重占用,Redis服务器崩溃

8.Redis集群呈现崩塌,集群瓦解

9.应用服务器无法及时得到数据响应请求,来自客户端的请求数量越来越多,应用服务器崩溃

10.应用服务器,redis,数据库全部重启,效果不理想

总而言之就两点:短时间范围内,大量key集中过期

解决方案

  • 思路:

1.更多的页面静态化处理

2.构建多级缓存架构

​ Nginx缓存+redis缓存+ehcache缓存

3.检测Mysql严重耗时业务进行优化

​ 对数据库的瓶颈排查:例如超时查询、耗时较高事务等

4.灾难预警机制

​ 监控redis服务器性能指标

  • ​ CPU占用、CPU使用率
  • ​ 内存容量
  • ​ 查询平均响应时间
  • ​ 线程数

5.限流、降级

短时间范围内牺牲一些客户体验,限制一部分请求访问,降低应用服务器压力,待业务低速运转后再逐步放开访问

  • 落地实践:

1.LRU与LFU切换

2.数据有效期策略调整

  • ​ 根据业务数据有效期进行分类错峰,A类90分钟,B类80分钟,C类70分钟
  • ​ 过期时间使用固定时间+随机值的形式,稀释集中到期的key的数量

3.超热数据使用永久key

4.定期维护(自动+人工)

  • ​ 对即将过期数据做访问量分析,确认是否延时,配合访问量统计,做热点数据的延时

5.加锁:慎用!

总的来说缓存雪崩就是瞬间过期数据量太大,导致对数据库服务器造成压力。如能够有效避免过期时间集中,可以有效解决雪崩现象的 出现(约40%),配合其他策略一起使用,并监控服务器的运行数据,根据运行记录做快速调整。

缓存击穿(摘抄)

场景:还是数据库服务器崩溃,但是跟之前的场景有点不太一样

1.系统平稳运行过程中

2.数据库连接量瞬间激增

3.Redis服务器无大量key过期

4.Redis内存平稳,无波动

5.Redis服务器CPU正常

6.数据库崩溃

问题排查:

1.Redis中某个key过期,该key访问量巨大

2.多个数据请求从服务器直接压到Redis后,均未命中

3.Redis在短时间内发起了大量对数据库中同一数据的访问

总而言之就两点:单个key高热数据,key过期

解决方案

1.预先设定

​ 以电商为例,每个商家根据店铺等级,指定若干款主打商品,在购物节期间,加大此类信息key的过期时长 注意:购物节不仅仅指当天,以及后续若干天,访问峰值呈现逐渐降低的趋势

2.现场调整

​ 监控访问量,对自然流量激增的数据延长过期时间或设置为永久性key

3.后台刷新数据

​ 启动定时任务,高峰期来临之前,刷新数据有效期,确保不丢失

4.二级缓存

​ 设置不同的失效时间,保障不会被同时淘汰就行

5.加锁

​ 分布式锁,防止被击穿,但是要注意也是性能瓶颈,慎重!

总的来说:缓存击穿就是单个高热数据过期的瞬间,数据访问量较大,未命中redis后,发起了大量对同一数据的数据库访问,导致对数 据库服务器造成压力。应对策略应该在业务数据分析与预防方面进行,配合运行监控测试与即时调整策略,毕竟单个key的过 期监控难度较高,配合雪崩处理策略即可。

 缓存穿透(摘抄)

场景:数据库服务器又崩溃了,跟之前的一样吗?

1.系统平稳运行过程中

2.应用服务器流量随时间增量较大

3.Redis服务器命中率随时间逐步降低

4.Redis内存平稳,内存无压力

5.Redis服务器CPU占用激增

6.数据库服务器压力激增

7.数据库崩溃

问题排查:

1.Redis中大面积出现未命中

2.出现非正常URL访问

问题分析

  • 获取的数据在数据库中也不存在,数据库查询未得到对应数据
  • Redis获取到null数据未进行持久化,直接返回
  • 下次此类数据到达重复上述过程
  • 出现黑客攻击服务器

解决方案

1.缓存null

​ 对查询结果为null的数据进行缓存(长期使用,定期清理),设定短时限,例如30-60秒,最高5分钟

2.白名单策略

​ 提前预热各种分类数据id对应的bitmaps,id作为bitmaps的offset,相当于设置了数据白名单。当加载正常数据时放行,加载异常数据时直接拦截(效率偏低)

​ 使用布隆过滤器(有关布隆过滤器的命中问题对当前状况可以忽略)

2.实施监控

​ 实时监控redis命中率(业务正常范围时,通常会有一个波动值)与null数据的占比

  • ​ 非活动时段波动:通常检测3-5倍,超过5倍纳入重点排查对象
  • ​ 活动时段波动:通常检测10-50倍,超过50倍纳入重点排查对象

​ 根据倍数不同,启动不同的排查流程。然后使用黑名单进行防控(运营)

总的来说:缓存击穿是指访问了不存在的数据,跳过了合法数据的redis数据缓存阶段,每次访问数据库,导致对数据库服务器造成压力。通常此类数据的出现量是一个较低的值,当出现此类情况以毒攻毒,并及时报警。应对策略应该在临时预案防范方面多做文章。

无论是黑名单还是白名单,都是对整体系统的压力,警报解除后尽快移除。

----其他----

MySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据?

redis内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略。

为什么Redis需要把所有数据放到内存中

如果不将数据放在内存中,磁盘I/O速度为严重影响redis的性能。

Redis 除了做缓存,还能做什么?

  • 分布式锁:通过 Redis 来做分布式锁是一种比较常见的方式。通常情况下,我们都是基于 Redisson 来实现分布式锁。
  • 限流:一般是通过 Redis + Lua 脚本的方式来实现限流。如果不想自己写 Lua 脚本的话,也可以直接利用 Redisson 中的 RRateLimiter 来实现分布式限流,其底层实现就是基于 Lua 代码+令牌桶算法。
  • 消息队列:Redis 自带的 List 数据结构可以作为一个简单的队列使用。Redis 5.0 中增加的 Stream 类型的数据结构更加适合用来做消息队列。它比较类似于 Kafka,有主题和消费组的概念,支持消息持久化以及 ACK 机制。
  • 延时队列:Redisson 内置了延时队列(基于 Sorted Set 实现的)。
  • 分布式 Session :利用 String 或者 Hash 数据类型保存 Session 数据,所有的服务器都可以访问。
  • 复杂业务场景:通过 Redis 以及 Redis 扩展(比如 Redisson)提供的数据结构,我们可以很方便地完成很多复杂的业务场景比如通过 Bitmap 统计活跃用户、通过 Sorted Set 维护排行榜。

activeMQ

activemq 的几种通信方式

publish(发布)-subscribe(订阅)(发布-订阅方式)
p2p(point-to-point)(点对点)

activemq 如果数据提交不成功怎么办(消息丢失)

​​​​​​​publish(发布)-subscribe(订阅)方式的处理
发布订阅模式的通信方式, 默认情况下只通知一次, 如果接收不到此消息就没有了。 这种场景只适用于对消息送达率要求不高的情况。 如果要求消息必须送达不可以丢失的话, 需要配置持久订阅。 每个订阅端定义一个 id,<property name=“clientId” 在订阅是向 activemq 注册。 发布消息
和接收消息时需要配置发送模式为持久化
template.setDeliveryMode(DeliveryMode. PERSISTENT );。 此时如果客户
端接收不到消息, 消息会持久化到服务端(就是硬盘上), 直到客户端正常接收后为止。
p - p(点对点)方式的处理
点对点模式的话, 如果消息发送不成功此消息默认会保到 activemq 服
务端直到有消费者将其消费, 所以此时消息是不会丢失的。

如何解决消息重复问题

一般来说我们可以在业务段加一张表,用来存放消息是否执行成功,每次业务事物
commit 之后,告知服务端,已经处理过该消息,
这样即使你消息重发了,也不会导致重复处理

activeMQ 发送消息的方式有哪些?

消息通信的基本方式有两种:
1、同步方式
2、异步方式

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值