MySQL技术内幕
MySQL体系结构和存储引擎InnoDB体系架构InnoDB存储引擎的架构。图2-1简单显示了InnoDB的存储已引擎的体系架构,从图可见,InnoDB存储引擎有多个内存块,可以认为这些内存块组成成了一个大的内存池,负责如下工作:
维护所有进程/线程需要访问的多个内部数据结构。
缓存磁盘上的数据,方便快速地读取,同时在对磁盘文件的数据修改之前在这里缓存。
重做日志(redolog)缓冲。
后台线程的主要作用是负责刷新内存池中的数据,保证缓冲池中的内存缓存的是最近的数据。此外将已修改的数据文件刷新到磁盘文件,同时保证正在数据库发生异常的情况下InnoDB能恢复到正常运行状态。
后台线程1、Master Thread
MasterThread是一个非常核心的后台线程,主要负责将缓冲池中的数据异步刷新到磁盘,保证数据的一致性,包括脏页的刷新、合并插人缓冲(INSERT BUFFER)、UNDO页的回收等。
2、IO Thread
在InnoDB存储引擎中大量使用了AIO(AsyncIO)来处理写I0请求,这样可以极大提高数据库的性能。而IO Thread的工作主 ...
Java核心技术 - JUC
JUC包中锁原理剖析LockSupport工具类JDK中的 rt.jar包 里面的 LockSupport 是个工具类,它的主要作用是挂起和唤醒线程,该工具类是创建锁和其他同步类的基础。
LockSupport类与每个使用它的线程都会关联一个许可证,在默认情况下调用LockSupport类的方法的线程是不持有许可证的。LockSupport是使用Unsafe类实现的,下面介绍LockSupport中的几个主要函数。
1、void park()
如果调用 park 方法的线程已经拿到了与 LockSupport关联 的许可证,则调用 Locksupport.park() 时会马上返回,否则调用线程会被禁止参与线程的调度,也就是会被阻塞挂起。
如下代码直接在main函数里面调用park方法,最终只会输出begin park!,然后当前线程被挂起(阻塞),这是因为在默认情况下调用线程是不持有许可证的。
1234567public class Demo { public static void main(String[] args) { Sys ...
Head First设计模式
设计模式入门OO基础
抽象、封装、多态、继承
OO原则
封装编号,
多用组合少用继承
针对接口编程,不针对实现编程
“针对接口编程”真正的意思是“针对超类型(supertype)编程”。
这里所谓的“接口”有多个含义,接口是一个“概念”,也是一种Java的interface构造。你可以在不涉及Java interface的情况下,“针对接口编程”,关键就在多态。利用多态,程序可以针对超类型编程,执行时会根据实际状况执行到真正的行为,不会被绑死在超类型的行为上。“针对超类型编程”这句话,可以更明确地说成“变量的声明类型应该是超类型,通常是一个抽象类或者是一个接口,如此,只要是具体实现此超类型的类所产生的对象,都可以指定给这个变量。这也意味着,声明类时不用理会以后执行时的真正对象类型!”
策略模式策略模式定义了算法族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。
应用示例,我们现在都有超类Duck,具有共同的特征会 ...
配置开关、灰度发布与快速回滚实践
先说为什么要做开关配置开关这件事,很多系统都在做,但真正上线时能不能救命,关键不在于有没有一个布尔值,而在于变更是不是可控。
业务里最常见的使用场景通常是下面这几类:
1、新逻辑先随代码上线,默认关闭
2、新下游能力先对白名单或小流量开放
3、活动期间按比例逐步放量
4、线上异常时快速关闭新路径,退回旧链路
这一篇只讲业务里常用的开关设计、灰度放量和快速回滚,不展开配置中心原理。
开关先分层如果所有配置都混成一堆 true 和 false,后面基本很难维护。
更常见也更稳的分法一般是:
1、保护性开关:立即止血,比如关闭消费者入口、关闭某个动作执行
2、策略开关:控制某段新逻辑是否启用,比如新签名校验、新路由策略
3、参数配置:灰度比例、超时时间、批量大小、重试次数
4、范围配置:白名单活动、渠道、租户、用户集合
可以先把配置对象收一下:
123456789@Datapublic class PrizeSwitchConfig { private boolean consumerClose = false; private boolean signCheck ...
Spring事务常见坑与处理实践
事务为什么总翻车事务这块最常见的问题,不是不会加注解,而是加了以后以为一定生效。
真实业务里,事务最容易翻车的地方通常是下面这些:
1、同类方法调用,事务根本没生效
2、异常被吃掉,事务没有回滚
3、事务里混了远程调用,把数据库连接长时间占住
4、异步线程、批量处理和事务边界没切清楚
这篇只讲业务代码里经常踩的坑,不展开讲事务基础概念。
同类调用不会生效这是最经典的一类坑。
123456789101112131415@Servicepublic class CouponService { public void sendBatch(List<Long> userIds) { for (Long userId : userIds) { sendOne(userId); } } @Transactional(rollbackFor = Exception.class) public void sendOne(Long userId) ...
Redis 在业务系统里的常见误用与修正
先说几个高频误用业务系统里用 Redis 很常见,但很多问题不是 Redis 不好用,而是用法太顺手,最后把它当成了万能胶。
最常见的误用通常是下面这几类:
1、把 Redis 当数据库底线,只存不落库
2、缓存键没有边界,TTL 也靠拍脑袋
3、分布式锁只管加锁,不管释放和续期
4、热点 key、BigKey、批量 key 清理没人管
5、Redis 异常时没有降级,主流程一起跟着抖
这一篇只讲业务里高频踩坑点和修正方式,不展开 Redis 基础原理。
别把 Redis 当唯一真相源很多业务一开始只是想提速,后面慢慢演变成:核心状态只写 Redis,不落库。
1234public void markUserCoupon(Long userId, Long couponId) { String key = "coupon:receive:" + userId + ":" + couponId; redisTemplate.opsForValue().set(key, "1", Duratio ...
AI 开发怎么真正用起来:Agent、Skills、MCP
先别把 AI 当自动写码机AI 写代码这件事,现在最大的问题不是能不能生成代码,而是很多人只会“问一句,贴一段”,最后要么答非所问,要么产出一堆不能落地的模板。
如果你平时是 Java 后端、全栈或者日常维护业务系统,真正有价值的不是“让 AI 替你开发一切”,而是把它变成一个能查上下文、能执行工具、能按规则做事的开发助手。
现在常见的几个关键词,基本绕不开这些:
1、Prompt
2、Agent
2、Skills
3、MCP
5、工作区上下文
这篇不讲大模型原理,只讲平时开发里最容易碰到的三个东西:Agent、Skills、MCP,它们分别是什么,为什么要有,平时该怎么用。
先把三个词分开这三个词经常被混着说,但它们其实不是一层东西。
先说最短的版本:
1、Agent 更像一个会自己拆任务、会决定下一步做什么的执行者
2、Skills 更像给这个执行者准备好的固定套路和工作规范
3、MCP 更像把它接到外部工具和数据源上的接口层
如果非要类比成一个工程团队,可以这么理解:
1、Agent 是干活的人
2、Skills 是团队里的 SOP 和最佳实践
3、MCP 是这个人 ...
线程池隔离与拒绝策略实践
先看问题线程池这块,很多项目不是不会用,而是用得太随便。
最常见的问题就两类:
1、所有异步任务共用一个线程池
2、拒绝策略和队列长度直接照抄模板
平时流量不高时看不出问题,一到高峰期,慢任务、重任务、补偿任务、回调任务全挤在一起,排查时也很难判断到底是哪类任务把线程打满了。
这篇只讲业务里常见的线程池隔离实践,不展开讲线程池原理。
先拆池子最常见的错误写法,是全项目只保留一个业务线程池。
1234567891011@Bean("commonExecutor")public ThreadPoolTaskExecutor commonExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(16); executor.setMaxPoolSize(32); executor.setQueueCapacity(1000); executor.setThreadNamePre ...
异步任务与线程池治理实践
线程池问题最容易伪装成“偶发超时”很多系统里的异步能力不是一次设计出来的,而是慢慢叠上去的:
1、先有一个公共线程池
2、后面补批量任务
3、再加补偿、消息处理、外部调用
4、最后所有异步任务都往里面塞
这时线上最常见的表现不是线程池报错,而是接口慢、任务堆、偶发超时、日志里还看不出来是谁把池子打满了。
真正该先拆的,是任务模型,不是线程数我现在更倾向按职责拆池,而不是按“项目里有异步”拆池。
比较实用的拆法通常是:
1、规则计算线程池
2、批量查询线程池
3、补偿任务线程池
4、消息消费后处理线程池
5、外部调用线程池
因为这几类任务的资源模型完全不同:
1、有的偏 CPU
2、有的偏 IO
3、有的容忍堆积
4、有的必须及时完成
如果混在一个池子里,出了问题基本只能看到“这个池满了”,但看不到是谁干的。
一套能进生产的线程池配置,至少要把边界定清楚1234567891011@Bean("asyncExecutor")public ThreadPoolTaskExecutor asyncExecutor() { ThreadPoolT ...
库存扣减与超卖兜底实践
库存问题到底难在哪库存链路最容易被误解成一句话:扣减时判断一下库存大于 0 就行。
真正到了高并发场景,库存问题通常不是“会不会减”,而是下面这些:
1、并发请求下会不会超卖
2、扣减成功后下游失败,库存怎么回补
3、Redis 预扣和数据库实扣怎么对齐
4、活动峰值时是宁可少卖,还是绝不能超卖
这一篇只讲业务里常用的库存扣减方式和兜底思路,不展开讲分布式事务理论。
扣减别先查后改库存最经典的错误写法就是先查库存,再 update。
1234567public void deduct(Long skuId, int count) { Stock stock = stockMapper.selectBySkuId(skuId); if (stock.getAvailable() < count) { throw new BizException("库存不足"); } stockMapper.updateAvailable(skuId, stock.getAvailable() - ...



