问题记录01
1、MySQL事务的底层原理
MySQL事务的原理主要包括事务的ACID特性(原子性、一致性、隔离性、持久性)的实现机制,以及MySQL存储引擎(如InnoDB)对事务的具体支持。
(1)undo log
- undo log(回滚日志)用于实现事务的原子性。它记录了数据在每个操作前的状态,以便在需要回滚时能够撤销这些操作。undo log的存储不同于redo log,它存放在数据库内部的一个特殊的段(segment)中,这个段称为回滚段。
(2)redo log
- redo log(重做日志)用于实现事务的持久性。它记录了数据在每个操作后的状态,以便在系统崩溃时能够恢复数据。redo log是物理日志,记录的是数据库页的物理修改操作。为了保证redo log的持久性,MySQL在写入redo log时,必须进行一次操作系统的fsync操作,防止redo log只是写入了操作系统的磁盘缓存中。
(3)MVCC(多版本并发控制)
- MVCC是InnoDB存储引擎实现事务隔离性的一种机制。它允许并发事务读取数据而不会产生冲突,因为每个事务都可以读取到数据的某个历史版本。MVCC通过数据行中的隐藏字段(如trx_id和roll_pointer)来实现。trx_id记录了最近修改该数据行的事务ID,而roll_pointer则指向了该数据行的上一个版本。
(4)锁机制
- 除了MVCC外,InnoDB还使用锁机制来确保事务的隔离性。锁机制可以防止并发事务之间的冲突,确保事务按顺序执行。InnoDB支持多种锁类型,如行锁、表锁等。
2、MySQL的锁有哪些?详细说下间隙锁
MySQL锁主要可以分为以下几类:
- 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。主要应用场景是MyISAM、MEMORY、MERGE等存储引擎。
- 行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。InnoDB存储引擎的锁机制就是通过行级锁来实现的。
- 页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。
其中,InnoDB存储引擎支持行级锁,行级锁又可以细分为以下几种:
- 记录锁(Record Lock):锁定索引记录。
- 间隙锁(Gap Lock):锁定索引记录之间的间隙,防止幻读。
- Next-Key Lock:记录锁和间隙锁的组合,既锁定索引记录,又锁定索引记录之间的间隙。
间隙锁通过锁定一个范围内的“间隙”来防止其他事务在该范围内插入新记录,从而避免幻读。
间隙锁锁定的是记录之间的间隙,而不是具体的记录。例如,如果表中有记录1、2、4、5,那么间隙锁可能锁定(2,4)之间的间隙。当一个事务执行范围查询时,MySQL会在查询的范围内对记录和记录之间的间隙都进行加锁。这样可以确保其他事务无法在这个范围内插入新记录,从而防止幻读等并发问题。
间隙锁是Next-Key Locks的一种特殊形式,同时锁定记录和记录之间的间隙。在执行范围查询或范围删除时,InnoDB会使用间隙锁来锁定查询或删除范围内的间隙。
间隙锁在MySQL的REPEATABLE READ(可重复读)和SERIALIZABLE(串行化)隔离级别下生效。
间隙锁主要用于处理并发事务中的范围查询,尤其是涉及到索引的INSERT、UPDATE、DELETE操作。通过锁定索引记录之间的间隙,间隙锁可以确保在事务执行期间,其他事务无法在这个范围内插入新的记录,从而维护了事务隔离性,特别是对于可重复读(Repeatable Read)隔离级别。
3、加事务和不加事务,Mybatis获取SqlSessionFactory的时机有什么区别?
在Mybatis中,无论是否加事务,获取SqlSessionFactory
的时机通常是一致的。这是因为SqlSessionFactory
的获取与事务的管理是两个相对独立的过程。通常通过读取Mybatis的全局配置文件(如mybatis-config.xml
)来创建SqlSessionFactory
。这个配置文件包含了数据库连接信息、事务管理器配置、映射文件位置等重要信息。SqlSessionFactory
实例在整个应用程序生命周期中通常是单例的。
在Mybatis中,事务的开启通常是通过SqlSession
对象来实现的。
- 加事务:在加事务的情况下,Mybatis会确保在同一个
SqlSession
对象上下文中执行的所有SQL语句都遵循ACID特性(原子性、一致性、隔离性、持久性)。如果事务中的任何一条SQL语句执行失败,那么整个事务都会回滚,以确保数据库状态的一致性。 - 不加事务:在不加事务的情况下,每条SQL语句都会在自己的
SqlSession
对象上下文中独立执行。这意味着如果一条SQL语句执行失败,它不会影响到其他SQL语句的执行。但是,这也可能导致数据不一致的问题,因为不同的SQL语句可能会在不同的时间点上对数据库进行修改。
4、Java业务代码里使用事务需要考虑哪些问题?
- 是使用事务管理器显式的提交事务 还是 通过注解(如
@Transactional
)在方法级别管理事务。 - 确保在事务方法内部捕获并适当处理异常,以便在必要时回滚事务。
- 明确事务回滚策略,在代码中明确哪些异常会导致事务回滚,并编写相应的回滚逻辑。
- 选择适当的事务传播行为,还有避免事务嵌套过深。
- 确保所有需要原子性操作的数据库操作都在同一个事务中。
- 确保事务不会长时间占用资源,及时提交或回滚。
- 为事务设置合理的超时时间,以防止事务因长时间未提交而阻塞系统资源。
5、MySQL分表后如何进行分页查询?
(1)如果分表是基于全局唯一ID或时间戳等字段进行的,你可以利用这些字段来定位需要查询的分表。计算出需要查询的ID范围或时间范围,根据这个范围去每个分表中查询符合条件的记录,然后合并取出所需条数。
(2)业务层对每个分表进行多次查询,每次查询都带上一个偏移量(offset)和限制(limit),直到获取到足够数量的记录。然后合并。
(3)如果你使用的是数据库中间件(如MyCAT、ShardingSphere等)或数据库自带的分片方案,它们通常提供了对分页查询的支持。中间件会自动将查询请求路由到正确的分表。
6、哨兵模式和cluster模式区别?
哨兵模式:
- 当主节点出现故障时,会自动进行故障转移,选举一个新的主节点来接替原主节点的工作。
- 所有的写操作都需要通过主节点。不提供数据分片功能,所有的数据都存储在主节点上,从节点仅用于故障转移时提供服务。
- 配置相对简单,但需要额外的资源来启动哨兵进程。
- 适合小规模部署中小型应用,扩展性也有限。
Cluster模式:
- 如果某个节点出现故障,Cluster模式会自动将故障节点的数据迁移到其他正常节点上。
- 支持数据分片,将数据分散存储在多个节点上。通过哈希槽(slots)来分配数据,使得数据的分片和路由是自动的,不需要手动管理数据分片。
- 配置更为复杂,需要设置多个节点,并处理节点之间的通信和数据同步。
- 适用于大规模部署大型应用,能够处理更大量级的数据和请求。
7、Redis的AOF一定不丢数据吗?
并不能保证在所有情况下都不丢失数据
- 并不是每次写操作都立即同步到磁盘,而是先写入AOF缓冲区,再由操作系统决定何时将其写入磁盘。这种异步写入机制可以提高Redis的写入性能,但也会增加数据丢失的风险。
- 如果在AOF文件同步到磁盘之前,Redis服务器崩溃或断电,那么缓冲区中的写操作可能会丢失。
- 在某些极端情况下,AOF文件可能会因为磁盘故障、文件系统错误等原因而损坏。如果AOF文件损坏,那么在Redis重启时可能无法正确加载和恢复数据。
- 如果Redis服务器所在的操作系统或硬件发生故障,如磁盘损坏、电源故障等,那么即使启用了AOF持久化,也可能无法避免数据丢失。
8、Dubbo是在提供端还是消费端做负载均衡的?
Dubbo的负载均衡是在服务消费端的代理层实现的。有轮询、随机、最少活跃调用数、一致性hash等负载均衡策略。
一致性哈希:
- 根据请求的参数(通常是第一个参数)生成一个hash值,然后在环形hash空间中找到离该hash值最近的节点(即服务提供者)进行调用。
- 这种策略使得相同参数的请求总是落在同一台机器上,有利于缓存机制的使用。
- 当某个服务提供者崩溃时,原本发往该服务提供者的请求会基于虚拟节点平摊到其他服务提供者上,不会引起剧烈变动。
9、Mybatis的拦截器
它允许开发者在MyBatis执行SQL语句的某些关键节点进行拦截,并插入自定义的行为。
(1)作用:
- 拦截器是AOP(面向切面编程)在MyBatis中的具体实现,通过拦截器可以实现对MyBatis执行流程的定制和扩展。
- 拦截器的主要作用包括SQL语句的修改、性能监控、权限校验、日志记录等。
(2)原理:
- MyBatis拦截器通过实现
org.apache.ibatis.plugin.Interceptor
接口,并重写其intercept
、plugin
和setProperties
方法来实现自定义逻辑。 - 当MyBatis初始化时,它会读取配置文件中的
<plugins>
配置,加载并实例化所有声明的拦截器。 - 每个拦截器都会被包装进
Plugin
对象中,并通过Plugin.wrap()
方法与目标对象(如Executor
、ParameterHandler
、ResultSetHandler
、StatementHandler
等)连接起来。 - 当执行SQL语句时,请求会通过拦截器链传递,每个拦截器的
intercept
方法都会被调用。如果intercept
方法返回结果,则请求不再传递给下一个拦截器或目标对象;如果没有返回结果,请求会继续传递给下一个拦截器或目标对象。
(3)四大核心对象(Executor、ParameterHandler、ResultSetHandler、StatementHandler)
- Executor:拦截执行器的方法,可以在SQL执行前后添加逻辑,比如缓存的逻辑,在查询语句执行前后检查和添加缓存。
- ParameterHandler:拦截参数的处理,可以在SQL参数绑定前后进行操作,适用于复杂的参数处理逻辑,比如加密/解密数据,或者对特殊的参数格式进行处理。
- ResultSetHandler:拦截结果集的处理,支持在结果集映射过程中插入自定义逻辑,比如结果集的加工处理、性能统计等。
- StatementHandler:拦截SQL语法构建的处理,可以在SQL语句被发送到数据库执行前进行自定义操作,比如修改原始SQL语句、设置特殊的Statement属性等。
(4)实现步骤:
- 创建拦截器类:实现
Interceptor
接口,并重写intercept
、plugin
和setProperties
方法。 - 实现自定义逻辑:在
intercept
方法中编写拦截逻辑,可以通过Invocation
对象获取目标方法的参数、代理对象等信息,从而修改SQL或执行其他操作。 - 配置拦截器:在MyBatis的配置文件中或通过配置类注册拦截器,使用
<plugins>
标签将拦截器配置到<configuration>
标签中。
(5)应用场景:
- 通用分页插件:通过拦截SQL语句,在查询语句中添加分页参数,实现分页功能。
- 动态SQL拼接:根据条件动态拼接SQL语句,实现灵活的查询功能。
- SQL审计:记录SQL的执行日志,包括执行时间、参数、返回结果等,用于SQL审计和性能监控。