京东实习后端面经,被问麻了...
1.mysql联合索引失效
MySQL联合索引失效通常发生在以下情况:
-
未使用最左前缀原则: 联合索引的最左前缀原则指的是在查询中使用索引的时候,必须从联合索引的最左边列开始使用。如果查询不按照最左前缀的顺序使用索引,MySQL 可能无法充分利用这个索引。
例如,如果有一个联合索引 (col1, col2),而你的查询中只使用了 col2,那么这个索引就不会被用到。
-
不符合索引顺序的查询: 联合索引只在查询中按照索引的顺序使用时才会生效。如果你的查询中的条件不按照索引的顺序,MySQL 可能无法使用这个索引。
例如,如果有一个联合索引 (col1, col2),而你的查询中使用了 col2,col1 进行筛选,那么这个索引也可能无法生效。
-
数据分布不均匀: 如果索引列的数据分布不均匀,即某些值的出现频率远高于其他值,那么 MySQL 可能会选择不使用索引,而是进行全表扫描。
-
数据类型不匹配: 联合索引的列如果包含了不同的数据类型,可能会导致索引失效。例如,如果一个列是字符串类型,另一个列是数字类型,MySQL 在执行查询时可能无法有效使用这个索引。
-
使用了函数或运算符: 如果在查询条件中使用了函数或运算符,可能会导致索引失效。MySQL 无法直接在索引上执行函数或运算符,因此可能会选择不使用索引。
为了避免这些问题,设计索引时应根据实际查询需求考虑最佳的索引顺序,并确保查询条件与索引一致。同时,确保索引列的数据类型和函数的使用方式符合索引的要求。在优化查询性能时,可以使用 EXPLAIN
来查看 MySQL 的执行计划,以确定是否正确使用了索引。
2.索引底层
MySQL索引的底层实现主要依赖于B+树和哈希索引两种结构。
- B+树索引:
- B+树结构: B+树是B树的一种变体,它在B树的基础上进行了优化。在B+树中,只有叶子节点存储实际的数据,而非叶子节点仅包含键值信息用于导航。这样的结构使得范围查询更加高效。InnoDB使用的索引即为B+树索引。
- 哈希索引:
- 哈希结构: 哈希索引是将键的值通过哈希函数转换成一个固定大小的哈希值,然后将哈希值与存储实际数据的位置相对应。这种结构使得单个键的查找效率很高,但不支持范围查询。InnoDB不直接支持哈希索引,但可以通过Memory存储引擎来创建哈希索引。
在InnoDB存储引擎中,使用的是B+树索引。每个InnoDB表都有一个聚簇索引(clustered index),这是数据行的物理排序。聚簇索引在InnoDB中实际上就是一颗B+树,因此表的数据行实际上是按照聚簇索引的排序存储的。
此外,InnoDB中的辅助索引也是B+树结构,这些辅助索引的叶子节点包含了指向聚簇索引中对应行的引用。
MySQL通过使用不同的索引结构,使得它在处理各种类型的查询和数据操作时能够取得较好的性能。选择适当的索引结构取决于具体的应用场景和查询需求。
3.说说分库分表
分库分表是一种数据库水平拆分的策略,旨在应对数据库规模不断增长导致的性能瓶颈和存储容量限制。这种策略将一个大型数据库拆分成多个较小的数据库实例(分库)和表(分表),从而提高并行处理能力、降低单个数据库实例的负载,提升整体系统的性能和可伸缩性。
以下是关于分库分表的一些要点:
-
水平拆分: 分库分表是水平拆分的一种形式,与垂直拆分(按照业务模块划分表)不同。水平拆分指的是将一个表中的数据按照某个规则分散存储在多个数据库实例或表中。
-
分库: 将原来的单个数据库拆分成多个独立的数据库。每个数据库实例都可以独立运行在不同的物理服务器上,从而提高并发处理能力。分库通常采用数据划分的规则,比如按照用户ID的取模或哈希值进行分库。
-
分表: 在每个数据库中,原来的大表被拆分成多个较小的表。分表的常见策略包括按照时间范围、按照业务模块或按照数据范围等。分表可以减小每个表的数据量,提高查询性能。
-
数据一致性: 在分库分表的架构中,需要考虑数据一致性的问题。因为数据分散在不同的数据库和表中,应用程序需要确保对跨库、跨表的事务有良好的处理机制。
-
全局唯一标识: 在分库分表的情况下,通常需要一个全局唯一的标识符,以便在多个数据库和表中唯一标识一条记录。这可以是通过分布式ID生成器、UUID等方式实现的。
-
路由策略: 应用程序需要实现合适的路由策略,确保查询时能够正确定位到需要的数据库和表。路由策略可以基于数据划分的规则,例如用户ID的哈希值。
-
动态扩展: 分库分表的优势之一是能够相对容易地进行水平扩展。当数据量增加时,可以通过增加新的数据库实例或表来进行扩展,而不必改变整体系统架构。
分库分表的实现需要谨慎考虑业务需求、数据一致性、查询性能和扩展性等因素。这样的架构适用于大型、高并发的应用,但也带来了管理和维护的挑战。
4.mysql事务
MySQL事务和InnoDB引擎的实现方式涉及到ACID属性(原子性、一致性、隔离性、持久性)的保障。
MySQL事务:
-
事务的开始和结束: 事务是由
BEGIN
、COMMIT
和ROLLBACK
语句来标识的。BEGIN
标志事务的开始,COMMIT
标志事务的结束并提交,而ROLLBACK
则用于事务的回滚。 -
隔离级别: MySQL支持不同的事务隔离级别,包括READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ和SERIALIZABLE。这些级别决定了在事务执行过程中,对数据的读写是否会受到其他事务的影响。
InnoDB引擎:
-
事务日志(Redo Log): InnoDB引擎使用事务日志(redo log)来实现事务的持久性。当事务对数据库进行更改时,这些更改首先被写入事务日志,然后再写入磁盘上的数据页。这样即使在事务提交之前系统崩溃,通过重放事务日志,可以将数据库恢复到崩溃前的状态。
-
两阶段提交(Two-Phase Commit): InnoDB引擎使用两阶段提交来确保事务的原子性。在第一阶段,事务的更改被写入事务日志,但尚未将其应用到磁盘上的数据页。在第二阶段,如果事务成功完成,所有的更改都被写入磁盘;如果事务失败,所有的更改都会被回滚。
-
MVCC(多版本并发控制): InnoDB使用MVCC来提供不同的事务隔离级别。MVCC通过在每行数据上维护多个版本,使得事务可以在不互相干扰的情况下并发执行。每个事务可以看到数据库在它开始之前已经提交的版本,但不会看到其他事务正在执行的版本。
-
行级锁: InnoDB引擎支持行级锁,这意味着事务在修改数据时只锁定涉及的行,而不是锁定整个表。这提高了并发性,减少了锁竞争,但也引入了一些额外的开销。
总体来说,InnoDB引擎通过事务日志、两阶段提交、MVCC和行级锁等机制,实现了对事务的可靠支持,保证了ACID属性的满足。这使得InnoDB成为MySQL中常用的事务型存储引擎。
5.MQ幂等性
消息队列(MQ)中的幂等性是指无论消息被消费多少次,最终的效果应该是一致的。在分布式系统中,由于网络延迟、重试机制等原因,消息可能被处理多次,而幂等性可以确保系统的一致性。
实现MQ幂等性的方法:
-
唯一标识符: 每条消息都应该包含一个唯一的标识符(Message ID),该标识符可以用来检测消息的重复。当消息处理完成后,可以记录已处理的消息标识符,下次收到相同标识符的消息时,直接判断为重复消息,不再处理。
-
幂等性检测逻辑: 在消息的处理逻辑中加入幂等性检测的逻辑,确保同一消息在多次处理时不会产生不一致的结果。例如,在数据库中存储已处理消息的信息,每次处理消息前检查是否已经处理过。
-
版本号: 每个消息可以包含一个版本号,表示消息的版本信息。消费者在处理消息时,可以检查消息的版本号,如果版本号相同,即使消息重复到达,也可以认为是幂等的。
-
幂等性接口设计: 对于消息的消费者,可以设计幂等性接口,确保相同输入产生相同的输出。通过合理设计接口,消费者可以在处理相同消息时返回相同结果,保证了幂等性。
-
事务性操作: 如果消息的处理涉及到事务性操作,可以利用数据库事务的特性来保证幂等性。通过将消息的处理和数据库的更新放在同一个事务中,可以确保即使消息多次到达,数据库的状态也能正确更新。
-
分布式锁: 使用分布式锁机制,确保在同一时间只有一个消费者能够处理消息。这可以避免多个消费者同时处理同一消息,从而保证幂等性。
实现MQ的幂等性需要结合业务场景和具体的消息处理逻辑,采用合适的方式来防止重复处理相同的消息。选择合适的幂等性策略可以提高系统的可靠性和一致性。
6.了解分布式吗
分布式系统是由多台计算机(或节点)协同工作,共同完成某个任务或提供某种服务。分布式系统常用于处理大规模数据、提高系统的可扩展性和可靠性。以下是一些常用的分布式技术和框架:
-
分布式存储系统:
- Hadoop Distributed File System (HDFS): 用于存储和处理大规模数据的分布式文件系统,是Apache Hadoop的核心组件。
- Amazon S3: 亚马逊提供的分布式对象存储服务,适用于在云环境中存储和检索数据。
-
分布式计算框架:
- Apache Spark: 用于大规模数据处理的分布式计算框架,支持内存计算,适用于迭代式算法和复杂的数据流处理。
- Apache Flink: 分布式流处理引擎,支持事件时间处理和状态管理,适用于实时数据处理和分析。
-
分布式消息队列:
- Apache Kafka: 高吞吐量的分布式消息系统,用于构建实时数据流平台,支持发布/订阅模型。
- RabbitMQ: 开源的消息代理,实现了高级消息队列协议(AMQP),用于在分布式系统中传递消息。
-
分布式数据库:
- Apache Cassandra: 高度可扩展的分布式 NoSQL 数据库,适用于处理大规模的分布式数据。
- MongoDB: 非关系型的分布式数据库,支持水平扩展,适用于处理大量文档型数据。
-
分布式缓存:
- Redis: 高性能的内存键值存储系统,用于缓存和数据存储。
- Memcached: 分布式内存对象缓存系统,用于加速动态Web应用程序。
-
容器与编排:
- Docker: 轻量级容器技术,用于打包、分发和运行应用程序。
- Kubernetes: 开源的容器编排引擎,用于自动化容器的部署、扩展和管理。
-
分布式事务:
- XA协议: 提供了一种分布式事务的协议,可以在不同数据库管理系统之间确保事务的一致性。
- TCC(Try-Confirm-Cancel): 一种补偿型分布式事务的设计模式,通过预留资源、确认和取消操作来保证事务的一致性。
-
服务发现与治理:
- Consul: 分布式服务发现和治理工具,用于服务注册、健康检查和动态路由。
- etcd: 分布式键值存储系统,常用于服务发现和配置管理。
这些技术和框架在不同领域解决了分布式系统面临的各种问题,提供了可靠的基础设施和工具,帮助开发者构建高性能、可扩展和可靠的分布式应用。
7.spring类如何使用的
Spring框架提供了众多的类和功能,用于开发各种类型的应用程序,包括依赖注入、AOP(面向切面编程)、事务管理、数据访问、Web开发等。以下是Spring中一些核心类的使用方式:
-
ApplicationContext: ApplicationContext 是 Spring 中用于管理Bean的上下文对象。它可以通过配置文件或Java配置类加载并管理应用程序中的所有Bean。
// 通过XML配置文件加载ApplicationContext ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); // 或者通过Java配置类加载ApplicationContext AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); // 通过ApplicationContext获取Bean MyService myService = context.getBean(MyService.class);
-
Bean: 在Spring中,Bean是由Spring容器管理的对象。通过配置文件或注解标记类为Bean,Spring容器负责实例化、配置和管理Bean。
// 在XML配置文件中定义Bean <bean id="myBean" class="com.example.MyBean"/> // 在Java配置类中定义Bean @Configuration public class AppConfig { @Bean public MyBean myBean() { return new MyBean(); } } // 使用@Component注解定义Bean @Component public class MyComponent { // ... }
-
依赖注入: Spring通过依赖注入来管理对象之间的关系。可以通过构造函数、setter方法或字段注入的方式将依赖关系注入到Bean中。
// 通过构造函数注入 public class MyClass { private MyDependency myDependency; public MyClass(MyDependency myDependency) { this.myDependency = myDependency; } } // 通过setter方法注入 public class MyClass { private MyDependency myDependency; public void setMyDependency(MyDependency myDependency) { this.myDependency = myDependency; } }
-
AOP(面向切面编程): Spring的AOP模块提供了面向切面编程的能力,通过定义切面和切点,可以在应用程序中插入横切关注点。
// 定义切面 @Aspect public class MyAspect { @Before("execution(* com.example.MyService.*(..))") public void beforeMethod() { // 在方法执行前执行的逻辑 } }
-
事务管理: Spring提供了对声明式事务的支持,通过配置可以简化事务管理的过程。
// 在XML配置文件中声明事务 <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="*" propagation="REQUIRED" /> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="serviceMethods" expression="execution(* com.example.*Service.*(..))" /> <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethods" /> </aop:config>
以上是一些Spring中核心类的基本使用方式。Spring框架提供了广泛的文档和示例,可以根据具体应用场景和需求深入学习和使用不同的功能。Spring框架提供了众多的类和功能,用于开发各种类型的应用程序,包括依赖注入、AOP(面向切面编程)、事务管理、数据访问、Web开发等。以下是Spring中一些核心类的使用方式:
-
ApplicationContext: ApplicationContext 是 Spring 中用于管理Bean的上下文对象。它可以通过配置文件或Java配置类加载并管理应用程序中的所有Bean。
// 通过XML配置文件加载ApplicationContext ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); // 或者通过Java配置类加载ApplicationContext AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); // 通过ApplicationContext获取Bean MyService myService = context.getBean(MyService.class);
-
Bean: 在Spring中,Bean是由Spring容器管理的对象。通过配置文件或注解标记类为Bean,Spring容器负责实例化、配置和管理Bean。
// 在XML配置文件中定义Bean <bean id="myBean" class="com.example.MyBean"/> // 在Java配置类中定义Bean @Configuration public class AppConfig { @Bean public MyBean myBean() { return new MyBean(); } } // 使用@Component注解定义Bean @Component public class MyComponent { // ... }
-
依赖注入: Spring通过依赖注入来管理对象之间的关系。可以通过构造函数、setter方法或字段注入的方式将依赖关系注入到Bean中。
// 通过构造函数注入 public class MyClass { private MyDependency myDependency; public MyClass(MyDependency myDependency) { this.myDependency = myDependency; } } // 通过setter方法注入 public class MyClass { private MyDependency myDependency; public void setMyDependency(MyDependency myDependency) { this.myDependency = myDependency; } }
-
AOP(面向切面编程): Spring的AOP模块提供了面向切面编程的能力,通过定义切面和切点,可以在应用程序中插入横切关注点。
// 定义切面 @Aspect public class MyAspect { @Before("execution(* com.example.MyService.*(..))") public void beforeMethod() { // 在方法执行前执行的逻辑 } }
-
事务管理: Spring提供了对声明式事务的支持,通过配置可以简化事务管理的过程。
// 在XML配置文件中声明事务 <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="*" propagation="REQUIRED" /> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="serviceMethods" expression="execution(* com.example.*Service.*(..))" /> <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethods" /> </aop:config>
收录各个网友分享的各个公司的面经,并给出答案。