芙影 - cixingji
type: channel
link: https://cixingji.cloud/index.php/author/1/
description: cixingji
items: AI TOOL看法
items: Redis知识全篇
items: Redis随笔总结
items: Spring随笔总结
items: Spring知识全篇
items: MySQL知识全篇
items: MySQL随笔总结
items: 欢迎使用 Typecho

AI TOOL看法
type: item
date: 2026-04-08T10:23:46+08:00
description: 首先所有AI工具对于人本身都是很大的提升，但是前提是要学会用，而不是“复制代码->让他解决->出现报错->复制代码”的循环。个人认为性能强弱的对比是：Claude Code > Codex > Trae > 其他国产工具 > 开源工具

Redis知识全篇
type: item
date: 2026-02-23T20:30:38+08:00
description: Redis基础redis是什么？一个用c语言实现的非关系型的键值对数据库，数据存储在内存上。redis和MEmcached区别共同点：都是基于内存区别：MEmcached只有key-value的数据结构redis有持久化机制，MEmcached没有，重启或者挂掉数据就没了redis原生就支持集群redis有发布订阅、lua脚本、事务，MEmcached没有redis的优点本质上就三点：高性能、高灵活、高可用高性能内存读写速度快高灵活多种数据结构：Hash存储用户信息，键值对形式，可以单个字段的更新Zset实现排行榜，支持范围查询和排序List还能充当简单的MQ高可用一、完整的持久化机制1.RDB快照：定期生成内存数据的全量快照2.AOF：记录每一条写操作的指令，支持每秒每次操作同步详细见持久化二、集群、主从复制、哨兵读写策略旁路缓存读多写少以DB的数据为准写先更新DB，然后删除cache读先从cache中读，有就返回，没有就DB中返回，然后数据放到cache中缺陷首次请求肯定不在cache里->热点数据提前放写频繁会导致cache频繁删除，命中率变很低强一致场景：更新db的时候同时更新cache，必须加（分布式）锁保证cache里面没有线程不安全问题短期可以不一致：更新db的时候同时更新cache，给缓存一个短的过期时间读写穿透意思在于：应用程序只和缓存交互，不再和数据库交互，而是由缓存和数据库交互侧重cache写先查cachecache中不存在，直接更新DBcache中存在，先更新cache，然后cache自己更新DB读查cachecache中存在，直接返回cache中不存在，先通过缓存组件从DB加载，写到cache里再返回思考：特点是由缓存组件和数据库打交道，而不是应用程序和缓存打交道。异步缓存写入Write Behind Pattern 和 Read/Write Through Pattern 很相似，两者都是由 cache 服务来负责 cache 和 db 的读写。但是，两个又有很大的不同：Read/Write Through 是同步更新 cache 和 db，而 Write Behind 则是只更新缓存，不直接更新 db，而是改为异步批量的方式来更新 db。一致性策略线程1来写，线程2来读先删缓存再更新数据库问题：线程1删除缓存然后更新Mysql中间，网络延迟，此时线程2查询发现缓存没有去Mysql查，查到旧数据，更新到redis中，最后结果是redis一直是旧数据，Mysql一直是新数据写的过程的一半出现的问题解决：延迟双删，也就是说线程1删除缓存更新数据库再删除缓存，实现了最终一致性注意延迟时间，根据业务找到线程2查询+更新redis这个过程的耗时，在这之后延迟删除先更新数据库再删缓存问题：线程2查询数据，redis的数据过期了，去Mysql查，查数据之后更新redis之前，线程1做完了更新Mysql+删除缓存，线程2才更新到redis数据，最后结果是redis一直是旧数据，Mysql一直是新数据概率很小很小，因为 更新Mysql+删除redis<读Mysql+写入redis 不太可能读的过程的一半出现的问题解决：延时双删，更数据库-->删除缓存-->延迟删除缓存删除失败情况增加重试机制：发送异步消息到息队列中，让系统监听消息队列Canal解耦，Mysql数据变动，立马通知到Canal，删除缓存和重试操作都Canal来做数据类型结构List、String、hash、Set、ZsetList：信息流---最新文章String：Hash：对象存储---文章信息Set：无序唯一集合---网站数据统计，比如点赞、转发；共同好友（交集）Zset：同set，加了一个score权重----排行榜Stringkey-value形底层实现：通过int和SDS（简单动态字符串），下面详细的SDS的结构和C的对比struct sdshdr { int len; // 已用长度（比如"abc"的len是3） int free; // 剩余空间（比如buf能存5字节，已用3，free=2） char buf[]; // 存数据的数组（末尾有`\0`兼容C字符串） };SDS保存文本数据，还可以保存二进制数据（\0兼容）；C只存文本SDS获取字符串长度的时间复杂度是O（1）（有len）；C语言字符串不能记录长度复杂度O（N）SDS可以自动扩容，C不可以HashHash 在字段较少且值较小时，底层使用 ziplist (压缩列表) 。这种结构在内存中是连续的，没有指针开销，非常紧凑。当字段增多时，会自动转为 hashtable。S和H区别特性StringHash 数据结构简单的 Key-Value。Value 可以是字符串、整数或序列化的对象…

Redis随笔总结
type: item
date: 2026-02-23T20:29:00+08:00
description: 基本redis 就是一个用c语言实现的键值对形式的数据库，只不过特殊的地方是数据存储在内存上。redis作用：整体上来说有很多，比如分布式锁，缓存，计数限流等，主要我用过的redis在视频项目中是缓存的作用，在面试项目中是做一个简易的消息队列的作用。redis的数据类型：String，Hash，Zset，Stream 详细见概括内容。redis的读写方式有三种，这个比较核心。第一种：旁路缓存，这个的读写的核心都在于DB，缓存就跟计算机组成的缓存一样作为辅助。读：先从缓存中读，有的话就直接返回，没有就从DB中返回然后放回DB中写：先更新DB，然后删除缓存。第二种：读写穿透，这个读写的核心在于缓存本身，整体的体系是都跟缓存接触，不和DB直接接触。读：先从缓存中读，有的话直接返回，没有的话先从缓存组件从DB中加载到缓存中然后再返回写：!!!怎么保证一致性模拟情况：两个线程一个读一个写先删除缓存再更新数据库在删除缓存和更新数据库之间，读的线程来了，看到了旧数据，然后更新数据到缓存中，这样缓存一直都是旧数据，数据库一直是新数据。解决方法：延迟双删，写线程删除缓存然后更新数据库再删除缓存，就可以了，第二次删除得选在读线程操作完之后删除。先更新数据库再删除缓存读的线程查询数据，但是缓存中的数据过期了，然后就得去数据库查，查完数据库和写回缓存之间，写线程干完了更新数据库然后删除缓存的步骤，这样导致了缓存一直是旧数据，数据库一直是新数据。这种相较于上一种的发生概率很低，因为：更新数据库的操作耗时是这里面最长的，很小概率是在这个间隙就做完了。解决方法：也是延迟双删持久化机制AOF机制AOF就是记录写操作的一个日志。是先进行写操作，然后进行日志记录。有两个风险一个就是因为写操作和写日志是两个操作并不是原子性的，所以可能宕机然后就导致丢失数据。第二个就是阻塞，因为是都在主线程上进行的，流量高的时候就容易阻塞重写的话是写完新的然后覆盖，是重复写不是全量写。RDB机制RDB就是创建一个某一个时间副本上的快照，存储的内存的数据。只有一个问题就是因为是全量写，所以如果时间设定的太频繁了容易卡顿，所以一般是分钟级设置。

Spring随笔总结
type: item
date: 2026-02-10T22:56:00+08:00
description: IOCIOC就是控制反转。也就是把创建对象管理对象等事情交给spring容器来做。举例：传统的开发就是A依赖于B，那就是A手动new一个对象出来；springIOC就是spring来帮我们实例化对象，我们需要什么对象直接在spring容器里取出来就是了。用一个技术就要看解决了什么问题，优点。而IOC的优点是解决了对象之间的耦合性，不会牵一发而动全身。举例：service依赖dao层无IOC：每次使用dao层实现类的增删改查就需要new这个实现类，如果有新的实现代码还要改业务代码。有IOC：spring的配置时代就改配置的对应实现类就行，springboot的注解时代就是Qualifier注解注明就行。AOPAOP就是面向切面编程。就是把横切的地方从核心业务中分离出来，也是一种解耦。通知就是在哪里触发，切面就是对关注点封装的类。解决什么问题，优点。可以解耦日志通知，接口限流，事务管理，如果一直重复实现这些行为，代码会特别冗余，难以维护。Bean的注入依赖注入常见三种方式：构造函数注入Setter注入Field（字段） 注入：在类的字段上使用注解（如 @Autowired 或 @Resource）来注入依赖项。Bean的实现xml：标签和工厂标签Java配置类：@Configuration+@Bean手动创建bean逻辑注解：@Component、@Repository、@Service等xml 和 java配置类 使用场景在于是第三方的SDK、第三方Jar包，不能自己写注解修改的这种场景注解自然就是自己写的业务代码的逻辑可以用的，同时也是自动装配机制的一环。自动装配原理自动装配就是现在的注解时代的不用写xml文件的核心机制。那这个机制是通过 @SpringBootApplication启动的，而这个注解是三个注解集合：@Configuration、@EnableAutoConfiguration 和 @ComponentScan@Configuration: 就是代表配置类，允许在上下文中注册额外的 Bean 或导入其他配置类。@EnableAutoConfiguration: 启用 Spring Boot 的自动配置机制。它是自动配置的核心，允许 Spring Boot 根据项目的依赖和配置自动配置 Spring 应用的各个部分。@ComponentScan:启用组件扫描。描被 @Component（以及 @Service、@Controller 等）注解的类，并将这些类注册为 Spring 容器中的 Bean。默认情况下，它会扫描该类所在包及其子包下的所有类。EnableAutoConfiguration是核心，下面有两个核心注解：@AutoConfigurationPackage，该注解上有一个注解，其中 Registrar 的作用是将启动类所在包下的所有子包的组件扫描注入到spring容器中。这也是为什么在spring boot中，把项目的controller、pojo、service、dao等包放在启动类同级目录下的原因。@Import(AutoConfigurationImportSelector.class)：这个注解的有一个方法的作用是去找配置文件，该方法去找META-INF/spring.factories文件中的所有自动配置类，并加载这些类。过程两条路，第一条路（手动配置的部分，也就是业务代码，有Component）启动：@SpringBootApplication，标记启动类，开始激活装配扫描：@ComponentScan+@AutoConfigurationPackage，扫描组件（范围）@AutoConfigurationPackage：通过注解导入的Registrar类，注册启动类所在的包是基础包，也就是设置了一个默认的扫描最大范围（执剑人）@ComponentScan：启动组件扫描器，扫描被@Component等注解修饰的类。第二条路（自动配置的部分，去找springboot默认自动配置的配置文件配置，）发现：@Import(AutoConfigurationImportSelector.class)，发现配置清单（位于META-INF/spring.factories）判断：配置清单上的每一个自动配置的类都会有一个 条件注解，用来判断需不需要装配，比如拿数据库举例子： 判断两个事情： 第一是判断有没有数据库的jar包，就是项目用没用数据库； 第二是判断现在项目里有没有DataSource这个配置类，有说明不用自动装配了，已经在第一条路装配了。没有就需要自动装配。注册：这步是把配置类内部的@Bean方法转化成BeanDefiniton注册到Spring容器里面 ----- 这些BeanDefinit…

Spring知识全篇
type: item
date: 2026-02-10T21:59:37+08:00
description: Spring谈谈Spring框架他是一个开源的轻量级java框架。一般说spring框架都指的是spring framework，是很多个模块的集合，比如AOP，IOC。最核心的思想实际上就是不重新造轮子，开箱即用。列举一些重要的模块？Core Container ：IOC AOP：aspect，aopTest：对单元测试JUnit、Mockito等常用测试框架支持Web：有对MVC的实现，对WebSocket的支持。Spring中Bean的作用域作用域描述singleton容器中唯一的 Bean 实例（默认）。prototype每次获取都会创建新实例。 也就是说，连续 getBean() 两次，得到的是不同的 Bean 实例。request每次 HTTP 请求创建新实例（仅 Web 应用）。session每次 HTTP Session 创建新实例（仅 Web 应用）。application/global-session每个 Web 应用启动时创建一个实例（仅 Web 应用）。websocket每次 WebSocket 会话创建新实例（仅 Web 应用）。注入Bean的方式依赖注入 (Dependency Injection, DI) 的常见方式：构造函数注入：通过类的构造函数来注入依赖项。Setter 注入：通过类的 Setter 方法来注入依赖项。Field（字段） 注入：直接在类的字段上使用注解（如 @Autowired 或 @Resource）来注入依赖项。实现Bean的方式1、xml：标签和工厂标签2、注解：@Component、@Repository、@Service等3、Java配置类：@Configuration+@Bean手动创建bean逻辑Spring事务及失效场景Spring框架事务管理 (@Transactional)作用保证业务层面的原子性功能简化开发 ：开发者无需编写 connection.commit()、connection.rollback()、connection.close() 等手动事务管理代码。只需通过 @Transactional 即可声明事务边界。应用场景commentMapper.insert(comment); (插入评论)videoStatsService.updateStats(...) (更新视频统计)如果第一步成功，但第二步因某种原因失败（如网络、死锁、异常），事务管理器会自动回滚第一步的插入操作，确保数据库中不会出现“评论已插入但统计数未更新”的不一致状态。失效情况​ 1、Spring事务是根据AOP实现的，就是拿到代理对象加入事务逻辑，所以如果调用方式不是通过注入调用的，也就是没有被Spring容器管理，就会失效。​ 比如一个类的一个非事务方法调用事务方法，事务失效，没走容器​ 1、解决：把被调用的事务方法写到一个新service通过注入调用​ 2、方法不是public修饰符​ 3、抛出的异常类型是检查型异常，或者根本没写抛异常语句@Component和@Bean区别特性@Component@Bean作用目标类方法控制权Spring 自动管理开发者手动控制使用条件配合 组件扫描 (@ComponentScan) 使用。必须在带有 @Configuration 或 @Component 注解的类中使用。创建过程spring 通过反射自动创建对象并注入依赖。开发者在方法体内手动实例化对象，并将对象返回给 spring容器。依赖注入@Autowired 或 @Resource 注解。在方法参数中声明依赖，spring 会自动传入。适用场景绝大多数自定义的类。1. 集成第三方库（无法修改源码）。 2. 需要复杂初始化逻辑。 3. 需要基于条件创建 Bean。衍生注解@Service, @Controller, @Repository无SpringBoot三者关系Spring：依赖注入、解耦 (AOP/IOC)MVC：Spring的一个模块，注解开发的核心，比如Controller，requestmapping等SpringBoot：有什么优点？1、提升开发效率：自动配置，起步依赖，开箱即用的功能2、和Spring生态集成3、Spring Boot 能够根据项目依赖自动配置大量的常见组件（如数据源、Web 容器、消息队列等），提供合理的默认设置。同时也允许开发者根据需要轻松覆盖或定制配置，极大减少了繁琐的手动配置工作。4、内嵌web服务器5、适合微服务架构6、Spring Boot 为常用的构建工具（如 Maven 和 Gradle）提供了专门的插件自动装配原理自动装配机制是通过 @SpringBootApplication 启动的，这个注解本质上是三个注解的集合。@Conf…

MySQL知识全篇
type: item
date: 2026-02-08T21:52:35+08:00
description: 存储引擎InnoDB和MyISAM区别InnodBmyisam事务支持不支持索引聚集索引非聚集索引（见B+树部分）锁最小是行锁最小是表锁count不保存行数保存，查行数很快恢复支持redo不支持索引索引是什么？就是数据库的目录索引的优缺点优点：快速检索，降低磁盘IO缺点：索引可能失效、占用额外的存储空间索引的分类聚集索引：数据的物理顺序和列值的逻辑顺序相同（主键）查询的时候，因为聚集索引 B+ 树的叶子节点存储了完整数据，直接查就行非聚集索引：就是顺序没关系（除了聚集就全是非聚集）查询的时候，因为非聚集索引 B+ 树的叶子节点存储的是 索引值 和 对应主键值1、需要先在这个树找到该行的主键值2、使用主键值，去聚集索引 B+ 树二次查找这个过程就叫做「回表」唯一索引：用UNIQUE INDEX变成唯一索引，唯一索引是非聚集索引的一种，作用在于主要用于保证这个索引数据的业务唯一性。覆盖索引：只要你查询的字段（SELECT 后面的列），全部被包含在你使用的索引中，就叫覆盖索引。特点在于不需要回表了直接在非聚集索引树上就能返回。索引的数据结构是树，因为树天生有二分查找的特性，查询速度高索引失效索引失效就是违反了数据库使用索引的原则，转变成了全表扫描查询条件类1、Like通配符前置LIKE '%关键字这个没什 是左侧顺序匹配的，相当于词典，只给右边的字母显然不能找到索引。！！！！2、最左前缀原则！！！！！在使用联合索引的时候，只有带着最左边的索引成分的时候，索引才能正常使用。索引失效的时候，查询的性能就变成了全表扫描，效率慢。CREATE INDEX idx_col ON table (a, b, c);a是最左边WHERE a = 1 AND b = 2可以，因为有a，索引成立WHERE b = 2 AND c = 3不可以，因为没有a，索引失效为什么：比如说三级索引，firstname、lastname、age索引字段对应书架分类作用first_name (第一级)大区域标签决定了所有书籍的宏观顺序。last_name (第二级)区域内小隔断只有在大区域（first_name）相同的书堆中，才按照 last_name 排序。age (第三级)最小的书签只有在前两级都相同的情况下，才按照 age 排序。 如果没有第一级的索引firstname，那通过lastname或age查询，是用不上索引的，因为第二级的索引只有在第一级索引的这个大区域下才有序，全局是无序的，所以失效了范围查询的时候：如果abc联合索引，在这个查询中，a 索引通过 IN 语句生效，用于多点定位。b 索引虽然生效，但它是一个范围查询。在联合索引中，一旦遇到范围查询，B+树的加速查找能力就会中断。因为 b 已经是范围，后续字段c在这个范围内是无序的，无法继续利用索引的排序特性来加速定位，因此c索引失效。3、否定条件比如 ！= not in，范围太大，使用索引成本高于直接进行全表扫描。操作索引类1、对索引进行运算where DATE(time) = '2025-1-1'因为这相当于对索引列的所有数据都计算一遍，等同于全表扫描。2、索引隐形转换比如id是int，结果和string类型比较，内部转换，同1。联合索引的存储方式(first\_name, last\_name, age, id)，查然后回表联合索引的 B+树最关键的一点在于，它的所有叶子节点（也就是数据块）是严格按照从左到右的字段顺序进行物理排序的。MYSQL索引为什么使用 B+树？平衡二叉树AVL：AVL实现平衡主要是靠旋转，旋转是非常耗时的，所以不能用（增加和删除）红黑树：和AVL相比，红黑树不追求完美的平衡，只是大致的平衡。对比AVL，查询略降，删除大提高因为红黑树同时引入了颜色，当插入或删除数据时，只需要进行 O(1)次数的旋转以及变色就能保证基本的平衡，不需要像 AVL 树进行 O(lgn)次数的旋转。总的来说， 红黑树的统计性能高于 AVL。红黑树应用：HashMap链表+红黑树解决哈希冲突 ，TreeMap 红黑树存储排序键值对在“高且瘦”的树中，你每次比较都可能需要访问一个新的节点。如果每个节点都位于磁盘的不同页（Page）上，那么查找一个数据就可能需要很多次磁盘I/O。比如树高20，就需要最多20次I/O，这无法接受。B树：矮胖树，一个节点可以有多个子节点，每个节点都会存储数据本身，节点内的键值是排好序的，节点之间也满足排序关系。B+树直接看和B树的区别数据存储：B+树的Data只存在于叶子节点中，非叶子节点只有索引。（B树什么节点都存数据）由于B+树非叶子节点不存Data，所以每个节点能存的索引更多，树更矮。B 树的非叶子节点结构大致是：[P_1, (K_1, D_1), P_2, (K_2,…

MySQL随笔总结
type: item
date: 2026-02-08T21:50:00+08:00
description: 索引分类：索引这一块按照本质分是两种索引，一种是聚集索引一种是非聚集索引。聚集索引就是实际顺序与存储的时候逻辑顺序相同的索引，也就是主键，如果没有主键，那MySQL会把第一个不包含NULL的唯一索引当做聚集索引，都没有，会生成一个隐式聚集索引。非聚集索引可以粗暴的理解为后加的人为的索引，比如name，age之类的。区别：这两种索引有区别，而这种区别是因为索引的底层结构导致的，索引的底层结构是B+树，B+树的特性就是非叶子节点不存储数据只存储索引，叶子节点存储data也就是数据。那聚集索引的B+树的样子就是：非叶子节点都是索引，叶子节点直接就是行数据。那非聚集索引的B+树的样子是：非叶子节点是索引，叶子节点是索引对应的主键值，所以需要再跳一次主键的B+树，这个过程就叫做回表。其他：至于其他派生的索引都是具有一部分功能性，比如唯一索引就是非聚集索引的一种，是用来表示这个数据的唯一性。覆盖索引的意思是在非聚集索引的这个B+树在查找的时候，索引已经囊括了所有要查的数据，也就是Select后面的要查的数据。特殊：比较特殊的是非聚集索引的联合索引，有最左前缀原则，这个也跟索引的底层的B+树不谋而合。多次谈到B+树结构，下文写一下B+树的优点以及结构结构：B+树之前说一下B树B树：矮胖树，一个节点可以有多个子节点，每个节点都会存储数据本身，节点内的键值是排好序的，节点之间也满足排序关系。B+树：1.数据存储：B+树的Data只存在于叶子节点中，非叶子节点只有索引。（B树什么节点都存数据）由于B+树非叶子节点不存Data，所以每个节点能存的索引更多，树更矮。2.叶子结点：B+树中有叶子节点有指针，叶子节点之间通过指针链接起来形成一个有序链表（B树叶子节点是相互独立的）那根据这俩区别，能总结出四个优点：数据存储的模式导致树更矮胖，磁盘IO次数少。因为Data都存在于叶子节点里面，最差的情况就是一般情况，查询性能稳定。叶子节点之间的双向链表，全表遍历效率高。叶子节点之间的双向链表，范围查询效率高。最左前缀原则：看详细的概括索引失效情况：看详细的概括锁锁的出现是因为要处理多并发多线程场景下的并发问题，也就是下面要说的事务。分类：锁的分类不像索引那样清晰，因为锁既可以锁整个表，也可以锁单行数据，所以大方向分为：表级锁和行级锁。因为MySQL的存储引擎是InnoDB，所以本文只涉及InnoDB的锁表级锁有：表锁：表级读锁阻止其他操作对表的写，表级写锁其他操作对表的读和写都阻止。元数据锁MDL：防止执行CRUD的时候表结构变更（数据库本身会加MDL，不用我们来操作）意向锁：意向锁是是为了解决行锁与表锁的冲突设计的，可以快速判断这个表里有没有行锁行级锁有：按照锁的兼容性分为：排他锁和共享锁，也就是X和S锁。按照锁的范围分为：记录锁：锁单条记录间隙锁：锁一个id数字结合，不包括记录本身（前开后开）临键锁：记录锁+间隙锁，锁一个范围，包含记录本身（前开后闭）什么时候加表级锁？当执行UPDATE、DELETE的语句的时候，WHERE没有命中索引或者索引失效的时候，就会对全表扫描加锁。什么时候加行级锁？看详细的概括三大日志binlog逻辑日志，作用是主从复制，全量数据的备份。追加写，写满一个文件接着创建接着写写入时机在每完成一次更新操作之后，生成一条binlog刷盘时机什么时候刷盘由sync_binlog参数决定0：不去强制要求，由系统自行判断何时写入磁盘；1：每次事务提交后，都要将binlog写入磁盘；N：每N个事务，才会将binlog写入磁盘。redolog缓存机制，以及WAL，看详细的概括。两阶段提交看详细的概括。undolog逻辑日志，用来回滚的，作用是保障事务的原子性和实现多版本并发控制MVCC。原理插入：记录插入的主键值就行--------回滚的时候只删除这个主键值对应的记录就行记录新插入行的主键，回滚的时候根据主键定位并删除删除：记录这个数据项的所有字段---回滚的时候把内容组成的记录重新插入到表中这里删除是逻辑删除，删除只是标记为已删除，回滚的时候再置为false更新：记录主键值和被修改的旧值---回滚的时候把列更新为旧值就行**1、更新的不是主键：那undolog记录的是主键以及被修改的原始值，回滚的时候根据主键找到这行，再用旧值覆盖新值** **2、更新的是主键：InnoDB的操作是“删除旧记录+插入新纪录”，所以undolog的记录是针对旧的主键的DELECT型+针对新的主键的INSERT型** 写入时机在事务没提交之前，先把更新前的数据放到undo log 中刷盘时机随 Redo Log 刷盘而持久化。因为undolog本身就算是一个修改操作，修改了。事务事务概念本身就是为了保障数据的一致性。事务的四大特性：目标实现手段说明原子性 (A)u…

欢迎使用 Typecho
type: item
date: 2026-02-04T15:24:00+08:00
description: 如果您看到这篇文章,表示您的 blog 已经安装成功.