MQ出现消息乱序了如何解决?

典型回答 消息中间件如Kafka、RocketMQ等,普通的消息是有可能存在乱序的,比如说因为网络延迟导致某个消息发送晚了,因为系统异常导致第一个消息处理失败了,等等原因都可能会导致消息乱序 举个简单的例子,一次下单过程中,有一个支付消息、一个发货消息。按理说支付一定在发货之前。所以消息的顺序也是先处理支付消息、再处理发货消息。但是对于一些特殊的业务,比如那种虚拟商品,可能支付后马上就自动发货了。这时候如果有一点点网络延迟,就可能导致发货消息优先于支付消息投递。这就是所谓的消息乱序。 一般来说,消息乱序会导致系统处理异常,比如A消息你还没出来就处理B消息的话可能会失败,也有可能你直接把B消息处理成功了,导致A再来的时候无法处理。等等一系列问题。所以这个乱序的问题是非常关键的。 一般来说,我们有几种办法来解决这个消息乱序的问题。 1、顺序消息。 对于那些明确的有顺序的消息,比如像支付消息和发货消息,这个就可以在发送的时候就用顺序消息的方式发送。把他们按照顺序投递到同一个partition上,利用分区的顺序性保证消息的顺序投递。 ✅RocketMQ如何保证消息的顺序性? ✅Kafka如何实现顺序消费? 同时还需要确保只有一个消费者进行串行消费,这样才能完全避免消息的乱序。 2、前置状态判断 对于上面说的支付消息和发货消息,我们可以在消息体中增加一个前置状态的信息,比如beforeStatus。 作为一个消费支付消息和发货消息的系统,我们可以基于这个beforeStatus来判断和我系统中的当前状态是否一致,如果是一致的,说明我是可以处理的,那么我就处理这个消息,如果是不一致的,那说明我要的消息还没来,那我就把这个消息处理失败,让MQ下次再重投给我。 这个方案有两个要求: 1、消息要能推进单据状态,比如支付消息可以把订单从待支付推进到已支付。 2、消息的状态是单向的,不能出现那种从待支付推进到已支付了,过了一会又变成待支付了。 这样就能通过状态来确保消息的有序。 3、增加序列号 如果无法完全保证发送和处理的顺序,又没有状态来做前置判断,可以在消息中引入序列号,消费端根据序列号重排。例如: 在消息中附加一个递增的序列号。 消费端使用缓冲区缓存收到的消息,根据序列号重新排序后再处理。 缺点是会增加系统复杂度,并且需要设置缓存超时时间来处理丢失的消息。 4、自己实现排序 还有一种方案,其实是对上面第2个和第3个方案的优化。 第二个方案存在一个问题,那就是依赖MQ的重新投递,有可能会导致最终这个消息丢失了,因为一旦长时间无法消费,消息就会不再重投了。而且不断地让MQ重试,也可能会导致消息堆积,并且对系统造成一定的压力。 第三个方案的问题就是需要再内存中维护一个队列,来进行排序,太麻烦了。 那么,我们有一个做法。是这样的流程: 1、接到消息之后,做基本的前置校验,如消息幂等、参数齐全等,如果校验不通过,直接返回失败。 2、一旦消息校验成功,把消息体转成一个内部的事件,这个事件是自己定义的,方便后续解析和处理。 3、把这个事件存到数据库中,状态设置为待处理。 4、如果数据库保存成功,返回消息处理成功。 5、再第四步返回之前,开启异步线程处理这个事件,执行他需要执行的代码,如果成功,则把消息状态设置为已处理。如果没成功,不用改消息状态(或者改为失败也可以),然后在执行次数上+1 6、起一个异步任务,定时扫描事件表中的未成功的事件进行重试。 这么做,就能确保所有的事件我都有存储,并且存储后立刻返回,避免消息重投和堆积。消息存储下来之后,我就可以基于这些消息做排序,以及重试了,如果某个消息处理失败了,也不怕,不断重试即可。当达到了一定次数之后,报警出来人工跟进。 进一步优化 为了减少这个方案的定时任务带来的延迟,我们可以在写入消息表的时候,在redis中存一条记录,业务单号(比如订单号)当作key,然后把存入的消息的主键id当作value存进去。 这样再有消息过来的时候,先正常处理,如果处理成功了,去redis中查一下是不是存在相同业务单号的待处理的消息,有的话,根据存储的主键id查询对应的事件,放线程池中进行处理。

March 22, 2026 · 1 min · santu

MySQL千万级数据量,查询如何做优化?

典型回答 千万级数据量, 听着好像挺大的,但是其实,可以非常明确的告诉大家,对于MySQL来说,单库单表对2000万以内的数据查询,甚至再多一点,5000万以内的数据查询,只要注意一下几点,就可以做到非常高性能的查询,而不需要去做分库分表,也不需要用ES,更不用分布式数据库: 索引优化 索引!一定是最立竿见影的优化手段。有索引和没索引,一个SQL能命中和不能命中索引,性能差异是天差地别的。所以,针对千万级数据量的表,索引的使用是至关重要的。在使用索引时,以下几个方面需要考虑和注意: 1、索引类型选择:大多数情况下我们都是用B+树索引,但是hash索引并不是完全没有可取之处,他在等值查询上面还是非常有优势的。所以,如果你的某一张表,不要做排序、范围查询等,只需要做等值查询的话,是可以考虑使用hash索引的。 ✅InnoDB中的索引类型? 2、联合索引:一个key的索引肯定不如多个key的索引效果要好,对于索引来说,前缀长度越长,效果一般是越好的。所以,对于有很多个查询条件的语句比较多的情况下,可以考虑构建联合索引,把这些相关的key放在一起,这样可以大大提升查询性能。 3、索引覆盖,有了联合索引之后,就可以用到索引覆盖了。索引覆盖非常容易,只需要你写SQL的时候,通过索引的key去查询其他的key就行了。只不过where中的key要遵守最左前缀匹配而已。用了索引覆盖,就可以避免回表,就可以大大的提升查询性能。 ✅什么是索引覆盖、索引下推? 4、避免索引失效。有的时候建立索引并不一定就能用的上索引,很多时候会导致索引失效了,你以为走了索引,但是实际上并没走,所以需要注意。比如一些常见的函数、类型不一致、不遵守最左前缀等等,都是可能会导致索引失效的。一定要通过执行计划来确认是不是走了索引,以及走对了索引。 ✅索引失效的问题是如何排查的,有那些种情况? 5、选择区分度高的字段作为索引。这样查询的效果才会好,但是也不绝对,有些场景也要特殊考虑,还要考虑索引的过滤效果。 ✅区分度不高的字段建索引一定没用吗? 避免多表JOIN 多表的JOIN是非常影响性能的,我们需要尽可能的避免他,可以通过适当的冗余字段来减少join的情况。另外,如果一定要join,那么也要用小表驱动大表,并且用索引作为关联条件。 ✅为什么大厂不建议使用多表join? ✅MySQL 为什么是小表驱动大表,为什么能提高查询性能? 避免使用select * 我们在做基本查询到时候,应该查询那些我们关心的字段,不要图方便直接select *,这样不仅没办法用到索引覆盖,还会因为查询的字段太多导致更多的磁盘IO,导致性能下降。 避免深分页 ✅MySQL单表一千万条数据怎么做分页查询? 降低事务的粒度 有的时候,如果一个事务中干的活太多,就会导致一个长事务,而长事务有很多危害,其中比较典型的就是占用数据库链接。 大家要知道,一个数据库的连接数是有限的,尤其是他给单台服务器分配的连接数更是非常有限的,所以,我们不能浪费这些连接,应该尽可能的降低事务的粒度,减少数据库连接的占用。 使用缓存 对于千万级的数据量的系统来说,上缓存基本上是必不可少的了。针对一些查询情况,可以通过缓存来减少查询的次数,提升性能。 但是不建议大家直接用数据库的缓存和mybatis的缓存,而是建议大家直接用redis这样的分布式缓存。这样我们就可以避免很多黑盒,因为我们可以明确的知道这里有缓存。 数据归档 对于千万级数据量,不可能所有的数据都是热数据,所谓热数据就是会经常查询的数据,比如交易订单,一般6个月前的订单很少有人再去查询他了。 所以我们其实可以针对数据做一些定期的归档 ,把他们从当前表中挪出来,放到历史表中,这样当前的库表数据量就会比较少了,就可以有很好的查询性能了。 硬件提升 对于一个数据库来说,他部署的硬件条件不一样,结果肯定也不一样,你部署在4C8G的服务上,和部署在64C512G的服务上,那一定是有很大的差异的。 所以,我们可以适当的通过提升实例的规格,来提升整体的吞吐量。 其他 以上,都干了,基本上抗个几千万的查询没啥问题,至于更新和插入操作,那就也需要考虑锁、热点行等等问题,这是另外的话题。 如果以上这些都干了,还是解决不了问题,那么再考虑分库分表、以及上搜索引擎。 ✅如果单表数据量大,只能考虑分库分表吗?

March 22, 2026 · 1 min · santu

MySQL单表一千万条数据怎么做分页查询?

典型回答 这其实是一个大数据量查询的问题,一千万级别数据量的表的话,MySQL还是可以扛得住的,即使是单表也问题不大,但是也需要注意查询的性能以及深分页的问题。 所以,这个问题主要是需要从以下几个方面来做思考和优化。 索引使用 首先是查询的方式,首先可以确定的是,我们一定要走索引查询。千万级数据量,如果不走索引的话,那妥妥是个慢SQL了,所以必须要走索引。 在索引上,尽可能的选择唯一索引(包括主键索引),因为唯一性索引的话查询起来会更快一点。 其次的话,如果能用到索引覆盖,尽可能通过索引覆盖来查询,这样就可以避免回表带来的消耗了。 ✅什么是索引覆盖、索引下推? 避免深分页 这个问题中,比较重要的就是一个分页的前提,我们想要做分页,就需要考虑深分页的问题: ✅MySQL的深度分页如何优化 而针对1000万的数据,当我们查询后面的部分页的时候,就会出现深分页的问题,所以我们需要想办法来避免。 比如通过记录上一页的最大ID的方式来实现查询: 1 2 3 4 5 -- 第 1 页 SELECT * FROM large_table WHERE id > 0 ORDER BY id ASC LIMIT 100; -- 第 N 页(假设上一页最后一条数据的 id 为 500) SELECT * FROM large_table WHERE id > 500 ORDER BY id ASC LIMIT 100; 还可以通过子查询来实现分页: 1 2 3 4 5 6 7 8 -- 定位第 N 页起点 SELECT * FROM large_table WHERE id >= ( SELECT id FROM large_table ORDER BY id ASC LIMIT 1000, 1 ) ORDER BY id ASC LIMIT 100; 这样可以避免因OFFSET导致的大量无效扫描(具体原理见上文)。但是这个方案不适合ID不连续的场景。 ...

March 22, 2026 · 1 min · santu

在for循环中调用数据库,有什么缺点?如何优化?

典型回答 很多人应该都见过这种写法,就是在for循环中去不断地调用数据库,其实这么做是不好的,不建议大家这么干,主要有几个问题。 有些数据库的调用方式,会创建数据库链接,然后再关闭数据库链接,如果在for循环中不断地进行数据库调用,就会导致大量的链接创建和关闭,会增加很多额外的网络开销。 就算是共用同一个connection,但是在每次执行CRUD的时候,都需要和数据库做一次网络交互,而这个过程也是有网络开销的,会使得整个方法的耗时比较长。 另外,**如果是写操作的话,在for循环中进行的话,如何做事务管理是个比较大的风险,**如果在方法外加了个整体的大事务,那么for循环执行到最后的时候,如果失败了,导致整体回滚,那么也是有很多成本在的。 所以,应该尽量的避免在for循环中做数据库操作,尤其是写操作。 替代的方式有以下几个,首先如果是读操作,那么可以通过in来代替for循环查询,SELECT*FROMtableWHERE id IN (1, 2, 3, 4); 这样就能通过一条SQL进行查询,只需要和数据库建立一次链接和通信。 还有就是如果是写操作,那么可以借助批量操作的方法,如mybatis中的SqlSession中的batchInsert、batchUpdate等,实现多条数据进行批量的写入和更新。 还有就是其实,很多操作,是可以考虑异步进行的,尤其是一些流水、记账等操作,往往都需要批量执行,这些都可以放到异步链路上去执行。具体的方案可以是通过扫表,或者线程池来实现。

March 22, 2026 · 1 min · santu

什么是数据归档,一般是怎么做的?

典型回答 数据归档是指将**不再频繁使用的历史数据从主数据库(主表)中转移到低成本的存储位置**(如归档表、归档库或离线存储系统),以减轻系统的存储压力,提高数据库的查询性能,并满足数据保留和审计的需求。 因为对于任何一个系统来说,他的数据一定会区分冷、热,因为时间是不断地推移的,很多历史的数据,他就是不太会再去操作他了,而查询的次数也非常有限。所以就可以做数据归档,或者也叫冷热分离。 数据归档的常见做法 是否常见 实现成本 兼顾查询 查询性能 分库分表归档 是 低 是 一般 分区归档 是 低 是 一般 数据备份归档 否 低 否 - 分布式存储归档 是 高 是 高 离线数仓归档 是 高 是 低 1. 分库分表归档(常见) 最常见的归档的方式就是在主库(表)之外,再建一个历史库(表),然后定期的将历史数据迁移到归档库(表)。一般来说,主表和归档表的表结构是一模一样的。也是一种分库分表的方案,就是历史数据和当前数据在不同的库表中 一般的实现方式就是根据时间、状态等字段,来筛选数据,然后把他们insert到新表中,再从旧表中删除: 1 2 3 4 5 6 7 -- 归档6个月以前的数据到归档表 INSERT INTO archive_table SELECT * FROM main_table WHERE modify_time < DATE_SUB(CURDATE(), INTERVAL 6 MONTH);; -- 删除主表中的归档数据 DELETE FROM main_table WHERE modify_time < DATE_SUB(CURDATE(), INTERVAL 6 MONTH);; 2. 基于分区表的归档(常见) 除了分库分表之外,MySQL 等数据库其实是支持分区表,最常见的就是按照时间分区。这样数据插入的时候就按照分区去插入了,而随着时间的推移,旧的分区慢慢就没有读写流量了。 ...

March 22, 2026 · 1 min · santu

第三方接口不稳定经常超时,如何处理三方接口异常不影响自己接口

典型回答 这种情况还挺常见的,我们经常需要调外部的服务,但是有的时候外部服务又不稳定,经常会失败或者超时,那么我们怎么避免因为他们的超时而影响到我们自己的接口呢? 有以下几种办法可以选择。 异步处理 对于一些可以异步请求第三方的场景,我们应该尽可能用异步的方案,包括MQ,异步线程,甚至是离线文件同步等方案。这种异步的方案我们用的非常多。 异步的话至少能保证我们给上游的返回是不受影响的。但是异步的话需要注意的是,我们需要有个收单的过程,就是别人请求过来的时候,我们做一下收单,然后收单成功之后就给上游返回成功,然后再异步处理。如果异步失败了,则根据收单记录进行重试。 这样做的话,还需要引入一个回调或者反查机制,因为我们只是告诉上游收单成功了,是否真正成功他是不知道的,所以需要有个回调或者反查的机制。 很多人看完之后一定会觉得复杂,但是其实大家对接过很多第三方的话就能发现,其实他们很多接口也都是这么做的,都是先收单,然后处理结果有了之后在回调,或者需要我们主动反查。 这是一种非常常见的做法,目的就是提升整体的吞吐量,以及减少对下游的强依赖。 设置超时机制 如果是同步调用,也不是没有办法,那就是我们设定一个超时时间,不管下游需要多少秒,我们执行的时候,如果超过我们给定的时间,就直接结束请求。避免被下游给拖垮,比如以下做: ✅线程池中怎么设置超时时间?一个线程如果要运行10s,怎么在1s就抛出异常 在我们发现超时之后,可以通过一些降级手段做返回,比如如果是查询接口的话,可以返回默认值,或者上次查询结果的值都可以。如果是写操作稍微麻烦一点,就需要约定好一些超时的处理策略,以及引入幂等+重试的机制了。 但是不管怎么样,这个超时机制还是非常有必要的,可以在关键时刻救命。 熔断机制 如果大家知道熔断的话,就会知道,我们可以借助熔断器实现当第三方接口的错误率或超时次数达到一定阈值时,停止请求该接口一段时间(熔断状态),然后逐渐恢复流量。 ✅什么是熔断? 我们可以借助hystrix、sentinel等工具帮我们实现快速熔断,这样就能很好的避免我们自己被拖垮,也可以避免给下游造成进一部分压力。 题外话 写完之后我才发现这个问题我之前好像写过,但是没关系了,我看了一下,内容差不多,就放在这吧,大家可以结合着看。: ✅和外部机构交互如何防止被外部服务不可用而拖垮

March 22, 2026 · 1 min · santu

Redis、MySQL和MongoDB的区别是什么,各自适用场景呢?

典型回答 Redis和MongoDB都是NoSQL数据库(以非表格格式存储数据的非关系型数据库),而MySQL是典型的关系型数据可。 在存储上,MongoDB和MySQL有点像,它他们都是基于磁盘存储的,而Redis是基于内存的,数据持久化一般通过RDB或AOF实现。 所以在性能上,Redis是最好的,而MongDB和MySQL则要差一些。 在事务的支持方面,MySQL是非常严格的支持了事务的,ACID全部都满足,而Redis也支持事务,只保证原子性,并且它的原子性也不不支持回滚,只保证事务过程中的命令执行不会被中断。 自MongoDB 4.0版本起,也开始支持多文档事务了,但相比传统的关系型数据库,事务支持较为简单。 ✅Redis 的事务机制是怎样的? 在用途方面,Redis 适用于需要快速读写、低延迟、缓存和实时数据处理的场景,比如做缓存、排行榜、Session存储等。MySQL 适用于结构化数据和持久化,业务数据的保存基本都是用MySQL这种关系型数据库的。而MongoDB 适用于需要高扩展性、灵活数据模型的大数据应用、日志存储(MongoDB适用于数据模式不固定、文档结构灵活的应用,如日志存储、用户行为数据分析等。 )和快速开发( 因为数据模型灵活,MongoDB允许更快速地迭代和开发,适合快速开发的应用 )的场景。 特性/数据库 Redis MySQL MongoDB 类型 内存键值存储(NoSQL) 关系型数据库(RDBMS) 文档型数据库(NoSQL) 数据模型 键值对,支持多种数据结构 表格(二维结构),SQL查询 文档(JSON或BSON格式) 持久化 基于内存 持久化(磁盘存储) 持久化(磁盘存储) 性能 极高(内存存储) 中等(磁盘存储) 较高(适合大数据量的分布式存储) 事务 支持简单的事务(只保证原子性) 支持ACID事务 支持多文档事务(4.0及以上) 扩展性 支持分布式,主要依靠内存 支持分库分表、主从复制,横向扩展 原生支持分布式,分片机制 适用场景 缓存、消息队列、实时统计、排行榜等 结构化数据存储、事务性操作、复杂查询 高可扩展性需求、非结构化数据、快速开发等

March 22, 2026 · 1 min · santu

Redis实现分布式锁,加锁的时候,redis不可用了咋整?

典型回答 当我们使用Redis作为分布式锁的时候,如果出现了Redis不可用,那么就会导致整个系统没办法进行加锁和解锁操作了,并且业务流程会因为无法加锁和解锁而卡住,无法继续执行。 一般来说,我们在实际工作中,都不太会考虑这种情况,因为Redis基本都是集群部署的,有高可用性保障的,为了这种极低概率发生的事情做很复杂的方案的特别少。所以,这个问题也是一个典型的“面试造火箭”。 那么,如果真的出现了这种情况,我们可以做的几个事情主要就是降级。让我们应用不要强依赖这个Redis的分布式锁,而导致整体系统的不可用。 有两种降级方案,第一种是降级为本地锁,当Redis的分布式锁不可用的时候,我们可以把锁降级成使用Java的本地锁,比如ReentrantLock或者synchronized等,这样可以确保即使 Redis 不可用,程序依然能够在单机模式下正常工作,避免系统崩溃。 当然,本地锁和分布式锁最大的问题就是不能保证全局互斥,所以只能用在可以容忍不严格分布式锁的情况下,简单点说,就是你可以牺牲一致性,保可用性的场景。 假如说你在底层有其他的防并发手段兜底,比如乐观锁、比如数据库锁、比如唯一性索引等等的时候,可以考虑用这个方案。 第二种降级方案,那就是降级为其他的分布式锁方案,比如借助Zookeeper,或者数据库实现的分布式锁。这个方案的好处是他还是个分布式锁,能起到全局互斥的作用,但是缺点是原来的Redis挂了,里面已有的锁可能没办法同步过来,还有就是这个方案太复杂了,成本太高了。 以上,就是一些可以做的容灾手段,至于其他的,通过Redis的集群模式、哨兵模式,高可用性保障等等也可以随便聊,只不过如果面试官一定限定了Redis就是挂了的话,可以聊这些降级的方案。 扩展知识 如何实现降级 想要让你的应用可以在Redis不可用的时候降级到其他的锁,那么就需要提前做改造,代码中预先的预留一个if-else,然后可以通过手动或者自动的方式让你的代码在if和else之间切换。 比如手动方式的话,可以通过配置中心推送一个配置(如useRedisLock的值),然后基于这个配置走Redis的分布式锁还是降级锁: 1 2 3 4 5 if(useRedisLock){ //使用Redis分布式锁 }else{ //使用降级锁 } 还有一种自动的方案,那就是自己在代码中重试几次,失败的话降级: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 // 业务逻辑 public void businessLogic() { boolean lockAcquired = false; // 先尝试获取 Redis 锁 for (int i = 0; i < RETRY_COUNT; i++) { lockAcquired = tryRedisLock(); if (lockAcquired) { break; } try { Thread.sleep(RETRY_DELAY); // 重试间隔 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } if (!lockAcquired) { // 如果 Redis 锁获取失败,降级使用本地锁 System.out.println("Redis 锁不可用,使用本地锁降级"); localLock.lock(); try { // 业务逻辑 System.out.println("执行业务逻辑(本地锁)"); } finally { localLock.unlock(); } } else { try { // 执行 Redis 锁下的业务逻辑 System.out.println("执行业务逻辑(Redis 锁)"); } finally { // 解锁 releaseRedisLock(); } } }

March 22, 2026 · 1 min · santu

如果让你实现短链服务,如何生成不重复的短链地址?

典型回答 把一个链接转成另外一个链接,方式很简单,有很多算法,比如MD5、SHA等等hash函数就可以,但是Hash函数存在着一定的碰撞风险。 如果要不碰撞,可以用数据库或者Redis自增ID来实现也可以的,但是这么做的话,就有可能被穷举出来。 那不想被穷举的话,还可以用类似雪花算法这种方式,但是雪花算法生成的id长度又太长了。 所以,我们想要把一个长连接转成短连接,并且有几个要求,还真不是一件容易的事儿: 1、链接更短 2、不能重复 3、不能被穷举 在行业内有一种比较常见的做法,那就是基于****MurmurHash+62进制转换的方式来实现。 MurmurHash 是一种哈希算法,因其高效的性能和良好的哈希分布,被广泛应用于需要快速哈希计算的场景,比如哈希表、分布式存储和数据去重。 MurmurHash 使用了多轮混合操作(mixing)的方式,通过位移、乘法和异或操作,将输入数据混淆,生成一个分布均匀的哈希值。 在短链服务中,MurmurHash(读作:么么哈希) 可以用来生成哈希值,将长 URL 转换为固定长度的哈希值作为短链地址的一部分。例如: 输入长 URL:https://www.hollischuang.com/archives/6998 计算哈希值:通过 MurmurHash3 算法生成哈希值。 转换为 Base62 编码:将哈希值转换为短链字符集(如 Base62)。 存储和查询:将短链和长 URL 的映射存入数据库。 1 2 3 4 String longUrl = "https://www.hollischuang.com/archives/6998"; int hash = MurmurHash3.murmurhash3_x86_32(longUrl.getBytes(), seed); String shortLink = base62Encode(hash); storeInDatabase(shortLink, longUrl); 输出结果大致为: 8M0kX 这样的字符串内容,就是一个我们想要的短链。 ...

March 22, 2026 · 1 min · santu

MySQL如果突然断电,会发生数据丢失吗?

典型回答 对于 InnoDB 存储引擎,如果配置得当(如 innodb_flush_log_at_trx_commit = 1),大多数情况下,突然断电后数据不会丢失,MySQL 会通过事务日志进行恢复。只有 未提交的事务 会丢失。 innodb_flush_log_at_trx_commit: 0:日志缓存区将每隔一秒写到日志文件中,并且将日志文件的数据刷新到磁盘上。该模式下在事务提交时不会主动触发写入磁盘的操作。 1:每次事务提交时MySQL都会把日志缓存区的数据写入日志文件中,并且刷新到磁盘中,该模式为系统默认的。 2:每次事务提交时MySQL都会把日志缓存区的数据写入日志文件中,但是并不会同时刷新到磁盘上。该模式下,MySQL会每秒执行一次刷新磁盘操作。 对于 MyISAM 存储引擎,断电可能导致表损坏或数据丢失,因为它没有事务日志机制。 如果使用了不当的磁盘缓存设置、较低的 innodb_flush_log_at_trx_commit 配置值或没有开启 sync_binlog,则可能会丢失一些数据。 但是大家需要注意!!!现代操作系统和硬件通常会对磁盘进行缓存,这意味着在写入磁盘时,数据可能会先被写入操作系统的缓存中,而不是直接写入磁盘。如果在缓存未刷新到磁盘时断电,这部分数据可能会丢失。为了避免这种情况,可以通过关闭磁盘缓存或者使用专用的硬件来减少风险。 扩展知识 Innodb的事务日志 MySQL 使用了 InnoDB 存储引擎时,所有的数据更改操作都会被记录在redo log中。这些事务日志是为了保证事务的原子性和持久性(ACID)而设计的。如果数据库突然断电,InnoDB 通过事务日志来确保数据的一致性和持久性,尽量避免数据丢失。 (这个过程其实就是崩溃恢复) Innodb的所有事务操作都会先写入 redo log,而不是直接写入数据文件。而且MySQL 还会记录 binlog,它用于复制和恢复数据。详见: ✅什么是事务的2阶段提交? 如果发生断电,对于已提交的事务,MySQL 会通过 redo log 恢复已提交的事务,因此已经提交的数据不会丢失。而对于未提交的事务,MySQL 会在重启后回滚这些事务,这意味着这些未提交的数据会丢失。 UPS 在很多公司中,为了避免意外断电带来的风险,通常会部署 不间断电源(UPS),这可以在断电时为服务器提供足够的时间来完成数据写入并安全关闭。通过硬件冗余和 UPS 系统,可以在断电时提高数据的持久性和安全性。

March 22, 2026 · 1 min · santu

留言给博主