OPPO面试真题(java)

1.常见的分布式事务解决方案

        在分布式系统中,由于数据存储在多个节点上,需要保证数据操作的一致性和原子性,这就涉及到分布式事务的处理。以下是一些常见的分布式事务解决方案:

  1. 两阶段提交(2PC):2PC是一种经典的分布式事务协议,它包括一个协调者和多个参与者。在2PC中,协调者负责协调所有参与者的事务操作,并确保所有节点都同意提交事务。这种方法的缺点是存在阻塞和单点故障的问题。

  2. 三阶段提交(3PC):3PC是对2PC的改进,引入了预提交阶段。在3PC中,协调者会先向参与者发送预提交请求,并等待参与者的确认。如果确认成功,则进行正式提交;否则进行回滚。3PC相对于2PC减少了阻塞的时间窗口,但仍然存在单点故障的问题。

  3. TCC(Try-Confirm-Cancel):TCC是一种基于补偿机制的分布式事务解决方案。在TCC中,每个参与者实现了Try、Confirm和Cancel三个操作,通过预留资源和回滚操作来实现事务的一致性。TCC的优点是灵活性高,可以根据具体业务场景定义补偿逻辑,但需要开发人员自行实现事务的补偿逻辑。

  4. 本地消息表(Local Message Table):本地消息表是一种常见的异步补偿方案。在这种方案中,事务操作被拆分为两个步骤:将事务操作写入本地数据库的消息表中,并发送消息到消息队列;然后由消费者从消息队列中读取消息,并执行实际的事务操作。通过消息表的记录和消息队列的保证,可以实现事务的最终一致性。

  5. 基于可靠消息的最终一致性:在分布式系统中,可以使用可靠消息中间件(如Kafka、RabbitMQ)来实现最终一致性。通过将事务操作封装为消息,将其发送到消息队列中,并由消费者异步处理消息,可以实现事务的最终一致性。这种方案适用于业务允许一定程度的异步和延迟的场景。

  6. 分布式事务中间件:有一些开源的分布式事务中间件,如Seata、XA-based方案等,提供了更为完整和方便的分布式事务管理。这些中间件可以集成到应用程序中,提供分布式事务的协调和控制。

2.接口幂等是如何保证的

        接口的幂等性是指对同一请求的多次执行所产生的结果和对一次执行的结果是一致的。保证接口的幂等性是很重要的,特别是在分布式系统和并发环境中。

以下是几种常见的方法来保证接口的幂等性:

  1. 唯一标识符:在每次请求中使用唯一标识符(例如UUID)作为请求的一部分。服务端在处理请求时,可以检查该标识符是否已经被处理过,如果已经处理过,则可以避免重复处理。

  2. 幂等接口设计:设计接口时,可以采用幂等的操作,即使接口被多次调用也不会产生副作用。例如,查询操作和创建操作可以设计成幂等的,而更新和删除操作通常不是幂等的。

  3. 乐观锁:在数据库层面,可以使用乐观锁来实现幂等性。通过给每条数据添加一个版本号或时间戳,并在更新时比较当前版本号或时间戳与请求中的版本号或时间戳,如果一致,则执行更新操作,否则拒绝更新。

  4. Token机制:在每次请求中携带一个唯一的Token,并将该Token保存在服务端。服务端在处理请求时,先验证Token的有效性,如果有效,则处理请求并删除Token,如果无效,则拒绝重复请求。

  5. 分布式锁:使用分布式锁机制可以确保同一时刻只有一个请求能够执行关键操作,避免并发操作引起的重复执行。

  6. 记录请求状态:在服务端记录每次请求的处理状态,可以通过查询请求状态来判断是否已经处理过,从而避免重复处理。

3.常见的索引结构有哪些?哈希表结构属于那种场景?

常见的索引结构包括:

  1. B树(B-tree):B树是一种自平衡的搜索树,常用于数据库和文件系统中的索引结构。B树适合处理范围查询,并且能够高效地支持插入和删除操作。

  2. B+树(B+ tree):B+树是在B树的基础上进行优化的一种索引结构。与B树不同的是,B+树的内部节点不存储数据,只存储键值,而数据都存储在叶子节点中。B+树适合用于范围查询和顺序访问。

  3. 哈希索引(Hash Index):哈希索引使用哈希表的数据结构,将键值与对应的数据项关联起来。哈希索引适合用于等值查询,通过哈希函数将键值映射到索引桶中,并直接访问对应的数据项。然而,哈希索引不适合范围查询和顺序访问。

  4. 全文索引(Full-Text Index):全文索引用于对文本内容进行搜索,支持关键词的匹配和模糊查询。全文索引常用于文本搜索引擎和内容管理系统等应用中。

  5. 倒排索引(Inverted Index):倒排索引用于文本搜索,将每个单词与包含该单词的文档关联起来。倒排索引适用于高效地执行文本搜索和检索操作。

        哈希表结构适用于需要快速的等值查询的场景。由于哈希表使用哈希函数直接计算键值的存储位置,可以实现O(1)的平均时间复杂度进行查询。因此,当需要快速的键值对查找操作而不需要范围查询或顺序访问时,哈希表结构是一个有效的选择。例如,缓存系统中的缓存键值对存储常常使用哈希表结构。但需要注意的是,哈希表结构无法支持范围查询,且对于键的顺序无法保证。

4.给你ab,ac,abc字段,如何加索引?

        这主要考察联合索引的最左前缀原则知识点。

         这个最左前缀可以是联合索引的最左N个字段。比如组合索引(a,b,c)可以相当于建了(a),(a,b),(a,b,c)三个索引,大大提高了索引复用能力。最左前缀也可以是字符串索引的最左M个字符。

因此给你ab,ac,abc字段,你可以直接加abc联合索引和ac联合索引即可。

5.数据库隔离级别,数据库默认的隔离级别以及为什么选它

        数据库的隔离级别是指多个并发事务之间的隔离程度,用于控制并发事务之间的相互影响。常见的数据库隔离级别包括:

  1. 读未提交(Read Uncommitted):最低的隔离级别,事务可以读取其他未提交的事务所做的修改,可能导致脏读、不可重复读和幻读的问题。

  2. 读已提交(Read Committed):事务只能读取已经提交的数据,解决了脏读的问题。但仍可能出现不可重复读和幻读的问题。

  3. 可重复读(Repeatable Read):事务在整个过程中能够重复读取相同的数据,即使其他事务对该数据进行了修改,解决了不可重复读的问题。但仍可能出现幻读的问题。

  4. 序列化(Serializable):最高的隔离级别,通过强制事务串行执行来避免任何并发问题,包括脏读、不可重复读和幻读。但会降低并发性能。

        数据库默认的隔离级别因数据库而异,常见的默认隔离级别是读已提交(Read Committed)。这是因为读已提交可以避免脏读的问题,并且具有较好的并发性能。对于大多数应用场景,读已提交的隔离级别已经足够,可以在保证数据一致性的同时提供较高的并发性能。

        选择隔离级别时需要根据具体的应用需求进行权衡。如果应用对数据的一致性要求较高,可以选择更高的隔离级别,如可重复读或序列化,但可能牺牲一定的并发性能。如果应用对并发性能要求较高,但可以容忍一定的数据不一致,可以选择较低的隔离级别。综合考虑应用需求和业务场景,选择合适的隔离级别可以在数据一致性和并发性能之间找到平衡点。

6.Repeatable Read是如何解决不可重复读的

        在可重复读(Repeatable Read)隔离级别下,数据库使用了一种叫做"行锁"(Row-Level Locking)的机制来解决不可重复读的问题。

        不可重复读是指在同一个事务中,对于相同的查询,多次读取到的数据可能不一致。这是由于在并发环境下,其他事务可以在当前事务读取数据的过程中对数据进行修改或删除。

        在可重复读隔离级别下,当事务开始时,会为所有查询所涉及的数据行加上共享锁。这意味着其他事务可以读取但不能修改这些数据行,从而保证了事务在读取过程中不会被其他事务修改。

        当其他事务要修改数据时,会尝试为相关的数据行加上排他锁。如果当前事务已经读取过这些数据行,那么其他事务无法获取到排他锁,从而阻塞了对数据的修改操作,保证了数据的一致性。

        因此,可重复读隔离级别通过行级锁的机制,确保了在同一个事务中多次读取相同的数据时,得到的结果是一致的,解决了不可重复读的问题。

7.脏读、不可重复读和幻读

        脏读(Dirty Read):脏读是指一个事务读取了另一个事务尚未提交的数据。假设事务A修改了某个数据,但还没有提交,此时事务B读取了这个尚未提交的数据。如果事务A在稍后回滚,那么事务B读取到的数据就是不正确的,即脏数据。

        不可重复读(Non-repeatable Read):不可重复读是指在同一个事务内,多次读取同一数据时,得到的结果不一致。这是由于在并发环境下,其他事务对数据进行了修改或删除,导致事务在多次读取过程中得到了不同的数据。

        幻读(Phantom Read):幻读是指在同一个事务内,多次执行相同的查询,得到的结果集不一致。这是由于在并发环境下,其他事务插入、删除或修改了满足查询条件的数据,导致事务在多次执行查询过程中得到了不同的结果集。

区别:

  • 脏读是指读取了尚未提交的数据,而不可重复读和幻读是指读取到了已经提交的数据。
  • 不可重复读是在同一个事务内,读取同一数据多次得到的结果不一致,而幻读是在同一个事务内,执行相同查询多次得到的结果集不一致。
  • 不可重复读主要是由于其他事务对数据的修改引起的,而幻读主要是由于其他事务对数据的插入或删除引起的。

解决方法:

  • 脏读的解决方法是使用事务的隔离级别,如读已提交或可重复读。
  • 不可重复读的解决方法是使用事务的隔离级别,如可重复读或串行化,并使用行级锁或快照读来保证数据的一致性。
  • 幻读的解决方法是使用事务的隔离级别,如可重复读或串行化,并使用表级锁、间隙锁或多版本并发控制(MVCC)等机制来避免幻读的发生。

8.RocketMQ是如何保证消息不丢失的

        RocketMQ是一款高可靠性、高性能的分布式消息队列系统,它采用了多种机制来保证消息的不丢失:

  1. 持久化存储:RocketMQ将消息持久化到磁盘上,即使在发生宕机或重启的情况下,消息也能够得到保留,不会丢失。

  2. 主从同步复制:RocketMQ支持主从同步复制机制,消息在发送到主节点后,主节点会将消息同步复制到从节点。这样即使主节点宕机,从节点上仍然有备份的消息,保证了消息的可靠性。

  3. 刷盘机制:RocketMQ提供了同步刷盘和异步刷盘两种方式。同步刷盘在消息发送时会等待消息被写入磁盘后才返回成功,而异步刷盘则不等待磁盘写入操作完成。通过合理配置刷盘机制,可以根据应用的需求权衡消息的可靠性和性能。

  4. 内存缓冲:RocketMQ在发送和接收消息时使用了内存缓冲区,通过将消息暂存在内存中,提高了消息的传输速度和吞吐量。即使在网络抖动或瞬时故障的情况下,内存中的消息可以暂时存储,不会立即丢失。

  5. 高可用架构:RocketMQ支持主备架构和分布式部署,通过配置多个Broker节点和多个NameServer节点,实现了高可用性和容错性。即使某个节点发生故障,消息仍然可以在其他可用节点上进行传输和存储,不会丢失。

        综上所述,RocketMQ通过持久化存储、主从同步复制、刷盘机制、内存缓冲和高可用架构等多种机制,确保了消息在传输和存储过程中的可靠性,尽可能地避免消息丢失。但需要注意的是,完全的消息不丢失是很难保证的,对于一些极端情况或者特定配置下,仍然存在潜在的消息丢失的可能性,因此在应用开发中需要根据业务需求和实际情况来选择合适的配置和机制。

9.Spring声明式事务原理,那些场景事务会失效?

        Spring声明式事务是通过AOP(Aspect-Oriented Programming)实现的,主要依赖于Spring的事务管理器和代理对象来实现事务的控制。

原理如下:

  1. 在Spring配置文件中配置事务管理器(例如DataSourceTransactionManager)。
  2. 在需要进行事务管理的方法上添加事务注解(例如@Transactional)。
  3. 使用AOP技术,在方法执行前后织入事务管理的逻辑。
  4. 当方法被调用时,Spring会在方法执行前开启一个事务,执行方法体,如果方法执行成功则提交事务,如果方法抛出异常则回滚事务。

        然而,有些场景下声明式事务可能会失效,导致事务不起作用,主要包括以下情况:

  1. 事务方法被private、final、static或在同一个类中调用,因为Spring事务基于动态代理实现,只能拦截公开的方法调用。

  2. 异常没有被正确抛出,被捕获并处理了,导致事务管理器无法捕获到异常从而无法触发回滚操作。

  3. 事务注解被应用在非公开方法上或者被非Spring管理的Bean调用,因为只有被Spring管理的Bean才会通过代理对象来执行事务逻辑。

  4. 事务注解被应用在方法内部调用的另一个方法上,因为AOP只能拦截外部方法的调用,无法拦截内部方法的调用。

  5. 异步方法调用,事务注解无法在异步方法中生效,需要通过其他手段来管理异步方法的事务。

  6. 数据库引擎不支持事务,例如某些NoSQL数据库可能不支持事务,或者事务隔离级别设置不正确,导致事务失效。

  7. 使用了其他框架的事务管理器,例如JTA(Java Transaction API)等,可能与Spring的事务管理器冲突导致事务失效。

10.怎么分库分表?分布式ID如何生成?

        分库分表是一种常用的数据库水平扩展方式,用于解决单个数据库的存储容量和性能瓶颈问题。分库分表将一个大的数据库拆分成多个较小的数据库实例(分库),并将表按照某种规则分散到多个数据库实例中(分表)。这样可以将数据存储和查询的负载均衡到多个数据库实例上,提高系统的性能和可伸缩性。

以下是一些常用的分库分表策略:

  1. 垂直分库:按照业务模块或功能将数据库中的表拆分到不同的数据库实例中。例如,将用户信息表、订单信息表等分别存储在不同的数据库中。

  2. 水平分库:按照某个分片键(如用户ID、订单ID)将数据均匀地分散到不同的数据库实例中。例如,根据用户ID的哈希值将用户数据分散到不同的数据库。

  3. 水平分表:按照某个分片键(如用户ID、订单ID)将数据均匀地分散到同一个数据库实例的不同表中。例如,根据用户ID的哈希值将用户数据分散到不同的表。

        分布式ID生成是在分布式系统中生成唯一标识符的一种技术。在分库分表的场景下,生成唯一的ID用于作为数据的主键,确保在整个分布式系统中的数据唯一性。

常见的分布式ID生成策略包括:

  1. UUID(Universally Unique Identifier):使用随机算法生成的全局唯一标识符。UUID具有足够的随机性,但生成的ID较长,不适合作为数据库的主键。

  2. Snowflake算法:Snowflake是Twitter开源的一种分布式ID生成算法。它使用一个64位的长整型来表示生成的ID,其中包含了时间戳、机器ID、数据中心ID和自增序列等信息,保证了生成的ID在分布式系统中的唯一性和有序性。

  3. 数据库自增主键:可以使用数据库自增主键来生成唯一ID。在分库分表的情况下,每个数据库实例维护自己的自增序列,避免了全局的ID冲突问题。

  4. 第三方分布式ID生成器:使用一些第三方的分布式ID生成器,如雪花算法的实现库或者其他开源工具,可以方便地生成唯一ID。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值