常见java面试题

JAVA基础

1. 多个线程同时读写,读线程的数量远远⼤于写线程,你认为应该如何解决 并发的问题?你会选择加什么样的锁?

 (ReentrantReadWriteLock)读写锁最适用于对数据结构的读操作次数多于写操作的场合,因为,读模式锁定时可以共享,而写模式锁定时只能某个线程独占资源(解释为读模式可以共享 所以不用创建线程 多个线程共享个资源,  二写模式由单个线程独占,所以要写更多的内容则需要创建更多的线程),因而,读写锁也可以叫做个共享-独占锁。

2. JAVA的AQS是否了解,它是⼲嘛的?

AbstractQueuedSynchronizer

用大白话来说,AQS就是基于CLH(一个虚拟的双向队列,仅存在节点之间的关联关系)队列,用volatile修饰共享变量state,线程通过CAS去改变状态符,成功则获取锁成功,失败则进入等待队列,等待被唤醒。

3. 除了synchronized关键字之外,你是怎么来保障线程安全的?

Lock volatile final cas aqs   

4. 什么时候需要加volatile关键字?它能保证线程安全吗?

当并发线程考虑线程安全问题 volatile只能修饰变量  直接读取主内存中的数据, 写也直接写主内存

5. 线程池内的线程如果全部忙,提交⼀个新的任务,会发⽣什么?队列全部 塞满了之后,还是忙,再提交会发⽣什么?

将任务存进任务队列中 .         ThreadPoolExecutor's中的submit()方法会抛出一个RejectedExecutionException异常

6. Tomcat本身的参数你⼀般会怎么调整?

Tomcat服务器优化(内存,并发连接数,缓存)

a) 内存优化:主要是对Tomcat启动参数进行优化,我们可以在Tomcat启动脚本中修改它的最大内存数等等。

b) 线程数优化:Tomcat的并发连接参数,主要在Tomcat配置文件中server.xml中配置,比如修改最小空闲连接线程数,用于提高系统处理性能等等。

c) 优化缓存:打开压缩功能,修改参数,比如压缩的输出内容大小默认为2KB,可以适当的修改。

7. synchronized关键字锁住的是什么东⻄?在字节码中是怎么表示的?在内存中的对象上表现为什么?

对象,monitor.enter()   monitor.exit()         标记对象头信息

8.synchronized和lock的区别?

(1)Lock是java的一个interface接口,而synchronized是Java中的关键字,synchronized是由JDK实现的,不需要程序员编写代码去控制加锁和释放;

(2)synchronized修饰的代码在执行异常时,jdk会自动释放线程占有的锁,不需要程序员去控制释放锁,因此不会导致死锁现象发生;但是,当Lock发生异常时,如果程序没有通过unLock()去释放锁,则很可能造成死锁现象,因此Lock一般都是在finally块中释放锁;

(3)Lock可以让等待锁的线程响应中断处理,如tryLock(long time, TimeUnit unit),而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够中断,程序员无法控制;

(4)通过Lock可以知道有没有成功获取锁,tryLock()方法返回boolean值,因此可知道是否获得了锁,而synchronized却无法办到。

(5)Lock的实现类ReentrantReadWriteLock提供了readLock()和writeLock()用来获取读锁和写锁的两个方法,这样多个线程可以进行同时读操作。

总体来讲,当并发量较小,资源竞争不激烈时,两者的性能是差不多的;当大量线程同时竞争,资源非常有限时,此时Lock的性能要远远优于synchronized。

 

9. wait/notify/notifyAll⽅法需不需要被包含在synchronized块中?这是为什 么?

wait() 方法执行默认释放对象锁  所以为了同步 需要在synchronized块中

10. ExecutorService你⼀般是怎么⽤的?是每个service放⼀个还是⼀个项⽬ ⾥⾯放⼀个?有什么好处?

     最好不要按项目来放,按不同的任务类型来放置最好。有的任务执行的时间长,有的任务执行的时间短,如果都使用一个线程池的话,执行任务时间长的容易阻塞住线程,导致其他执行时间短的任务无法获取到线程

11.Jdk代理和CGLIB代理的区别?

JDK动态代理只能对实现了接口的类生成代理,而不能针对类

CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法(继承)

12.CountDownLatch和CyclicBarrier的区别

CountDownLatchCyclicBarrier
减计数方式加计数方式
计数为0时,无法重置计数达到指定值时,计数置为0重新开始
不可重复利用可重复利用

13.ThreadLocal是什么?

简单来说就是当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

14.spring作为单例模式的典范,是如何避免线程安全问题呢?

15.CopyOnWrite?

顾名思义,CopyOnWrite就是在write操作之前,对集合进行Copy,针对容器的任意改操作(add,set,remove之类),都是在容器的副本上进行的。并且在修改完之后,将原容器的引用指向修改后的副本。
如果线程A得到容器list1的iterator之后,线程B对容器list1加入了新的元素,由于线程A获得list1的iterator时候在线程B对list1进行修改前,所以线程A是看不到线程B对list1进行的任何修改。

应用场景:经常用在读多写少的场景,比如EventListener的添加,网站的category列表等偶尔修改,但是需要大量读取的情景。

缺点
1.数据一致性的问题。  
  因为读操作没有用到并发控制,所以可能某个线程读到的数据不是实时数据。
2.内存占用问题。
  因为写操作会进行数据拷贝,并且旧有的数据引用也可能被其他线程占有一段时间,这样针对数据比较大的情况,可能会占用相当大的内存。并且由于每次写操作都会占用额外的内存,最后进行的GC时间也可能相应的增加。

16.jdk提供的队列?

在Java的并发包中已经提供了BlockingQueue的实现,比较常用的有ArrayBlockingQueue和LinkedBlockingQueue,前者是以数组的形式存储,后者是以Node节点的链表形式存储。

相同点:ArrayBlockingQueue和LinkedBlockingQueue都是通过condition通知机制来实现可阻塞式插入和删除元素,并满足线程安全的特性;

不同点:1. ArrayBlockingQueue底层是采用的数组进行实现,而LinkedBlockingQueue则是采用链表数据结构;

                 2.ArrayBlockingQueue插入和删除数据,只采用了一个lock,而LinkedBlockingQueue则是在插入和删除分别采用了putLocktakeLock,这样可以降低线程由于线程无法获取到lock而进入WAITING状态的可能性,从而提高了线程并发执行的效率。 

17.jdk提供的线程池? 

1.newCachedThreadPool创建一个可复用线程池程,这类线程池比较适合执行大量的耗时较少的任务,当整个线程池都处于闲置状态时,线程池中的线程都会超时被停止

2.newFixedThreadPool 创建一个定长线程池,由于newFixedThreadPool只有核心线程并且这些核心线程不会被回收,这样它更加快速底相应外界的请求。

3.newScheduledThreadPool 创建一个可以调度线程池

4.newSingleThreadExecutor 创建一个单线程化的线程池,它确保所有的任务都在同一个线程中按顺序中执行

18.线程池的好处?

1.重用线程池的线程,避免因为线程的创建和销毁锁带来的性能开销

2.有效控制线程池的最大并发数,避免大量的线程之间因抢占系统资源而阻塞

3.能够对线程进行简单的管理,并提供一下特定的操作如:可以提供定时、定期、单线程、并发数控制等功能

19.对象的克隆?

 实现对象克隆有两种方式:

  1). 实现Cloneable接口并重写Object类中的clone()方法;

  2). 实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆。

20.快速失败(fail-fast)和安全失败(fail-safe)的区别是什么?

快速失败:当你在迭代一个集合的时候,如果有另一个线程正在修改你正在访问的那个集合时,就会抛出一个ConcurrentModification。在java.util包下的都是快速失败。

安全失败:你在迭代的时候会去底层集合做一个拷贝,所以你在修改上层集合的时候是不会受影响的,不会抛出ConcurrentModification异常。在java.util.concurrent包下的全是安全失败的。

Spring

1.springMVC处理流程?

2.@Autowire?有两个实例是怎么查找的?

@Autowire ----------自动装配(byName、byType)

 

 先说明下场景,代码如下:

有如下接口:

public interface EmployeeService {
    public EmployeeDto getEmployeeById(Long id);
}

同时有下述两个实现类 EmployeeServiceImpl和EmployeeServiceImpl1:

@Service("service")
public class EmployeeServiceImpl implements EmployeeService {
    public EmployeeDto getEmployeeById(Long id) {
        return new EmployeeDto();
    }
}

@Service("service1")
public class EmployeeServiceImpl1 implements EmployeeService {
    public EmployeeDto getEmployeeById(Long id) {
        return new EmployeeDto();
    }
}

调用代码:

@Controller
@RequestMapping("/emplayee.do")
public class EmployeeInfoControl {
    
    @Autowired
    EmployeeService employeeService;
     
    @RequestMapping(params = "method=showEmplayeeInfo")
    public void showEmplayeeInfo(HttpServletRequest request, HttpServletResponse response, EmployeeDto dto) {
        #略
    }
}

在启动tomcat时报如下错误:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'employeeInfoControl': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: com.test.service.EmployeeService com.test.controller.EmployeeInfoControl.employeeService; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [com.test.service.EmployeeService] is defined: expected single matching bean but found 2: [service1, service2]

解决:

@Controller
@RequestMapping("/emplayee.do")
public class EmployeeInfoControl {
    
    @Autowired
    @Qualifier("service")
    EmployeeService employeeService;
    
    @RequestMapping(params = "method=showEmplayeeInfo")
    public void showEmplayeeInfo(HttpServletRequest request, HttpServletResponse response, EmployeeDto dto) {
        #略
    }
}


3.什么是 AOP?

AOP的主要原理:动态代理

1.静态代理: 

 针对每个具体类分别编写代理类; 

 针对一个接口编写一个代理类; 

2.动态代理: 

针对一个方面编写一个InvocationHandler,然后借用JDK反射包中的Proxy类为各种接口动态生成相应的代理类



8.什么是 IOC?

IOC控制反转的实现是基于spring的bean工厂,当你需要引用一个对象时,将本来应该由你控制的实例化对象的操作

交给配置好的xml来进行,然后再给你注入进去,从而降低代码间的耦合度

9.Spring IOC 怎么管理 Bean 之间的依赖关系,怎么避免循环依赖?

提前曝光bean实例,获得对象的早期引用
10.BeanFactory 和 FactoryBean 有什么区别?

BeanFactory,以Factory结尾,表示它是一个工厂类(接口),用于管理Bean的一个工厂。在Spring中,BeanFactory是IOC容器的核心接口,它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。

以Bean结尾,表示它是一个Bean,不同于普通Bean的是:它是实现了FactoryBean<T>接口的Bean,根据该Bean的ID从BeanFactory中获取的实际上是FactoryBean的getObject()返回的对象,而不是FactoryBean本身,如果要获取FactoryBean对象,请在id前面加一个&符号来获取。


11.BeanFactory 和 ApplicationContext 又有什么不同?

BeanFactory采用了工厂设计模式,负责读取bean配置文档,管理bean的加载,实例化,维护bean之间的依赖关系,负责bean的声明周期。而ApplicationContext除了提供上述BeanFactory所能提供的功能之外,还提供了更完整的框架功能:国际化支持、aop、事务等。同时BeanFactory在解析配置文件时并不会初始化对象,只有在使用对象getBean()才会对该对象进行初始化,而ApplicationContext在解析配置文件时对配置文件中的所有对象都初始化了,getBean()方法只是获取对象的过程。

   因此我们一般在使用的时候尽量使用ApplicationContext。

13.谈谈Spring Bean 创建过程中的设计模式?

Bean的模式(设计模式)
Spring中Bean的模式有两种,默认的是单例模式,另一种是原型模式。

单例模式的Bean,如何解决线程安全问题?
出现线程安全问题的情况: 当Bean对象对应的类存在可变的成员变量并且其中存在改变这个变量的线程时,多线程操作该Bean对象时会出现线程安全。

解决方案: Spring使用ThreadLocal解决单例模式Bean的线程安全问题。ThreadLocal采用了 “以空间换时间” 的方式,为每一个线程都提供了一份变量副本,因此可以同时访问而互不影响。

SpringCloud

1.Eureka Server能不能抗住一个大型系统的访问压力?

public abstract class AbstractInstanceRegistry implements InstanceRegistry {
    private static final Logger logger = LoggerFactory.getLogger(AbstractInstanceRegistry.class);

    private static final String[] EMPTY_STR_ARRAY = new String[0];
    private final ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>> registry
            = new ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>>();

Eureka Server的注册表直接基于纯内存,即在内存里维护了一个数据结构。

各个服务的注册、服务下线、服务故障,全部会在内存里维护和更新这个注册表。

各个服务每隔30秒拉取注册表的时候,Eureka Server就是直接提供内存里存储的有变化的注册表数据给他们就可以了。

同样,每隔30秒发起心跳时,也是在这个纯内存的Map数据结构里更新心跳时间。

ConcurrentHashMap的key就是服务名称,比如“inventory-service”,就是一个服务名称。

value则代表了一个服务的多个服务实例。value中的map:key代表服务的实例,InstanceInfo代表了服务实例的具体信息,  比   如机器的ip地址、hostname以及端口号。

Eureka Server端优秀的多级缓存机制(ReadOnlyCacheMap、ReadWriteCacheMap)

尽可能保证了内存注册表数据不会出现频繁的读写冲突问题。

并且进一步保证对Eureka Server的大量请求,都是快速从纯内存走,性能极高。

2.微服务的熔断 、降级、限流?

熔断机制是应对雪崩效应的一种微服务链路保护机制。熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状况,当失败的调用到一定阈值,缺省是5秒内20次调用失败,就会启动熔断机制。

降级是指自己的待遇下降了,从RPC调用环节来讲,就是去访问一个本地的伪装者而不是真实的服务。当双11活动时,把无关交易的服务统统降级,如查看蚂蚁深林,查看历史订单,商品历史评论,只显示最后100条等等。

限流的目的是通过对并发访问/请求进行限速或者一个时间窗口内的的请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务(定向到错误页或告知资源没有了)、排队或等待(比如秒杀、评论、下单)、降级(返回兜底数据或默认数据,如商品详情页库存默认有货)。常见的限流算法有:令牌桶、漏桶。

3.微服务的负载均衡?

feign、ribbon

 

1、随机规则(RandomRule)

2、线性轮训规则(RoundRobinRule)

3、重试规则(RetryRule)

4、WeightedResponseTimeRule(反馈权重规则)

5、ClientConfigEnableRoundRobinRule

6、BestAvailableRule(选择最空闲的服务)

7、PredicateBasedRule

 

 

MySQL

1. 如果有很多数据插⼊MYSQL 你会选择什么⽅式?

a)数据库方面:

     (1)表结构建立时不要使用索引,要不然插入过程过还要维护索引B+树;

     (2)修改存储引擎为InnoDB;

     (3)运用存储过程

b)事物使用:

     考虑把添加放在一个事物中去处理。

c)单条sql插入:

     拼接多组字段

d)文件导入:

      load data local infile "a.dat" into mytable;

e)考虑nosql:

     考虑存储nosql 再以队列的方式insert 开启多个任务进程等。

2. 如果查询很慢,你会想到的第⼀个⽅式是什么?索引是⼲嘛的?

性能优化的思路:

  1. 首先需要使用慢查询功能,去获取所有查询时间比较长的SQL语句
  2. 分析慢查询日志
  3. Query Profiler却可以定位出一条SQL语句执行的各种资源消耗情况,比如CPU,IO等,以及该SQL执行所耗费的时间等。
  4. explain执行计划

索引:

*  使用索引的主要目的是为了优化查询速度

* 索引是一种特殊的文件或者叫数据结构(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针。更通俗的说,数据库索引好比是一本书前面的目录,能加快数据库的查询速度

优点:可以大大提高MySQL的检索速度。

缺点:(1)索引会占用空间,表越大索引占用的空间越大

               (2)性能损失主要指更新操作,当你在表中添加、删除或者更新行数据的时候, 在索引中也会有相同的操作

3. 如果建了⼀个单列索引,查询的时候查出2列,会⽤到这个单列索引吗?

4. 如果建了⼀个包含多个列的索引,查询的时候只⽤了第⼀列,能不能⽤上 这个索引?查三列呢?

索引最左前缀原则

5. 接上题,如果where条件后⾯带有⼀个 i + 5 < 100 会使⽤到这个索引吗?

6. 怎么看是否⽤到了某个索引?

执行计划:explain SQL语句

7. like %aaa%会使⽤索引吗? like aaa%呢?

mysql 索引失效:

      1.如果条件中有or,即使其中有条件带索引也不会使用(这也是为什么尽量少用or的原因)

       (要想使用or,又想让索引生效,只能将or条件中的每个列都加上索引)

       2.对于多列索引,不是使用的第一部分(第一个),则不会使用索引

       3.like查询是以%开头

       4.如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引

       5.如果mysql估计使用全表扫描要比使用索引快,则不使用索引

 

8. drop、truncate、delete的区别?

一:用法和区别

drop:drop table 表名

      删除内容和定义,并释放空间。执行drop语句,将使此表的结构一起删除。

truncate (清空表中的数据):truncate table 表名

      删除内容、释放空间但不删除定义(也就是保留表的数据结构)。与drop不同的是,只是清空表数据而已。

      truncate不能删除行数据,虽然只删除数据,但是比delete彻底,它只删除表数据。

delete:delete from 表名 (where 列名 = 值)

       与truncate类似,delete也只删除内容、释放空间但不删除定义;但是delete即可以对行数据进行删除,也可以对整表数据进行删除。

二:注意

      1.delete语句执行删除的过程是每次从表中删除一行,并且同时将该行的删除操作作为事务记录在日志中保存,以便进行进行回滚操作。

      2.执行速度一般来说:drop>truncate>delete

      3.delete语句是数据库操作语言(dml),这个操作会放到 rollback segement 中,事务提交之后才生效;如果有相应的 trigger,执行的时候将被触发。

      4.truncate、drop 是数据库定义语言(ddl),操作立即生效,原数据不放到 rollback segment 中,不能回滚,操作不触发trigger。

      5.truncate语句执行以后,id标识列还是按顺序排列,保持连续;而delete语句执行后,ID标识列不连续

9. 平时你们是怎么监控数据库的? 慢SQL是怎么排查的?

腾讯云和阿里云的数据库

10. 你们数据库是否⽀持emoji表情,如果不⽀持,如何操作?

不支持:UTF-8编码有可能是两个、三个、四个字节。Emoji表情是4个字节,而Mysql的utf8编码最多3个字节,所以数据插不进去。

解决方案:将Mysql的编码从utf8转换成utf8mb4

11. 你们的数据库单表数据量是多少?⼀般多⼤的时候开始出现查询性能急 剧下降?

12. 查询死掉了,想要找出执⾏的查询进程⽤什么命令?找出来之后⼀般你 会⼲嘛?

13. 读写分离是怎么做的?你认为中间件会怎么来操作?这样操作跟事务有 什么关系?

14. 分库分表有没有做过?线上的迁移过程是怎么样的?如何确定数据是正 确的?

15.数据库的水平拆分与垂直拆分?

(1)水平分割:

例:QQ的登录表。假设QQ的用户有100亿,如果只有一张表,每个用户登录的时候数据库都要从这100亿中查找,会很慢很慢。如果将这一张表分成100份,每张表有1亿条,就小了很多,比如qq0,qq1,qq1...qq99表。

用户登录的时候,可以将用户的id%100,那么会得到0-99的数,查询表的时候,将表名qq跟取模的数连接起来,就构建了表名。比如123456789用户,取模的89,那么就到qq89表查询,查询的时间将会大大缩短。

这就是水平分割。

(2)垂直分割:

垂直分割指的是:表的记录并不多,但是字段却很长,表占用空间很大,检索表的时候需要执行大量的IO,严重降低了性能。这时需要把大的字段拆分到另一个表,并且该表与原表是一对一的关系。

例如学生答题表tt:有如下字段:

Id name 分数 题目 回答

其中题目和回答是比较大的字段,id name 分数比较小。

如果我们只想查询id为8的学生的分数:select 分数 from tt where id = 8;虽然知识查询分数,但是题目和回答这两个大字段也是要被扫描的,很消耗性能。但是我们只关心分数,并不想查询题目和回答。这就可以使用垂直分割。我们可以把题目单独放到一张表中,通过id与tt表建立一对一的关系,同样将回答单独放到一张表中。这样我们插叙tt中的分数的时候就不会扫描题目和回答了。

16. 数据库中事物的特征?

原子性( Atomicity )、一致性( Consistency )、隔离性( Isolation )和持久性( Durability )。这四个特性简称为 ACID 特性。

18. InnodB与MyISAM的区别

(1)事物:

                   MyISAM是非事务安全型的,而InnoDB是事务安全型

  (2)   锁:

                   MyISAM锁的粒度是表级,而InnoDB支持行级锁定。

  (3)    全文索引:

                   MyISAM支持全文类型索引,而InnoDB不支持全文索引。

  (4)    查询效率:

                   MyISAM相对简单,所以在效率上要优于InnoDB,小型应用可以考虑使用MyISAM。

  (5)    外键:

                    MyISAM不支持外健,InnoDB支持。

  (6)    count:

                   MyISAM保有表的总行数,InnoDB没有

                   

19. MySQL为什么使用B+树作为索引?

  1. B Tree和B+ Tree的特点与区别

* 树的高度一般都是在2-4这个高度,树的高度直接影响IO读写的次数。

* 如果是三层树结构---支撑的数据可以达到20G,如果是四层树结构---支撑的数据可以达到几十T

*  B Tree和B+ Tree的最大区别在于非叶子节点是否存储数据的问题。B Tree是非叶子节点和叶子节点都会存储数据。而B+ Tree只有叶子节点才会存储数据,而且存储的数据都是在一行上,而且这些数据都是有指针指向的,也就是由顺序的。

  1. 非聚集索引(MyISAM)

 * 叶子节点只会存储数据行的指针,简单来说数据和索引不在一起,就是非聚集索引。

 * 主键索引和辅助索引都会存储指针的值

  1. 聚集索引(InnoDB)

* 主键索引(聚集索引)的叶子节点会存储数据行,也就是说数据和索引是在一起,这就是聚集索引。

* 辅助索引只会存储主键值

* 如果没有没有主键,则使用唯一索引建立聚集索引;如果没有唯一索引,MySQL会按照一定规则创建聚集索引。

Oracle

1.MySQL和Oracle的区别

(1)Oracle是大型数据库而Mysql是中小型数据库,Oracle收费,MySQL开源免费

(2)Oracle支持大并发,大访问量,是OLTP最好的工具

(3)安装所用的空间差别也是很大的,Mysql安装完后才152M而Oracle有3G左右,且使用的时候Oracle占用特别大的内存空间和其他机器性能。

(4)主键Mysql一般使用自动增长类型,在创建表时只要指定表的主键为auto increment,插入记录时,不需要再指定该记录的主键值,Mysql将自动增长;Oracle没有自动增长类型,主键一般使用的序列,插入记录时将序列号的下一个值付给该字段即可

(5)单引号的处理MYSQL里可以用双引号包起字符串,ORACLE里只可以用单引号包起字符串

(6)分页的SQL语句,MySQL用 limit,Oracle用ROWNUM

                  select * from emp limit 2,3表示查询第三条数据到第六条数据(包前不包后)也就是第三条数据包括第三条数据不包括第六条数据 也就是 3, 4,5条记录

                  select * from (select rownum as rn,empno,ename from emp e where rownum<10) ta where ta.rn>5;

 (7)长字符串的处理长字符串的处理ORACLE也有它特殊的地方。INSERT和UPDATE时最大可操作的字符串长度小于等于4000个单字节, 如果要插入更长的字符串, 请考虑字段用CLOB类型,方法借用ORACLE里自带的DBMS_LOB程序包。插入修改记录前一定要做进行非空和长度判断,不能为空的字段值和超出长度字段值都应该提出警告,返回上次操作。

(8)空字符的处理MYSQL的非空字段也有空的内容,ORACLE里定义了非空字段就不容许有空的内容。按MYSQL的NOT NULL来定义ORACLE表结构, 导数据的时候会产生错误。因此导数据时要对空字符进行判断,如果为NULL或空字符,需要把它改成一个空格的字符串。

(9)字符串的模糊比较MYSQL里用 字段名 like '%字符串%',ORACLE里也可以用 字段名 like '%字符串%' 但这种方法不能使用索引, 速度不快。

(10)Oracle实现了ANSII SQL中大部分功能,如,事务的隔离级别、传播特性等而Mysql在这方面还是比较的弱

2.for update的功能?

就是一个悲观锁。 
借助for update子句,我们可以在应用程序的层面手工实现数据加锁保护操作(防止多线程的情况下出现问题) 
如:select * from 表名 for update,就可以把查询出来的数据进行加锁控制,别人就无法查询更新了。

 

JVM

1. 你知道哪些或者你们线上使⽤什么GC策略? 它有什么优势,适⽤于什么 场景?

2. JAVA类加载器包括⼏种?它们之间的⽗⼦关系是怎么样的?双亲委派机 制是什么意思?有什么好处?

启动Bootstrap类加载、扩展Extension类加载、系统System类加载。

父子关系如下:

启动类加载器 ,由C++ 实现,没有父类;

扩展类加载器,由Java语言实现,父类加载器为null;

系统类加载器,由Java语言实现,父类加载器为扩展类加载器;

自定义类加载器,父类加载器肯定为AppClassLoader。

双亲委派机制:类加载器收到类加载请求,自己不加载,向上委托给父类加载,父类加载不了,再自己加载。

优势避免Java核心API篡改

3. 如何⾃定义⼀个类加载器?你使⽤过哪些或者你在什么场景下需要⼀个⾃ 定义的类加载器吗?

自定义类加载的意义:

加载特定路径的class文件

加载一个加密的网络class文件

热部署加载class文件

4. 堆内存设置的参数是什么?

-Xmx 设置堆的最大空间大小

-Xms 设置堆的最小空间大小

5. Perm Space中保存什么数据? 会引起OutOfMemory吗?

加载class文件。

会引起,出现异常可以设置 -XX:PermSize 的大小。JDK 1.8后,字符串常量不存放在永久带,而是在堆内存中,JDK8以后没有永久代概念,而是用元空间替代,元空间不存在虚拟机中,二是使用本地内存。

6. 做gc时,⼀个对象在内存各个Space中被移动的顺序是什么?

标记清除法,复制算法,标记整理、分代算法。

新生代一般采用复制算法 GC,老年代使用标记整理算法。

垃圾收集器:串行新生代收集器、串行老生代收集器、并行新生代收集器、并行老年代收集器。

CMS(Current Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器,它是一种并发收集器,采用的是Mark-Sweep算法。

7. 你有没有遇到过OutOfMemory问题?你是怎么来处理这个问题的?处理 过程中有哪些收获?

permgen space、heap space 错误。

常见的原因

内存加载的数据量太大:一次性从数据库取太多数据;

集合类中有对对象的引用,使用后未清空,GC不能进行回收;

代码中存在循环产生过多的重复对象;

启动参数堆内存值小。

8. 1.8之后Perm Space有哪些变动? MetaSpace⼤⼩默认是⽆限的么? 还是 你们会通过什么⽅式来指定⼤⼩?

JDK 1.8后用元空间替代了 Perm Space;字符串常量存放到堆内存中。

MetaSpace大小默认没有限制,一般根据系统内存的大小。JVM会动态改变此值。

-XX:MetaspaceSize:分配给类元数据空间(以字节计)的初始大小(Oracle逻辑存储上的初始高水位,the initial high-water-mark)。此值为估计值,MetaspaceSize的值设置的过大会延长垃圾回收时间。垃圾回收过后,引起下一次垃圾回收的类元数据空间的大小可能会变大。

-XX:MaxMetaspaceSize:分配给类元数据空间的最大值,超过此值就会触发Full GC,此值默认没有限制,但应取决于系统内存的大小。JVM会动态地改变此值。

9. Jstack是⼲什么的? Jstat呢? 如果线上程序周期性地出现卡顿,你怀疑可 能是gc导致的,你会怎么来排查这个问题?线程⽇志⼀般你会看其中的什么 部分?

jstack 用来查询 Java 进程的堆栈信息。

jvisualvm 监控内存泄露,跟踪垃圾回收、执行时内存、cpu分析、线程分析。

10. StackOverFlow异常有没有遇到过?⼀般你猜测会在什么情况下被触 发?如何指定⼀个线程的堆栈⼤⼩?⼀般你们写多少?

栈内存溢出,一般由栈内存的局部变量过爆了,导致内存溢出。出现在递归方法,参数个数过多,递归过深,递归没有出口

多线程

1) 什么是线程?

2) 线程和进程有什么区别?

3) 如何在Java中实现线程?

4) 用Runnable还是Thread?

6) Thread 类中的start() 和 run() 方法有什么区别?

7) Java中CyclicBarrier 和 CountDownLatch有什么不同?

8) Java中的volatile 变量是什么?

9) Java中的同步集合与并发集合有什么区别?

10) 如何避免死锁?

11) Java中活锁和死锁有什么区别?

12) Java中synchronized 和 ReentrantLock 有什么不同?

13) Java中ConcurrentHashMap的并发度是什么?

14) 如何在Java中创建Immutable对象?

15) 单例模式的双检锁是什么?

16) 写出3条你遵循的多线程最佳实践

17) 如何避免死锁?

18. 常用的线程池模式以及不同线程池的使用场景

Netty

1.BIO、NIO和AIO的区别?

2.NIO的组成?

3.Netty的特点?

4.Netty的线程模型?

5.TCP 粘包/拆包的原因及解决方法?

6.了解哪几种序列化协议?

7.如何选择序列化协议?

8.Netty的零拷贝实现?

9.Netty的高性能表现在哪些方面?

10.NIOEventLoopGroup源码?

11.Netty和Mina的优缺点?

(1)都是Trustin Lee的作品,Netty更晚;

(2)Mina将内核和一些特性的联系过于紧密,使得用户在不需要这些特性的时候无法脱离,相比下性能会有所下降,Netty解决了这个设计问题;

(3)Netty的文档更清晰,很多Mina的特性在Netty里都有;

(4)Netty更新周期更短,新版本的发布比较快;

(5)它们的架构差别不大,Mina靠apache生存,而Netty靠jboss,和jboss的结合度非常高,Netty有对google protocal buf的支持,有更完整的ioc容器支持(spring,guice,jbossmc和osgi);

(6)Netty比Mina使用起来更简单,Netty里你可以自定义的处理upstream events或/和downstream events,可以使用decoder和encoder来解码和编码发送内容;

(7)Netty和Mina在处理UDP时有一些不同,Netty将UDP无连接的特性暴露出来;而Mina对UDP进行了高级层次的抽象,可以把UDP当成”面向连接”的协议,而要Netty做到这一点比较困难。
 

 

Redis

1.Redis与Memorycache的区别?

(1) 、Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,zset,hash等数据结构的存储。memcache支持简单的数据类型,String。

(2)、Redis支持数据的备份,即master-slave模式的数据备份。

(3) 、Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用,而Memecache把数据全部存在内存之中

(4)、 redis的速度比memcached快很多

(5)、Memcached是多线程,非阻塞IO复用的网络模型;Redis使用单线程的IO复用模型。
 

2.Redis的五种数据结构?

string、 hash、 list、 set、 zset

string最常规的set/get操作,value可以是String也可以是数字。一般做一些复杂的计数功能的缓存。
hashvalue存放的是结构化的对象,比较方便的就是操作其中的某个字段单点登录,存储用户信息
list使用List的数据结构消息队列、基于redis的分页功能,性能极佳,用户体验好
setset堆放的是一堆不重复值的集合全局去重
zsetsorted set多了一个权重参数score,集合中的元素能够按score进行排列排行榜应用,取TOP N操作

 

3.Redis过期策略以及内存淘汰机制

场景:(1)比如你redis只能存5G数据,可是你写了10G,那会删5G的数据。怎么删的?

               (2)   数据已经设置了过期时间,但是时间到了,内存占用率还是比较高?

过期策略:

redis采用的是定期删除+惰性删除策略。

思考???

(1)为什么不用定时删除策略?

        定时删除,用一个定时器来负责监视key,过期则自动删除。虽然内存及时释放,但是十分消耗CPU资源。在大并发请求下,CPU要将时间应用在处理请求,而不是删除key,因此没有采用这一策略.

 

(2)定期删除+惰性删除是如何工作的呢?

定期删除,redis默认每个100ms检查,是否有过期的key,有过期key则删除。需要说明的是,redis不是每个100ms将所有的key检查一次,而是随机抽取进行检查(如果每隔100ms,全部key进行检查,redis岂不是卡死)。因此,如果只采用定期删除策略,会导致很多key到时间没有删除。


于是,惰性删除派上用场。也就是说在你获取某个key的时候,redis会检查一下,这个key如果设置了过期时间那么是否过期了?如果过期了此时就会删除。

(3)采用定期删除+惰性删除就没其他问题了么?

不是的,如果定期删除没删除key。然后你也没即时去请求key,也就是说惰性删除也没生效。这样,redis的内存会越来越高。那么就应该采用内存淘汰机制。

在redis.conf中有一行配置:

# maxmemory-policy volatile-lru

 

1)noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。应该没人用吧。

 

2)allkeys-lru当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。推荐使用,目前项目在用这种

 

3)allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key。应该也没人用吧,你不删最少使用Key,去随机删。

 

4)volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。这种情况一般是把redis既当缓存,又做持久化存储的时候才用。不推荐

 

5)volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。依然不推荐

 

6)volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。不推荐

4.redis的并发竞争问题?

场景:

这个问题大致就是,同时有多个子系统去set一个key。这个时候要注意什么呢?

回答:如下所示


(1)如果对这个key操作,不要求顺序


这种情况下,准备一个分布式锁,大家去抢锁,抢到锁就做set操作即可,比较简单。


(2)如果对这个key操作,要求顺序


假设有一个key1,系统A需要将key1设置为valueA,系统B需要将key1设置为valueB,系统C需要将key1设置为valueC.


期望按照key1的value值按照 valueA–>valueB–>valueC的顺序变化。这种时候我们在数据写入数据库的时候,需要保存一个时间戳。假设时间戳如下

 

系统A key 1 {valueA  3:00}

系统B key 1 {valueB  3:05}

系统C key 1 {valueC  3:10}

 

那么,假设这会系统B先抢到锁,将key1设置为{valueB 3:05}。接下来系统A抢到锁,发现自己的valueA的时间戳早于缓存中的时间戳,那就不做set操作了。以此类推。

 

其他方法,比如利用队列,将set方法变成串行访问也可以。总之,灵活变通。

5.单线程的redis为什么这么快?

(一)纯内存操作
(二)单线程操作,避免了频繁的上下文切换
(三)采用了非阻塞I/O多路复用机制

redis单线程模型
redis单线程模型

 14.redis和数据库双写一致性问题

先明白一个前提。就是如果对数据有强一致性要求,不能放缓存。我们所做的一切,只能保证最终一致性

采取正确更新策略,先更新数据库,再删缓存。其次,因为可能存在删除缓存失败的问题,提供一个补偿措施即可,例如利用消息队列。

15.如何应对缓存穿透和缓存雪崩问题

缓存穿透:

即黑客故意去请求缓存中不存在的数据,导致所有的请求都怼到数据库上,从而数据库连接异常。

解决方案:

 

(一)利用互斥锁,缓存失效的时候,先去获得锁,得到锁了,再去请求数据库。没得到锁,则休眠一段时间重试

 

(二)采用异步更新策略,无论key是否取到值,都直接返回。value值中维护一个缓存失效时间,缓存如果过期,异步起一个线程去读数据库,更新缓存。需要做缓存预热(项目启动前,先加载缓存)操作。

 

(三)提供一个能迅速判断请求是否有效的拦截机制,比如,利用布隆过滤器,内部维护一系列合法有效的key。迅速判断出,请求所携带的Key是否合法有效。如果不合法,则直接返回。

缓存雪崩:

即缓存同一时间大面积的失效,这个时候又来了一波请求,结果请求都怼到数据库上,从而导致数据库连接异常。

解决方案:

 

(一)给缓存的失效时间,加上一个随机值,避免集体失效。

 

(二)使用互斥锁,但是该方案吞吐量明显下降了。

 

(三)双缓存。我们有两个缓存,缓存A和缓存B。缓存A的失效时间为20分钟,缓存B不设失效时间。自己做缓存预热操作。然后细分以下几个小点

 

  • I 从缓存A读数据库,有则直接返回

  • II A没有数据,直接从B读数据,直接返回,并且异步启动一个更新线程。

  • III 更新线程同时更新缓存A和缓存B。

NGINX

1、请解释一下什么是Nginx?
答:Nginx是一个web服务器和反向代理服务器,用于HTTP、HTTPS、SMTP、POP3和IMAP协议。
2、请列举Nginx的一些特性?
答:Nginx服务器的特性包括: 

 1)反向代理/L7负载均衡器 

 2)嵌入式Perl解释器 

 3)动态二进制升级 

 4)可用于重新编写URL,具有非常好的PCRE支持

3、nginx和apache的区别?
答: 

 1)轻量级,同样起web 服务,比apache 占用更少的内存及资源

 2)抗并发,nginx 处理请求是异步非阻塞的,而apache 则是阻塞型的,在高并发下nginx 能保持低资源低消耗高性能

 3)高度模块化的设计,编写模块相对简单 

 4)最核心的区别在于apache是同步多进程模型,一个连接对应一个进程;nginx是异步的,多个连接(万级别)可以对应一个进程

4.nginx是如何实现高并发的
答:一个主进程,多个工作进程,每个工作进程可以处理多个请求,每进来一个request,会有一个worker进程去处理。但不是全程的处理,处理到可能发生阻塞的地方,比如向上游(后端)服务器转发request,并等待请求返回。那么,这个处理的worker继续处理其他请求,而一旦上游服务器返回了,就会触发这个事件,worker才会来接手,这个request才会接着往下走。由于web server的工作性质决定了每个request的大部份生命都是在网络传输中,实际上花费在server机器上的时间片不多。这是几个进程就解决高并发的秘密所在。即@skoo所说的webserver刚好属于网络io密集型应用,不算是计算密集型。
5、请解释Nginx如何处理HTTP请求。
答:Nginx使用反应器模式。主事件循环等待操作系统发出准备事件的信号,这样数据就可以从套接字读取,在该实例中读取到缓冲区并进行处理。单个线程可以提供数万个并发连接。
6、在Nginx中,如何使用未定义的服务器名称来阻止处理请求?
答:只需将请求删除的服务器就可以定义为: 

 Server { 
     listen 80; server_name “ “ ; 
     return 444;
 }
这里,服务器名被保留为一个空字符串,它将在没有“主机”头字段的情况下匹配请求,而一个特殊的Nginx的非标准代码444被返回,从而终止连接。

7、 使用“反向代理服务器”的优点是什么?
答:反向代理服务器可以隐藏源服务器的存在和特征。它充当互联网云和web服务器之间的中间层。这对于安全方面来说是很好的,特别是当您使用web托管服务时。
8、请列举Nginx服务器的最佳用途。
答:Nginx服务器的最佳用法是在网络上部署动态HTTP内容,使用SCGI、WSGI应用程序服务器、用于脚本的FastCGI处理程序。它还可以作为负载均衡器。
9、请解释Nginx服务器上的Master和Worker进程分别是什么?
答: 

 Master进程:读取及评估配置和维持 

 Worker进程:处理请求

10、请解释你如何通过不同于80的端口开启Nginx?
答:为了通过一个不同的端口开启Nginx,你必须进入/etc/Nginx/sites-enabled/,如果这是默认文件,那么你必须打开名为“default”的文件。编辑文件,并放置在你想要的端口:

Like server { 
   listen 81; 
}

11、请解释是否有可能将Nginx的错误替换为502错误、503?
答: 502 =错误网关 503 =服务器超载 有可能,但是您可以确保fastcgi_intercept_errors被设置为ON,并使用错误页面指令。 

Location / { fastcgi_pass 127.0.01:9001; fastcgi_intercept_errors on; error_page 502 =503/error_page.html; #… }
12、在Nginx中,解释如何在URL中保留双斜线?
答:要在URL中保留双斜线,就必须使用merge_slashes_off; 

 语法:merge_slashes [on/off] 

 默认值: merge_slashes on 

 环境: http,server

13、请解释ngx_http_upstream_module的作用是什么?
答:ngx_http_upstream_module用于定义可通过fastcgi传递、proxy传递、uwsgi传递、memcached传递和scgi传递指令来引用的服务器组。
14、请解释什么是C10K问题?
答:C10K问题是指无法同时处理大量客户端(10,000)的网络套接字。
15、请陈述stub_status和sub_filter指令的作用是什么?
答: 

 1)Stub_status指令:该指令用于了解Nginx当前状态的当前状态,如当前的活动连接,接受和处理当前读/写/等待连接的总数   2)Sub_filter指令:它用于搜索和替换响应中的内容,并快速修复陈旧的数据

16、解释Nginx是否支持将请求压缩到上游?
答:您可以使用Nginx模块gunzip将请求压缩到上游。gunzip模块是一个过滤器,它可以对不支持“gzip”编码方法的客户机或服务器使用“内容编码:gzip”来解压缩响应。
17、解释如何在Nginx中获得当前的时间?
答:要获得Nginx的当前时间,必须使用SSI模块、$date_gmt和$date_local的变量。Proxy_set_header THE-TIME $date_gmt;
18、用Nginx服务器解释-s的目的是什么?
答:用于运行Nginx -s参数的可执行文件。
19、解释如何在Nginx服务器上添加模块?
答:在编译过程中,必须选择Nginx模块,因为Nginx不支持模块的运行时间选择。

20、Nginx负载均衡的五种策略?

(1)、轮询(默认)
每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。 
upstream backserver { 
server 192.168.0.14; 
server 192.168.0.15; 


(2)、指定权重
指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。 
upstream backserver { 
server 192.168.0.14 weight=10; 
server 192.168.0.15 weight=10; 


(3)、IP绑定 ip_hash
每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。 
upstream backserver { 
ip_hash; 
server 192.168.0.14:88; 
server 192.168.0.15:80; 


(4)、fair(第三方)
按后端服务器的响应时间来分配请求,响应时间短的优先分配。 
upstream backserver { 
server server1; 
server server2; 
fair; 


(5)、url_hash(第三方)
按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。 
upstream backserver { 
server squid1:3128; 
server squid2:3128; 
hash $request_uri; 
hash_method crc32; 

 

网络协议

1.Http和Https的区别?

答:Http协议运行在TCP之上,明文传输,客户端与服务器端都无法验证对方的身份;Https是身披SSL(Secure Socket Layer)外壳的Http,运行于SSL上,SSL运行于TCP之上,是添加了加密和认证机制的HTTP。二者之间存在如下不同:

  • 端口不同:Http与Http使用不同的连接方式,用的端口也不一样,前者是80,后者是443;

  • 资源消耗:和HTTP通信相比,Https通信会由于加减密处理消耗更多的CPU和内存资源;

  • 开销:Https通信需要证书,而证书一般需要向认证机构购买;

Https的加密机制是一种共享密钥加密和公开密钥加密并用的混合加密机制。

2.对称加密与非对称加密?

答:

对称密钥加密是指加密和解密使用同一个密钥的方式,这种方式存在的最大问题就是密钥发送问题,即如何安全地将密钥发给对方;而非对称加密是指使用一对非对称密钥,即公钥和私钥,公钥可以随意发布,但私钥只有自己知道。发送密文的一方使用对方的公钥进行加密处理,对方接收到加密信息后,使用自己的私钥进行解密。

由于非对称加密的方式不需要发送用来解密的私钥,所以可以保证安全性;但是和对称加密比起来,它非常的慢,所以我们还是要用对称加密来传送消息,但对称加密所使用的密钥我们可以通过非对称加密的方式发送出去。

3.TCP与UDP的区别?

答:TCP (Transmission Control Protocol)和UDP(User Datagram Protocol)协议属于传输层协议,它们之间的区别包括:

  • TCP是面向连接的,UDP是无连接的;

  • TCP是可靠的,UDP是不可靠的;

  • TCP只支持点对点通信,UDP支持一对一、一对多、多对一、多对多的通信模式;

  • TCP是面向字节流的,UDP是面向报文的;

  • TCP有拥塞控制机制;UDP没有拥塞控制,适合媒体通信;

  • TCP首部开销(20个字节)比UDP的首部开销(8个字节)要大;

 

4.从浏览器中输入URL到页面加载的发生了什么?

  整个过程大致可以分成以下几个步骤:

(1)浏览器查看缓存,如果缓存中有,则直接在屏幕中显示内容;若没有,则执行以下步骤;

(2)进行域名(DNS)解析,获取相应的IP地址;(域名解析的过程,实际上是将域名还原为IP地址的过程)

(3)浏览器向服务器发起TCP连接,与浏览器建立TCP三次握手;

(4)握手成功后,浏览器发送HTTP请求;(HTTP请求主要包括:请求行、请求头、请求正文)

(5)服务器接受并处理请求,并返回HTTP响应;

(6)浏览器收到HTTP响应后,开始构建页面,解析html源码,对页面进行渲染;

(7)生成DOM树,解析CSS样式、js交互;

(8)(可选)浏览器发送静态资源请求;

(9)(可选)浏览器发送Ajax请求;

(10)页面构建完成,关闭连接。(TCP四次挥手)
 

分布式

1.分布式锁?

https://blog.csdn.net/zhangsanfeng2009/article/details/80970059

           (1)目的:解决分布式场景下数据一致性的问题,分布式与单机情况下最大的不同在于其不是多线程而是多进程

                           多线程由于可以共享堆内存,因此可以简单的采取内存作为标记存储位置。而进程之间甚至可能都不在同一台物                              理机上,因此需要将标记存储在一个所有进程都能看到的地方。

        (2)业务场景:

        (3)实现方式:

                      #####基于数据库实现分布式锁

                         1.基于数据库表(methodLock表、version字段)乐观锁

                         2.基于数据库悲观锁(for  update)

                         

                           数据库实现分布式锁的缺点: 会有各种各样的问题,在解决问题的过程中会使整个方案变得越来越复杂。

                            操作数据库需要一定的开销,性能问题需要考虑。

                    

                    ### 基于 Redis 做分布式锁

                    1.基于 REDIS 的 SETNX()、EXPIRE() 方法做分布式锁

                    2.redis官方推荐使用的Redisson就提供了分布式锁和相关服务

                    

                   使用缓存实现分布式锁的优点: 性能好,实现起来较为方便。

                   使用缓存实现分布式锁的缺点: 通过超时时间来控制锁的失效时间并不是十分的靠谱。

                     ###基于 ZooKeeper 做分布式锁

                    1.利用临时顺序节点控制时序实现

                   使用Zookeeper实现分布式锁的优点: 有效的解决单点问题,不可重入问题,非阻塞问题以及锁无法释放的问题。实现                     起来较为简单。

                   使用Zookeeper实现分布式锁的缺点 : 性能上不如使用缓存实现分布式锁。 需要对ZK的原理有所了解。

                   

 

                     

        

2.分布式事物?

简单的说,就是一次大的操作由不同的小操作组成,这些小的操作分布在不同的服务器上,且属于不同的应用,比如从一个oracle中存一个记录,再从另一个oracle中删除那条记录,分布式事务需要保证这些小操作要么全部成功,要么全部失败

                  (1)两阶段提交协议(2PC)

优点: 尽量保证了数据的强一致,适合对数据强一致要求很高的关键领域。(其实也不能100%保证强一致)

缺点: 实现复杂,牺牲了可用性,对性能影响较大,不适合高并发高性能场景

 

                 (2) 补偿事务(TCC)

                       Try -Confirm-Cancel 

                 

举个例子,假入 Bob 要向 Smith 转账,思路大概是:
我们有一个本地方法,里面依次调用
1、首先在 Try 阶段,要先调用远程接口把 Smith 和 Bob 的钱给冻结起来。
2、在 Confirm 阶段,执行远程调用的转账的操作,转账成功进行解冻。
3、如果第2步执行成功,那么转账成功,如果第二步执行失败,则调用远程冻结接口对应的解冻方法 (Cancel)。

优点: 跟2PC比起来,实现以及流程相对简单了一些,但数据的一致性比2PC也要差一些

缺点: 缺点还是比较明显的,在2,3步中都有可能失败。TCC属于应用层的一种补偿方式,所以需要程序员在实现的时候多写很多补偿的代码,在一些场景中,一些业务流程可能用TCC不太好定义及处理。

                 (3) 本地消息表(异步确保)

基本思路就是:(这种思路是来源于ebay)

消息生产方,需要额外建一个消息表,并记录消息发送状态。消息表和业务数据要在一个事务里提交,也就是说他们要在一个数据库里面。然后消息会经过MQ发送到消息的消费方。如果消息发送失败,会进行重试发送。

消息消费方,需要处理这个消息,并完成自己的业务逻辑。此时如果本地事务处理成功,表明已经处理成功了,如果处理失败,那么就会重试执行。如果是业务上面的失败,可以给生产方发送一个业务补偿消息,通知生产方进行回滚等操作。

生产方和消费方定时扫描本地消息表,把还没处理完成的消息或者失败的消息再发送一遍。如果有靠谱的自动对账补账逻辑,这种方案还是非常实用的。

这种方案遵循BASE理论,采用的是最终一致性,笔者认为是这几种方案里面比较适合实际业务场景的,即不会出现像2PC那样复杂的实现(当调用链很长的时候,2PC的可用性是非常低的),也不会像TCC那样可能出现确认或者回滚不了的情况。

优点: 一种非常经典的实现,避免了分布式事务,实现了最终一致性。

缺点: 消息表会耦合到业务系统中,如果没有封装好的解决方案,会有很多杂活需要处理。

                    (4)MQ 事务消息

有一些第三方的MQ是支持事务消息的,比如RocketMQ,他们支持事务消息的方式也是类似于采用的二阶段提交,但是市面上一些主流的MQ都是不支持事务消息的,比如 RabbitMQ 和 Kafka 都不支持。

以阿里的 RocketMQ 中间件为例,其思路大致为:

第一阶段Prepared消息,会拿到消息的地址。
第二阶段执行本地事务,第三阶段通过第一阶段拿到的地址去访问消息,并修改状态。

也就是说在业务方法内要想消息队列提交两次请求,一次发送消息和一次确认消息。如果确认消息发送失败了RocketMQ会定期扫描消息集群中的事务消息,这时候发现了Prepared消息,它会向消息发送者确认,所以生产方需要实现一个check接口,RocketMQ会根据发送端设置的策略来决定是回滚还是继续发送确认消息。这样就保证了消息发送与本地事务同时成功或同时失败。

优点: 实现了最终一致性,不需要依赖本地数据库事务。

缺点: 实现难度大,主流MQ不支持,RocketMQ事务消息部分代码也未开源

 

 

3.分布式session


4.分布式一致性hash?

一致性哈希是一种特殊的哈希算法,提供了这样的一个哈希表,当重新调整大小的时候,平均只有部分(k/n)key需要重新映射哈希槽,而不像传统哈希表那样几乎所有key需要需要重新映射哈希槽


 

 

 

 

 

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值