阿里巴巴四面

阿里巴巴电话面试总结

 

  1. 谈一谈你对反射机制的理解。

(官方概念)在程序运行状态中,对于任意一个类,都能够知道它的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性。这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

(自己的理解)java中,我们要想知道一个类里面有哪些属性和方法,要想创建一个对象并调用对象里面的属性和方法,我们首先要拿到这个类的.java文件,但是有些情况下,我们无法获取到这个类的.java文件,只能获取到这个类编译后的.class文件,如何通过这个.class文件,去知道这个类里面有哪些属性和方法,如何通过这个.class文件去创建这个类的实例对象,并调用里面的属性和方法,这种技术就叫java的反射机制。

 

  1. 讲述一下synchronized关键字的原理。

synchronized关键实现同步依赖于锁对象,其中,synchronized关键字实现的同步代码块依赖的锁对象是任意对象,synchronized关键字实现的同步方法依赖的锁对象是this对象,synchronized关键字实现的静态同步方法依赖的锁对象是类的字节码对象。

每一个对象都有一个监视器锁monitor,monitor有一个进入数,该进入数有两种状态,0或者1,默认值为0,假如线程A访问synchronized关键字修饰的同步代码块时,首先判断monitor进入数的值,发现为0,可以访问。当进入该代码块后,进入数的值由0变为1,此时我们可以说该线程A拿到了锁。之后假如线程B也来访问该同步代码块,它也需要去判断监视器锁中进入数的值,发现为1,不能访问,线程B将处于阻塞状态。当线程A执行完同步代码块后,会释放锁,即将monitor进入数的值由1改为0。此时其它的线程才能够进入该同步代码块。

这就是synchronized关键实现同步的原理。

 

  1. 如何在分布式环境下实现线程同步?

答:在单机环境下实现线程同步的方法在分布式环境下都不适用。分布式环境中的线程安全问题是一大难点。有什么方法来解决呢?

线程安全问题指的是当多个线程修改、访问同一共享资源时,出现的结果不确定的现象。不同的线程可能处于集群中不同的服务上,我们无法像单机情况下加锁来实现同步,但是共享资源是唯一的,数据库是分布式环境中各服务器的共享点,所以第一个方法就是:

(一)为数据库中的共享数据加行锁

不管有多少线程来访问这条共享数据,不管这些线程属于哪些服务器,加了行锁后的共享数据,一次只允许一个线程访问修改,那这个线程没执行完前,其它的线程只能处于阻塞状态,只能等着。这种方式安全性比较高,但是如果对每一条共享数据都加行锁的话,执行效率会大大下降。但这是一种方法。

(二)避免共享资源的存在

在并发访问时,之所以会出现线程安全问题,原因就是有多个线程同时访问、修改共享资源。如果没有共享资源呢?就算再多的线程也不会出现线程安全问题。这就需要我们在业务设计时,通过合理的时间调度,保证任务与任务之间不存在共享资源,比如我们需要用多线程或分布式集群来计算一堆客户的相关统计值,由于客户的统计值是共享资源,因此可能会产生线程安全问题。但从业务上我们可以分析出客户与客户之间数据是不共享的,因此可以设计一个规则来保证一个客户的计算工作和数据访问只会被一个线程或一台工作机完成,而不是把一个客户的计算工作分配给多个线程去完成。这种规则很容易设计,例如可以采用hash算法。

(三)时间戳(网上查的,不太能理解)

分布式环境中并发是没法保证时序的,无论是通过远程接口的同步调用或异步消息,因此很容易造成某些对时序性有要求的业务在高并发时产生错误。比如系统A需要把某个值的变更同步到系统B,由于通知的时序问题会导致一个过期的值覆盖了有效值。对于这个问题,常用的办法就是采用时间戳的方式,每次系统A发送变更给系统B的时候需要带上一个能标示时序的时间戳,系统B接到通知后会拿时间戳与存在的时间戳比较,只有当通知的时间戳大于存在的时间戳,才做更新。这种方式比较简单,但关键在于调用方一般要保证时间戳的时序有效性。

(四)串行化(网上查的,不太能理解)

有的时候可以通过串行化可能产生并发问题操作,牺牲性能和扩展性,来满足对数据一致性的要求。比如分布式消息系统就没法保证消息的有序性,但可以通过变分布式消息系统为单一系统就可以保证消息的有序性了。另外,当接收方没法处理调用有序性,可以通过一个队列先把调用信息缓存起来,然后再串行地处理这些调用。

 

  1. 分布式中为什么会经常用到消息中间件(消息队列),有什么用?

答:消息队列利用发布——订阅模式工作,消息发送者发布消息,一个或者多个消息接收者订阅消息。消息发送者是消息源,在对消息进行处理后将消息发送至分布式消息队列,消息接收者从分布式消息队列获取该消息后继续进行处理。

消息中间件在分布式系统中的主要作用:异步通讯解耦并发缓冲。通过引入消息中间件来解耦应用间(服务间)的直接调用,同时也会起到异步通讯和缓冲并发的作用。

异步通讯:消息发送方将消息发送给消息队列之后,不需要等待消息接收方将数据处理,就可以再发送其它的消息给消息队列。

解耦:消息发送者和消息接受者之间没有直接耦合,消息发送者将消息发送至分布式消息队列即结束对消息的处理,而消息接受者只需要从分布式消息队列获取消息后进行处理,不需要知道该消息从何而来。对新增业务,只要对该类消息感兴趣,即可订阅该消息,对原有系统和业务没有任何影响,从而降低了网站业务的耦合性。

并发缓冲:在网站访问高峰,消息可以暂时存储在消息队列中等待消息接收者根据自身负载处理能力控制消息处理速度,减轻数据库等后端存储的负载压力。

 

  1. 如何解决分布式事务问题?有哪些方法?

答:之前我学的事务指的都是本地事务,本地事务可以严格的保证事务的四大特性:ACID,但是在分布式环境下,本地事务就无法使用了。

举个例子:

就拿互联网应用的用户注册功能来说,通常都有两个操作:

- 注册成功,保存用户信息;

- 注册成功,给用户对应的积分或成长值。

如果是一个单体架构实现这个功能就非常简单,在一个本地事务里,往用户信息表中插入一条记录,并且往积分表中插入一条记录,提交事务就完成了。但如果是微服务架构,用户和积分通常是两个独立的服务,它们有各自的应用和数据库,那么本地事务就失效了。

这就涉及到分布式事务问题,分布式事务解决方案基于两个定理,分别是CAP定理和BASE定理。

CAP定理:

任何分布式系统在可用性、一致性、分区容错性方面,不能兼得,最多只能得其二。

而分区容错性(数据传输的不确定性)在分布式系统中又是不可缺少的,所以,只能在可用性和一致性方面进行取舍。

基于CAP定理,我们知道,要在分布式系统中实现事务的ACID特性是不现实的,必须进行取舍,因此出现了BASE定理。

BASE定理:

Basically Available(基本可用)、Soft state(软状态)和Eventually consistent(最终一致性)。

将BASE定理和CAP定理结合可知:BASE定理牺牲高一致性,获取可用性和分区容错性。

将BASE定理与ACID特性结合可知:BASE定理保障了事务的原子性(A)和持久性(D),降低了一致性(C)和隔离性(I)的要求。

基于CAP定理和BASE定理,常见的分布式事务解决方案有三种:

(1)可靠消息最终一致性方案(异步确保型)(适用场景比较广)

(2)TCC事务补偿型方案(两阶段型、补偿型)

(3)最大努力通知型方案

 

  1. 简单介绍一下可靠消息最终一致性方案。

答:可靠消息最终一致性方案依赖于消息中间件,可以把整个方案分成两个问题,是消息发送一致性问题,一个是消息投递问题。市场上现有的消息中间件,如MQ系列,并不能保证消息发送的一致性问题,所以,可靠消息最终一致性方案除了用到消息中间件外,还需要基于消息中间件完成一些辅助设计,这些设计可分为如下子系统:

消息服务子系统

实时消息服务子系统(消息中间件)

消息状态确认子系统

消息恢复子系统

消息管理子系统

 

其正向流程如下:

  1. 主动方应用系统调用“预发送消息接口”把消息发给消息服务子系统,消息状态标记为“待确认”;
  2. 消息服务子系统调用“存储预发送消息接口”,把消息持久化到消息服务库,但并不向被动方应用系统投递消息;
  3. 消息服务子系统向主动方应用系统返回消息持久化结果(成功/失败),主动方应用系统根据返回结果进行判断如何进行业务操作处理:
  1. 失败:放弃业务操作处理,结束(必要时向上层返回失败结果);
  2. 成功:执行业务操作处理;
  1. 业务操作完成后,把业务操作结果(成功/失败)发送给消息服务子系统;
  2. 消息服务子系统收到业务操作结果后,根据业务结果进行处理;
  1. 失败:删除消息存储中的消息,结束;
  2. 成功:将状态为“待确认”的消息更新为“待发送(可发送)”,并将消息发送给实时消息服务子系统。
  1. 被动方应用系统中的消息业务消费端对实时消息服务子系统进行实时监控;
  2. 当检测到有消息需要处理时,调用“业务处理”接口,处理业务。
  3. 业务处理完毕,被动方应用系统中的消息业务消费端向实时消息服务子系统返回一个带有ACK标志的确认信息。实时消息服务子系统收到这个确认消息之后,就会将队列中对应的消息删除掉;
  4. 被动方应用系统中的消息业务消费端调用消息服务子系统中的“确认消息已被成功消费”接口,将对应的“待发送”状态的消息删除掉。

若整个过程发生异常怎么处理?

(消息发送一致性过程异常处理)

当消息服务子系统收到主动方应用系统发送的“待确认”消息,并将之持久化进消息服务器后,会向主动方应用系统返回一个持久化结果,接下来就是等待主动方应用系统返回的业务操作结果。可是这个过程因为是跨服务,就有可能因为网络原因中断。消息服务子系统迟迟接收不到主动方应用系统返回的业务执行结果,无法知道执行到底是成功还是失败,那该怎么办呢?

消息状态确认子系统用来处理这种异常情况。我们可以将消息状态确认子系统看作是一个定时轮询的进程,它会定时调用消息服务子系统中的“查询状态确认超时的消息”接口,从消息服务库中将那些一直得不到确认的“待确认”消息捞取出来,同时消息状态确认子系统也要调用主动方应用系统中的“业务查询”接口,来判断对应的业务操作有没有执行成功,若执行成功,消息状态确认子系统就会调用消息服务子系统中的“确认并发送消息”接口,将“待确认”消息的状态改为“待发送”,并将消息发送给实时消息服务子系统;若执行失败,消息状态确认子系统就会调用消息服务子系统中的“删除消息”接口,将那些得不到确认的“待确认消息”删除。

消息状态确认子系统是用来处理消息发送一致性中的异常情况的。

(消息投递过程异常处理)

被动方应用系统中的消息业务消费端监听到实时消息服务子系统中有消息需要处理时,就会调用“业务操作”接口执行业务操作,之后会返回给实时消息服务子系统一个确认信息。两者也是跨服务的,可能会因为网络原因导致中断。无法将ACK确认消息返给实时消息服务子系统。这样就会导致消息服务子系统中状态为“待发送”的消息得不到“消息已被成功消费”的确认。对于这种异常情况,我们要如何处理呢?

消息恢复子系统是用来解决消息消费过程中出现的异常情况的。我们也可以将消息恢复子系统看作一个定时轮询的进程,它会定时调用消息服务子系统中的“查询消费确认超时的消息”接口,将那些迟迟得不到消费确认的状态为“待发送”状态的消息捞取出来,并把这些消息重新投递给实时消息服务子系统。让消息投递过程再重新执行一遍。

 

  1. 简单介绍一下TCC事务补偿型方案。

答:TCC方案不需要用到消息中间件。解决分布式事务问题时,大多数情况下,我们选择使用可靠消息最终一致性方案,但有些业务场景对实时性要求特别高,这个时候我们必须要选择使用TCC方案。比方说账户处理、收费等业务场景。

比方说在电子商务中,当用户订单创建完成之后,需要向资金库添加资金,向积分库添加积分,这种情况对实时性要求特别高,对一致性要求特别高,对隔离性要求特别高,这种情况下就要选择使用TCC方案来处理分布式事务问题。

一个完整的TCC事务方案包含三个角色,分别是:主业务服务、从业务服务和业务活动管理器。

·主业务服务负责发起并完成整个业务活动

·从业务服务提供TCC型业务操作

·业务活动管理器控制业务活动的一致性,它登记业务活动中的操作,并在业务活动提交时确认所有的TCC型操作的confirm操作,在业务活动取消时调用所有TCC型操作的cancel操作。

我们可以这样理解,主业务活动负责发起整个事务,但具体的工作由从业务服务来完成,我们所讲的TCC事务方案主要指的就是从业务服务所做的事情。

TCC对应着三个英文单词,分别是Try、Confirm和Cancel三种操作,这三种操作的业务含义如下:

·Try:预留业务资源

·Confirm:确认执行业务操作

·Cancel:取消执行业务操作

 

这样讲特别抽象,我们举一个具体的例子来说明:

账务拆分的业务场景如下,分别位于三个不同分库的帐户A、B、C,A和B一起向C转帐共80元:

(1)Try:尝试执行业务。

完成所有业务检查(一致性):检查A、B、C的帐户状态是否正常,帐户A的余额是否不少于30元,帐户B的余额是否不少于50元。

预留业务资源(准隔离性):帐户A的冻结金额增加30元,帐户B的冻结金额增加50元,这样就保证不会出现其他并发进程扣减了这两个帐户的余额而导致在后续的真正转帐操作过程中,帐户A和B的可用余额不够的情况。

(2)Confirm:确认执行业务。

真正执行业务:如果Try阶段帐户A、B、C状态正常,且帐户A、B余额够用,则执行帐户A给账户C转账30元、帐户B给账户C转账50元的转帐操作。

不做任何业务检查:这时已经不需要做业务检查,Try阶段已经完成了业务检查。

只使用Try阶段预留的业务资源:只需要使用Try阶段帐户A和帐户B冻结的金额即可。

(3)Cancel:取消执行业务

释放Try阶段预留的业务资源:如果Try阶段部分成功,比如帐户A的余额够用,且冻结相应金额成功,帐户B的余额不够而冻结失败,则需要对帐户A做Cancel操作,将帐户A被冻结的金额解冻掉。

问题1:TCC方案是二阶段提交操作,但和二阶段提交协议2PC不是一回事。

问题2:TCC方案的主要缺点就是开发成本高(全部需要代码实现,难!)。

问题3:在实时性要求高、一致性要求高、隔离性要求的业务场景下,一般都要选择TCC事务方案。

 

  1. 简介一下最大努力通知型方案。

答:该方案和可靠消息最终一致性方案一样,都要借助消息中间件。具体业务流程没细研究。

 

数据库集群方面的知识点

1海量数据的存储问题

如今随着互联网的发展,数据的量级也是呈指数的增长,从GB到TB到PB。对数据的各种操作也是愈加的困难,传统的关系型数据库已经无法满足快速查询与插入数据的需求。这个时候NoSQL的出现暂时解决了这一危机。它通过降低数据的安全性,减少对事务的支持,减少对复杂查询的支持,来获取性能上的提升。

但是,在有些场合NoSQL一些折衷是无法满足使用场景的,就比如有些使用场景是绝对要有事务与安全指标的。这个时候NoSQL肯定是无法满足的,所以还是需要使用关系型数据库。如何使用关系型数据库解决海量存储的问题呢?此时就需要做数据库集群,为了提高查询性能将一个数据库的数据分散到不同的数据库中存储。

当数据太多,以至于数据库存不下的时候,我们要做数据库分片。

2什么是数据库分片

简单来说,就是指通过某种特定的条件,将我们存放在同一个数据库中的数据分散存放到多个数据库(主机)上面,以达到分散单台设备负载的效果。

数据的切分根据其切分规则的类型,可以分为两种切分模式。

(1)一种是按照不同的表(或者Schema)来切分到不同的数据库(主机)之上,这种切可以称之为数据的垂直(纵向)切分。数据量比较大,是因为有几张表中存放的数据量比较大,比如商品表、订单表、商品表等,我把不同的表放到不同的数据库中,订单库、商品库、用户库等。将不同的表放到不同的数据库中,这就是所谓的垂直切割。这种思路是可行的,但是假如我只是单张表数据量特别大,即便是水平切分之后,存这张表的数据库性能也会很低。

(2)另外一种则是根据表中的数据的逻辑关系,将同一个表中的数据按照某种条件拆分到多台数据库(主机)上面,这种切分称之为数据的水平(横向)切分。大多数的互联网网站采取的是水平切割,因为垂直切割解决不了单表数据量过大的问题。

现在大多互联网项目的数据分片采用的都是水平切割,垂直切割解决不了单表数据量太大的问题。

当数据库分片后(水平切割),数据由一个数据库分散到多个数据库中。此时系统要查询时需要切换不同的数据库进行查询,那么系统如何知道要查询的数据在哪个数据库中?当添加一条记录时要向哪个数据库中插入呢?这些问题处理起来都是非常的麻烦。

这种情况下可以使用一个数据库中间件mycat来解决相关的问题。接下来了解一下什么是mycat。

3什么是Mycat?

Mycat背后是阿里曾经开源的知名产品——Cobar。Cobar的核心功能和优势是MySQL数据库分片,此产品曾经广为流传,据说最早的发起者对Mysql很精通,后来从阿里跳槽了,阿里随后将Cobar开源,并维持到2013年年初。然后,就没有然后了。

Cobar的思路和实现路径的确不错。基于Java开发的,实现了MySQL公开的二进制传输协议,巧妙地将自己伪装成一个MySQL Server,目前市面上绝大多数MySQL客户端工具和应用都能兼容。比自己实现一个新的数据库协议要明智的多,因为生态环境在那里摆着。

Mycat是基于Cobar演变而来,对Cobar的代码进行了彻底的重构,使用NIO重构了网络模块,并且优化了Buffer内核,增强了聚合、Join等基本特性,同时兼容绝大多数数据库称为通用的数据库中间件。

简单的说,Mycat就是:一个新颖的数据库中间件产品,支持mysql集群,或者marisdb cluster,提供高可用性数据分片集群。你可以像使用mysql一样使用mycat。对于开发人员来说根本感觉不到mycat的存在。

4Mycat具体是如何进行数据库分片的呢?

答:这一块不是我做的,我并不太清楚内部实现原理。

数据库集群由mycat来管理,对于开发人员来说,只需要连一个mycat就好了,操作mycat和操作单mysql是一样一样的!至于mycat管理了多少集群?集群之间是如何运作的?不需要开发人员来管。

5Mycat读写分离

数据库读写分离对于大型系统或者访问量很高的互联网应用来说,是必不可少的一个重要功能。对于MySQL来说,标准的读写分离是主从模式,一个写节点Master后面跟着多个读节点,读节点的数量取决于系统的压力,通常是1-3个读节点的配置

 

Mycat读写分离和自动切换机制,需要mysql的主从复制机制配合。主数据库通过主从复制生成一个从数据库,这两个数据库中存放的数据是一模一样的,当对主数据库修改之后,修改结果也会同步到从数据库中。数据库的读写分离要依赖于数据库的主从复制,对主数据库进行写操作,对从数据库进行读操作。

6Mysql的主从复制

 

主从复制需要注意的地方

1、主DB server和从DB server数据库的版本一致

2、主DB server和从DB server数据库数据名称一致

3、主DB server开启二进制日志,主DB server和从DB server的server_id都必须唯一

 

猜你喜欢

转载自blog.csdn.net/lz1170063911/article/details/81364947