标签之外,真正的自由是什么?
最近看到一个词,叫“妈人旷”。
它来自一句很流行的话:
妈妈人生是旷野。
大概意思是,人生不应该只有一条标准答案,不一定非要按照社会设定好的路径前进——读好学校、找好工作、升职加薪、买房结婚。
人的一生可以有很多可能,可以选择不同的生活方式。
这个观点本身没有什么问题。
最近看到一个词,叫“妈人旷”。
它来自一句很流行的话:
妈妈人生是旷野。
大概意思是,人生不应该只有一条标准答案,不一定非要按照社会设定好的路径前进——读好学校、找好工作、升职加薪、买房结婚。
人的一生可以有很多可能,可以选择不同的生活方式。
这个观点本身没有什么问题。
线上偶发 RejectedExecutionException 时,我先检查了线程池配置:32 个核心线程,最大线程数也是 32,队列长度 16。任务按批提交,每批完成后才会进入下一批。单看这些数字,线程池似乎留有余量,不应该轻易触发拒绝。
调大队列以后,异常暂时消失了,但这只能说明故障和瞬时容量有关,并没有解释为什么。后面的排查发现,我对 corePoolSize + queueCapacity 的理解更深了一层:它描述的是某一时刻能够容纳多少在途任务,并不保证每次批量提交都能被接住。
即时编译(Just-In-Time Compilation,简称JIT)是一种编译技术,它在代码即将首次执行时进行编译,因此得名“即时编译”。JIT是动态编译的一种特例。随着时间的发展,JIT的概念已经扩展,现在常被用来泛指动态编译;然而,狭义的JIT编译与更广泛的动态编译之间仍存在区别。动态编译(Dynamic Compilation)指的是在程序运行时进行编译,而静态编译(Static Compilation,也称事前编译,Ahead-Of-Time Compilation,简称AOT)则是在程序运行前完成编译。自适应动态编译(Adaptive Dynamic Compilation)也是一种动态编译技术,它先让程序以某种方式运行,收集信息后再进行编译,从而实现更高层次的优化。
Elasticsearch 虽提供强大搜索功能,但默认排序在复杂业务场景下渐显局限。电商需综合商品热度、评分等因素排序;新闻平台看重时效性与权威性;企业知识管理系统要考虑文档重要性等。Function Score Query 应运而生,它允许开发者依据业务规则自定义打分机制,实现精准个性化排序,以满足多样化需求,提升用户搜索体验与业务竞争力。
IK 能把一段中文切成词,但在业务里,知道“切出了什么”有时还不够。面对“施耐德继电器”这样的文本,我还希望分词结果能告诉下游:施耐德 是品牌,继电器 是品类;遇到物料号时,也应该保留对应的业务类型。
普通扩展词典可以让 IK 识别新词,却不会自动区分品牌、品类和物料号。为了补上这部分信息,我顺着 IK 的词典加载与词元输出流程做了一次改造。这篇记录只整理其中最关键的路径,以及二开时实际改动的地方。
analysis, 中文意思分析,指的是es在文档发送之前对文档正文的执行过程,以添加到inverted index, 这一过程包含一下几个步骤:
缓存作为一种介于应用程序和数据库之间的中间层,能够有效地存储频繁访问的数据,减少对数据库的直接查询操作,从而显著提高系统的性能。它通过将热点数据临时存储在快速访问的存储介质(如 Redis)中,使得后续的相同请求可以直接从缓存中获取数据,避免了重复的数据库查询开销,大大缩短了数据的响应时间。
然而,缓存的引入虽然带来了性能上的提升,却也引发了一系列新的问题,其中最为关键的就是缓存与数据库之间的数据一致性问题。由于缓存和数据库是两个独立的存储系统,它们的数据更新操作可能无法实时同步,导致在某些情况下,缓存中的数据与数据库中的数据不一致。这种不一致性可能会给用户带来困惑,影响业务的正常运行,例如用户可能获取到过期或错误的数据,进而影响用户体验和业务决策的准确性。
因此,在设计和实现包含缓存的系统架构时,如何确保缓存与数据库之间的数据一致性成为了一个亟待解决的重要问题。这需要深入分析各种可能导致数据不一致的场景和原因,研究并制定有效的治理方案,在保证系统性能的前提下,尽可能降低数据不一致出现的概率,以满足业务对数据准确性和可靠性的要求。
因为可见性问题导致处理器不同核心看到的内存操作顺序不一致,从而看起来就像是指令被重排了一样
JMM在编译器重排阶段, 会禁止特定类型的编译器重排, 在处理器重排序阶段,通过在指令之间插入内存屏障来禁止特定类型的处理器重排, 从而确保了在不同的编译器和不同的处理器平台上,不会应为重排序规则不一致导致非预期的运行结果
现代处理器与内存进行数据交互,会先将数据写到自己的缓冲区中, 每个处理器都有自己的缓冲区, 这种特性就会导致缓冲区中的数据和内存的数据存在不一致, 例如核心A执行两条指令, 指令1是更新x的值, 指令2是读取y的值, 核心A更新了x并写入缓冲区,然后去主存读了y; 核心B执行两条指令, 指令1是更新y的值, 指令2是读取x的值, 核心B更新了y的值写入缓冲区, 然后去主存读了x的值, 因为两个核心的缓冲器都还未刷新,所以他们读到的都是旧值, 或者是只刷新了一个缓冲区; 这些都会导致在指令层面看起来就像是发生了指令重排一样, 其实核心是因为可见性问题,所以也会有人将这种现象称之为内存系统指令重排。
单机容量不够时,把计算和数据分散到更多机器上,是一条很自然的扩展路径。普通服务器可以逐步增加,故障也有机会被限制在局部,系统不必长期依赖价格高昂且扩展空间有限的单机配置。
但分布式并没有消除复杂度,只是改变了复杂度出现的位置。原来由进程内调用、本地磁盘和同一个时钟隐含保证的事情,拆开后都要通过网络协议重新建立:服务要找到彼此,数据要分片和复制,节点要判断远端是否存活,还要对操作顺序和最终结果达成一致。
单机事务把提交和回滚封装在一个数据库内部。操作一旦跨过数据库或服务边界,情况就变了:一个节点已经执行成功,另一个节点没有响应,协调者既不能确认对方失败,也不能确认成功消息只是暂时没有返回。此时要解决的不只是如何提交,还包括故障期间要不要等待、恢复后如何续接,以及重复消息会不会把同一笔业务执行两次。
2PC、3PC 和 TCC 都试图组织一次跨节点提交,CAP、异步复制与 Quorum 则解释了网络分区出现后为什么总要舍弃一些东西。把这些内容放在一起看,可以更清楚地分辨:哪些方案在解决原子提交,哪些方案在调整一致性与可用性的边界。