桥下の民 - Advanced
type: channel
link: https://wordplay.work/index.php/category/Advanced
items: LangChain4J 概述 & 起步
items: Skywalking
items: Redis-Cluster 记录
items: JVM 调优
items: 并发编程 Code
items: 同城灾备、同城双活、两地三中心、异地多活
items: ClickHouse
items: SpringBoot启动配置原理
items: Btree 索引详解
items: MySQL 索引 B-Tree（B+Tree）

LangChain4J 概述 & 起步
type: item
date: 2025-08-19T06:49:00+00:00
description: 一、概述 1.1、概述 LangChain4J 即 LangChain For Java。 随着人工智能（AI）技术的迅猛发展，越来越多的开发者开始将目光投向AI应用的开发。然而，目前市场上大多数AI框架和工具如LangChain、PyTorch等主要支持Python，而Java开发者常常面临工具缺乏和学习门槛较高的问题。 LangChain4J 官方文档 1.2、作用 LLM 大模型应用技术架构： 大模型开发分类：超级个体+智能体、行业垂直大模型、基础通用大模型。 产品定位： 二、起步 2.1、前置约定 2.1.1、langchain4j 执行的各种大语言模型(LLMS) Comparison Table of all supported Language Models 2.1.2、模型约定 2.1.3、配置及关键点 所有调用均基于 OpenAI 协议标准或者 SrpingBoot 官方推荐整合规则，实现一致的接口设计与规范，确保多模型切换的便利性，提供高度可扩展的开发支持。 2.2、入门 2.2.1、接入按理百炼平台的通义模型 https://bailian.console.aliyun.com 2.2.2、大模型调用 1、获得 API-key 2、获得模型名 3、获得 baseUrl 开发地址

Skywalking
type: item
date: 2025-03-21T02:48:00+00:00
description: 一、Skywalking skywalking 是一个优秀的国产开源框架，2015 年由个人吴晟（华为开发者）开源 ， 2017 年加入 Apache 孵化器。 skywalking 支持 dubbo，SpringCloud，SpringBoot 集成，代码无侵入，通信方式采用 GRPC，性能较好，实现方式是 java 探针，支持告警，支持 JVM 监控，支持全局调用统计等等，功能较完善。 二、Skywalking & Spring Cloud Sleuth+ZipKin Skywalking 相比于 zipkin 还是有很大的优势的，如下： 1、Skywalking 采用字节码增强的技术实现代码无侵入，zipKin 代码侵入性比较高。 2、Skywalking 功能比较丰富，报表统计，UI 界面更加人性化。 三、Skywalking 架构 skywalking 和 zipkin 一样，也分为服务端和客户端，服务端负责收集日志数据并且展示，架构如下： 上述架构图中主要分为四个部分，如下： 上面的 Agent：负责收集日志数据，并且传递给中间的 OAP 服务器 中间的 OAP：负责接收 Agent 发送的 Tracing 和 Metric 的数据信息，然后进行分析 (Analysis Core) ，存储到外部存储器 ( Storage )，最终提供查询 ( Query ) 功能。 左面的 UI：负责提供 web 控制台，查看链路，查看各种指标，性能等等。 右面 Storage：负责数据的存储，支持多种存储类型。 Agent 负责收集日志传输数据，通过 GRPC 的方式传递给 OAP 进行分析并且存储到数据库中，最终通过 UI 界面将分析的统计报表、服务依赖、拓扑关系图展示出来。 三、服务端搭建 skywalking 同样是通过 jar 包方式启动，需要下载 jar 包，地址： https://skywalking.apache.org/downloads/ https://archive.apache.org/dist/skywalking 3.1、下载安装包 选择 V8.7.0 或其他版本，如下图： 重要的目录结构分析如下： agent：客户端需要指定的目录，其中有一个 jar，就是负责和客户端整合收集日志 bin：服务端启动的脚本 config：一些配置文件的目录 logs：oap 服务的日志目录 oap-libs：oap 所需的依赖目录 webapp：UI 服务的目录 3.2、配置修改 启动之前需要对配置文件做一些修改，修改如下： 3.2.1、/config/application.yml 这个是 oap 服务的配置文件，需要修改注册中心为 nacos，如下图： 配置 1：修改默认注册中心选择 nacos，这样就不用在启动参数中指定了。 配置 2：修改 nacos 的相关配置，由于陈某是本地的，则不用改动，根据自己情况修改。 3.2.2、webapp/webapp.yml 这个是 UI 服务的配置文件，其中有一个 server.port 配置，是 UI 服务的端口，默认 8080，可将其改成 8081，避免端口冲突。 3.3、启动服务 启动命令在 /bin 目录下，这里需要启动两个服务，如下： oap 服务：对应的启动脚本 oapService.bat，Linux 下对应的后缀是 sh。 UI 服务：对应的启动脚本 webappService.bat，Linux 下对应的后缀是 sh。 当然还有一个 startup.bat 启动文件，可以直接启动上述两个服务，我们可以直接使用这个脚本，直接双击，将会弹出两个窗口则表示启动成功。 此时直接访问：http://localhost:8888/，直接进入 UI 端。 四、客户端搭建 客户端也就是单个微服务，由于 Skywalking 采用字节码增强技术，因此对于微服务无代码侵入，只要是普通的微服务即可，不需要引入什么依赖。 想要传输数据必须借助 skywalking 提供的 agent，只需要在启动参数指定即可，命令如下： -javaagent:E:\SOFTWARE\skywalking\apache-skywalking-apm-bin\agent\skywalking-agent.jar -Dskywalking.agent.service_name=jeecg-cloud-gateway -Dskywalking.collector.backend_service=localhost:11800 上述命令解析如下： -javaagent：指定 skywalking 中的 agent 中的 skywalking-agent.jar 的路径。 -Dskywalking.agen…

Redis-Cluster 记录
type: item
date: 2024-03-18T02:24:00+00:00
description: 一、介绍 1.1、简介 什么是redis集群： 由于数据量过大，单个Master复制集难以承担，因此需要对多个复制集进行集群，形成水平扩展每个复制集只负责存储整个数据集的一部分，这就是Redis的集群，其作用是提供在多个Redis节点间共享数据的程序集。 功能： redis集群支持多个master，每个master又可以挂在多个slave。具有读写分离、数据高可用的效果。 由于集群自带了哨兵的故障转移机制，内置了高可用的支持，所以使用集群无需再去使用哨兵功能。 客户端与redis的节点连接，不再需要连接集群中所有的节点，只需要任意连接集群中的一个可用节点即可。 槽位slot负责分配到各个物理服务节点，由对应的集群来负责维护节点，插槽和数据之间的关系。 Redis 集群的数据分片： Redis 集群没有使用一致性hash，而是引入哈希槽的概念。Redis 集群有16384个哈希槽，每个key通过CRC16校验后对16384取模来决定放置哪个槽，集群的每个节点负责部分hash槽，举个例子，比如当前集群有3个节点，那么: 分片是什么： 使用Redis集群时我们会将存储的数据分散到多台redis机器上，这称为分片。简言之，集群中的每个Redis实例都被认为是整个数据的一个分片。 如何找到给定key的分片： 为了找到给定key的分片，我们对key进行CRC16(key)算法处理并通过对总分片数量取模。然后，使用确定性哈希函数，这意味着 给定的key将多次始终映射到同一个分片，我们可以推断将来读取特定key的位置。 1.2、分片与槽位slot的优势 最大优势，方便扩缩容和数据分派查找： 这种结构很容易添加或者删除节点,比如如果我想新添加个节点D，我需要从节点 A,B,C中得部分槽到D上，如果我想移除节点A，需要将A中的槽移到B和C节点上，然后将没有任何槽的A节点从集群中移除即可，由于从一个节点将哈希槽移动到另一个节点并不会停止服务，所以无论添加删除或者改变某个节点的哈希槽的数量都不会造成集群不可用的状态。 1.3、为什redis集群的最大槽数时16384个 正常的心跳数据包带有节点的完整配置，可以用幂等方式用旧的节点替换旧节点，以便更新旧的配置。 这意味着它们包含原始节点的插槽配置，该节点使用2k的空间和16k的插槽，但是会使用8k的空间（使用65k的插槽）。 同时，由于其他设计折衷，Redis集群不太可能扩展到1000个以上的主节点。 因此16k处于正确的范围内，以确保每个主机具有足够的插槽，最多可容纳1000个矩阵，但数量足够少，可以轻松地将插槽配置作为原始位图传播。请注意，在小型群集中，位图将难以压缩，因为当N较小时，位图将设置的slot / N位占设置位的很大百分比。 (1)如果槽位为65536，发送心跳信息的消息头达8k，发送的心跳包过于庞大。 在消息头中最占空间的是myslots[CLUSTER_SLOTS/8]。 当槽位为65536时，这块的大小是: 65536÷8÷1024=8kb 在消息头中最占空间的是myslots[CLUSTER_SLOTS/8]。 当槽位为16384时，这块的大小是: 16384÷8÷1024=2kb 因为每秒钟，redis节点需要发送一定数量的ping消息作为心跳包，如果槽位为65536，这个ping消息的消息头太大了，浪费带宽。 (2)redis的集群主节点数量基本不可能超过1000个。 集群节点越多，心跳包的消息体内携带的数据越多。如果节点过1000个，也会导致网络拥堵。因此redis作者不建议redis cluster节点数量超过1000个。 那么，对于节点数在1000以内的redis cluster集群，16384个槽位够用了。没有必要拓展到65536个。 (3)槽位越小，节点少的情况下，压缩比高，容易传输 Redis主节点的配置信息中它所负责的哈希槽是通过一张bitmap的形式来保存的，在传输过程中会对bitmap进行压缩，但是如果bitmap的填充率slots / N很高的话(N表示节点数)，bitmap的压缩率就很低。 如果节点数很少，而哈希槽数量很多的话，bitmap的压缩率就很低。 1.4、槽位映射的发展 第一阶段：哈希取余分区 2亿条记录就是2亿个Kv，我们单机不行必须要分布式多机，假设有3台机器构成一个集群，用户每次读写操作都是根据公式hash(key)% N个机器台数，计算出哈希值，用来决定数据映射到哪一个节点上。 优点: 简单粗暴，直接有效，只需要预估好数据规划好节点，例如3台、8台、10台，就能保证一段时间的数据支撑。使用Hash算法让固定的-到同一台服务器上，这样每台服务器固定处理一部分请求(并维护这些请求的信息)，起到负载均衡+分而治之的作用。 缺点: 原来规划好的…

JVM 调优
type: item
date: 2024-03-05T08:07:00+00:00
description: 一、调优命令 Sun JDK监控和故障处理命令有jps jstat jmap jhat jstack jinfo 。 1.1、jps JVM Process Status Tool,显示指定系统内所有的HotSpot虚拟机进程。 1.2、jstat JVM statistics Monitoring是用于监视虚拟机运行时状态信息的命令，它可以显示出虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据。 1、基本语法： # 基本格式 jstat - <vmid> [interval] [count] # 常用示例 jstat -gc 1234 1000 10 # 每秒输出进程号1234的GC信息，共输出10次 2、查看类加载统计： # 查看类加载信息 jstat -class <pid> # 输出示例： Loaded Bytes Unloaded Bytes Time 7035 14506 0 0 3.67 # 字段说明： Loaded : 加载class的数量 Bytes : 加载class的字节数 Unloaded : 未加载class的数量 Bytes : 未加载class的字节数 Time : 加载时间 3、查看GC统计： # 查看GC统计信息 jstat -gc <pid> # 输出示例： S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT 1024.0 1024.0 0.0 0.0 8192.0 8171.9 20480.0 20480.0 4864.0 3720.0 512.0 324.8 2 0.002 1 0.003 0.005 # 字段说明： S0C: Survivor0区容量 S1C: Survivor1区容量 S0U: Survivor0区已使用 S1U: Survivor1区已使用 EC : Eden区容量 EU : Eden区已使用 OC : 老年代容量 OU : 老年代已使用 MC : 方法区容量 MU : 方法区已使用 YGC: 年轻代GC次数 YGCT: 年轻代GC时间 FGC: Full GC次数 FGCT: Full GC时间 GCT: 总GC时间 4、查看GC详细信息： # 查看新生代GC统计 jstat -gcnew <pid> # 输出示例： S0C S1C S0U S1U TT MTT DSS EC EU YGC YGCT 1024.0 1024.0 0.0 1024.0 1 15 1024.0 8192.0 7623.5 2 0.002 # 字段说明： TT : Tenuring threshold（晋升阈值） MTT : Maximum tenuring threshold（最大晋升阈值） DSS : Desired survivor size（期望的survivor大小） 实际使用示例： # 监控内存使用情况 while true; do # 每5秒输出一次GC信息 jstat -gc $(jps | grep MyApp | awk '{print $1}') 5000 done # 输出到文件进行分析 jstat -gc $(jps | grep MyApp | awk '{print $1}') 1000 > gc.log 5、性能分析脚本： #!/bin/bash # gc_monitor.sh PID=$1 INTERVAL=${2:-1000} # 默认1000ms COUNT=${3:-""} # 默认持续输出 if [ -z "$PID" ]; then echo "Usage: $0 <pid> [interval] [count]" exit 1 fi # 输出表头 echo "Time YGC YGCT FGC FGCT GCT" # 监控GC情况 jstat -gc $PID $INTERVAL $COUNT | awk ' NR>1 { printf "%s %d %.3f %d %.3f %.3f\n", strftime("%H:%M:%S"), $15, # YGC $16, # YGCT $17, # FGC $18, # FGCT $19 # GCT }' 6、内存分析脚本： #!/bin/bash # memory_monitor.sh PID=$1 INTERVAL=${2:-1000} COUNT=${3:-""} if [ -z "$PID" ]; then echo "Usage: $0 <pid> [interval] [count]" exit 1 fi # 输出表头 echo "Time Eden% Old% Meta%" # 监控内存使用率…

并发编程 Code
type: item
date: 2024-03-02T02:46:00+00:00
description: 一、CAS 无锁算法 public class ReentrantSpinLock { private AtomicReference<Thread> cas = new AtomicReference<Thread>(); private int count; public void lock() { Thread current = Thread.currentThread(); if (current == cas.get()) { // 如果当前线程已经获取到了锁，线程数增加一，然后返回 count++; return; } // 如果没获取到锁，则通过CAS自旋 while (!cas.compareAndSet(null, current)) { // DO nothing } } public void unlock() { Thread cur = Thread.currentThread(); if (cur == cas.get()) { if (count > 0) {// 如果大于0，表示当前线程多次获取了该锁，释放锁通过count减一来模拟 count--; } else {// 如果count==0，可以将锁释放，这样就能保证获取锁的次数与释放锁的次数是一致的了。 cas.compareAndSet(cur, null); } } } } TicketLock 解决公平性的问题。 public class TicketLockV2 { /** * 服务号 */ private AtomicInteger serviceNum = new AtomicInteger(); /** * 排队号 */ private AtomicInteger ticketNum = new AtomicInteger(); /** * 新增一个ThreadLocal，用于存储每个线程的排队号 */ private ThreadLocal<Integer> ticketNumHolder = new ThreadLocal<Integer>(); public void lock() { int currentTicketNum = ticketNum.getAndIncrement(); ticketNumHolder.set(currentTicketNum); System.out.println("LOCK " + Thread.currentThread().getName() + " currentTicketNum：" + currentTicketNum + " serviceNum：" + serviceNum.get()); while (currentTicketNum != serviceNum.get()) { Thread.onSpinWait(); // 添加自旋优化 } } public void unlock() { Integer currentTickNum = ticketNumHolder.get(); if (currentTickNum == null) { throw new IllegalMonitorStateException("应该先获取锁再释放锁"); } if (!serviceNum.compareAndSet(currentTickNum, currentTickNum + 1)) { throw new IllegalMonitorStateException("当前线程并未持有锁"); } System.out.println("UNLOCK " + Thread.currentThread().getName() + "- currentTicketNum：" + currentTickNum + "- serviceNum：" + serviceNum.get()); ticketNumHolder.remove(); // 清理ThreadLocal，防止内存泄漏 } } public class TicketLockV2Example { private static int counter = 0; private static final TicketLockV2 lock = new TicketLockV2(); public static void main(String[] args) throws InterruptedException { // 创建多个线程进行测试 int threadCount = 5; Thread[] threads = new Thread[threadCount]; for (int i = 0; i…

同城灾备、同城双活、两地三中心、异地多活
type: item
date: 2024-02-18T10:02:00+00:00
description: 一、系统可用性 从架构设计的原则说起。 好的软件架构应该遵循以下 3 个原则：高性能、高可用、易扩展。 高可用通常用 2 个指标来衡量： 平均故障间隔 MTBF（Mean Time Between Failure）：表示两次故障的间隔时间，也就是系统「正常运行」的平均时间，这个时间越长，说明系统稳定性越高 故障恢复时间 MTTR（Mean Time To Repair）：表示系统发生故障后「恢复的时间」，这个值越小，故障对用户的影响越小 可用性与这两者的关系：可用性（Availability）= MTBF / (MTBF + MTTR) * 100%。 这个公式得出的结果是一个「比例」，通常我们会用「N 个 9」来描述一个系统的可用性。 系统发生故障其实是不可避免的，尤其是规模越大的系统，发生问题的概率也越大。这些故障一般体现在 3 个方面： 硬件故障：CPU、内存、磁盘、网卡、交换机、路由器 软件问题：代码 Bug、版本迭代 不可抗力：地震、水灾、火灾、战争 这些风险随时都有可能发生。所以，在面对故障时，我们的系统能否以「最快」的速度恢复，就成为了可用性的关键。 下面，从一个最简单的系统出发，一步步演化出一个支持「异地多活」的系统架构。过程中，会看到一个系统会遇到哪些可用性问题，以及为什么架构要这样演进，从而理解异地多活架构的意义。 二、单机架构 业务处于起步阶段，体量非常小，架构如下： 这个架构模型非常简单，客户端请求进来，业务应用读写数据库，返回结果，非常好理解。 但需要注意的是，这里的数据库是「单机」部署的，所以它有一个致命的缺点：一旦遭遇意外，例如磁盘损坏、操作系统异常、误删数据，那这意味着所有数据就全部「丢失」了，这个损失是巨大的。 可以对数据做备份，把数据库文件「定期」cp 到另一台机器上，这样，即使原机器丢失数据，你依旧可以通过备份把数据「恢复」回来，以此保证数据安全。 这个方案实施起来虽然比较简单，但存在 2 个问题： 恢复需要时间：业务需先停机，再恢复数据，停机时间取决于恢复的速度，恢复期间服务「不可用」 数据不完整：因为是定期备份，数据肯定不是「最新」的，数据完整程度取决于备份的周期 很明显，数据库越大，意味故障恢复时间越久。那按照前面提到的「高可用」标准，这个方案可能连 1 个 9 都达不到，远远无法满足我们对可用性的要求。 那有什么更好的方案，既可以快速恢复业务？还能尽可能保证数据完整性呢？ 这时可以采用这个方案：主从副本。 三、主从副本 可以在另一台机器上，再部署一个数据库实例，让这个新实例成为原实例的「副本」，让两者保持「实时同步」，就像这样： 一般把原实例叫作主库（master），新实例叫作从库（slave）。这个方案的优点在于： 数据完整性高：主从副本实时同步，数据「差异」很小。 抗故障能力提升：主库有任何异常，从库可随时「切换」为主库，继续提供服务。 读性能提升：业务应用可直接读从库，分担主库「压力」读压力。 这个方案不错，不仅大大提高了数据库的可用性，还提升了系统的读性能。 同样的思路，「业务应用」也可以在其它机器部署一份，避免单点。因为业务应用通常是「无状态」的（不像数据库那样存储数据），所以直接部署即可，非常简单。 因为业务应用部署了多个，所以现在还需要部署一个「接入层」，来做请求的「负载均衡」（一般会使用 nginx 或 LVS），这样当一台机器宕机后，另一台机器也可以「接管」所有流量，持续提供服务。 从这个方案可以看出，提升可用性的关键思路就是：冗余。 没错，担心一个实例故障，那就部署多个实例，担心一个机器宕机，那就部署多台机器。 到这里，架构基本已演变成主流方案了，之后开发新的业务应用，都可以按照这种模式去部署。 四、风险不可控 按照前面的分析，为了避免单点故障，应用虽然部署了多台机器，但这些机器的分布情况，我们并没有去深究。 而一个机房有很多服务器，这些服务器通常会分布在一个个「机柜」上，如果你使用的这些机器，刚好在一个机柜，还是存在风险。 如果恰好连接这个机柜的交换机 / 路由器发生故障，那么你的应用依旧有「不可用」的风险。 虽然交换机 / 路由器也做了路线冗余，但不能保证一定不出问题。 部署在一个机柜有风险，那把这些机器打散，分散到不同机柜上，是不是就没问题了？ 这样确实会大大降低出问题的概率。但我们依旧不能掉以轻心，因为无论怎么分散，它们总归还是在一个相同的环境下：机房。 即使机房级别的防护已经做得足够好，但只要有「概率」出问题，那现实情况就有可能发生。虽然概率很小，但一旦真的发生，影响之大可见一斑。 看到这里你可能会想，机房出现问题的概率也太小了吧，工作了这么多年，也没让我碰上一次，有必要考虑得这么复杂吗？ 但你有没有思考这样一个问题：不同体量的系统，它们各自关注的重点…

ClickHouse
type: item
date: 2024-01-30T02:06:00+00:00
description: 一、ClickHouse 入门 ClickHouse 是俄罗斯的 Yandex 于 2016 年开源的列式存储数据库（DBMS），使用 C++语言编写，主要用于在线分析处理查询（OLAP），能够使用 SQL 查询实时生成分析数据报告。 1.1、ClickHouse 的特点 1.1.1、列式存储 以下面的表为例： Id Name Age 1 张三 18 2 李四 22 3 王五 34 1）采用行式存储时，数据在磁盘上的组织结构为： 好处是想查某个人所有的属性时，可以通过一次磁盘查找加顺序读取就可以。但是当想查所有人的年龄时，需要不停的查找，或者全表扫描才行，遍历的很多数据都是不需要的。 2）采用列式存储时，数据在磁盘上的组织结构为： 3）列式储存的好处： 对于列的聚合，计数，求和等统计操作原因优于行式存储。 由于某一列的数据类型都是相同的，针对于数据存储更容易进行数据压缩，每一列选择更优的数据压缩算法，大大提高了数据的压缩比重。 由于数据压缩比更好，一方面节省了磁盘空间，另一方面对于 cache 也有了更大的发挥空间。 1.1.2、DBMS 的功能 几乎覆盖了标准 SQL 的大部分语法，包括 DDL 和 DML，以及配套的各种函数，用户管理及权限管理，数据的备份与恢复。 1.1.3、多样化引擎 ClickHouse 和 MySQL 类似，把表级的存储引擎插件化，根据表的不同需求可以设定不同的存储引擎。目前包括合并树、日志、接口和其他四大类 20 多种引擎。 1.1.4、高吞吐写入能力 ClickHouse 采用类 LSM Tree的结构，数据写入后定期在后台 Compaction。通过类 LSM tree的结构，ClickHouse 在数据导入时全部是顺序 append 写，写入后数据段不可更改，在后台compaction 时也是多个段 merge sort 后顺序写回磁盘。顺序写的特性，充分利用了磁盘的吞吐能力，即便在 HDD 上也有着优异的写入性能。 官方公开 benchmark 测试显示能够达到 50MB-200MB/s 的写入吞吐能力，按照每行100Byte 估算，大约相当于 50W-200W 条/s 的写入速度。 1.1.5、数据分区与线程级并行 ClickHouse 将数据划分为多个 partition，每个 partition 再进一步划分为多个 index granularity(索引粒度)，然后通过多个 CPU核心分别处理其中的一部分来实现并行数据处理。在这种设计下，单条 Query 就能利用整机所有 CPU。极致的并行处理能力，极大的降低了查询延时。 所以，ClickHouse 即使对于大量数据的查询也能够化整为零平行处理。但是有一个弊端就是对于单条查询使用多 cpu，就不利于同时并发多条查询。所以对于高 qps 的查询业务，ClickHouse 并不是强项。 1.1.6、性能对比 ClickHouse 像很多 OLAP 数据库一样，单表查询速度优于关联查询，而且 ClickHouse 的两者差距更为明显。 二、ClickHouse 的安装 2.1、准备工作 2.1.1、确定防火墙处于关闭状态 2.1.2、CentOS 取消打开文件数限制 （1）在 hadoop102 的 /etc/security/limits.conf 文件的末尾加入以下内容 [atguigu@hadoop102 ~]$ sudo vim /etc/security/limits.conf * soft nofile 65536 * hard nofile 65536 * soft nproc 131072 * hard nproc 131072 （2）在 hadoop102 的/etc/security/limits.d/20-nproc.conf 文件的末尾加入以下内容 [atguigu@hadoop102 ~]$ sudo vim /etc/security/limits.d/20-nproc.conf * soft nofile 65536 * hard nofile 65536 * soft nproc 131072 * hard nproc 131072 2.1.3、安装依赖 sudo yum install -y libtool sudo yum install -y *unixODBC* 2.1.4、CentOS 取消 SELINUX （1）修改/etc/selinux/config 中的 SELINUX=disabled [atguigu@hadoop102 ~]$ sudo vim /etc/selinux/config SELINUX=disabled 2.2、单机安装 官网：https://clickhouse.t…

SpringBoot启动配置原理
type: item
date: 2023-10-04T12:27:00+00:00
description: 一、创建SpringApplication对象 initialize(sources);//创建对象 private void initialize(Object[] sources) { //【保存主配置类】 if (sources != null && sources.length > 0) { this.sources.addAll(Arrays.asList(sources)); } //【判断当前是否一个web应用】 this.webEnvironment = deduceWebEnvironment(); //从类路径下找到META-INF/spring.factories配置的所有ApplicationContextInitializer；然后保存起来 setInitializers((Collection) getSpringFactoriesInstances( ApplicationContextInitializer.class)); //从类路径下找到ETA-INF/spring.factories配置的所有ApplicationListener setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //从多个配置类中找到有main方法的主配置类 this.mainApplicationClass = deduceMainApplicationClass(); } 拿到初始化器： 保存初始化器和监听器： 二、运行run方法 public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; FailureAnalyzers analyzers = null; configureHeadlessProperty(); //获取SpringApplicationRunListeners；从类路径下META-INF/spring.factories SpringApplicationRunListeners listeners = getRunListeners(args); //回调所有的获取SpringApplicationRunListener.starting()方法 listeners.starting(); try { //封装命令行参数 ApplicationArguments applicationArguments = new DefaultApplicationArguments( args); //准备环境 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); //创建环境完成后回调SpringApplicationRunListener.environmentPrepared()；表示环境准备完成 Banner printedBanner = printBanner(environment); //创建ApplicationContext；决定创建web的ioc还是普通的ioc context = createApplicationContext(); analyzers = new FailureAnalyzers(context); //准备上下文环境;将environment保存到ioc中；而且applyInitializers()； //applyInitializers()：回调之前保存的所有的ApplicationContextInitializer的initialize方法 //回调所有的SpringApplicationRunListener的contextPrepared()； // prepareContext(context, environment, listeners, applicationArguments, printedBanner); //prepareContext运行完成以后回调所有的SpringApplicationRunListener的contextLoaded（）； //s刷新容器；ioc容器初始化（如果是web应用还会创建嵌入式的Tomcat）；Spring注解版 //扫描，创建，加载所有组件的地方；（配置类，组件，自动配置） refreshC…

Btree 索引详解
type: item
date: 2023-10-04T03:03:00+00:00
description: Btree索引 Btree索引(或Balanced Tree)，是一种很普遍的数据库索引结构，oracle默认的索引类型（本文也主要依据oracle来讲）。其特点是定位高效、利用率高、自我平衡，特别适用于高基数字段，定位单条或小范围数据非常高效。理论上，使用Btree在亿条数据与100条数据中定位记录的花销相同。 数据结构利用率高、定位高效。 Btree索引的数据结构如下： 结构看起来Btree索引与Binary Tree相似，但在细节上有所不同，上图中用不同颜色的标示出了Btree索引的几个主要特点： 树形结构：由根节(root)、分支(branches)、叶(leaves)三级节点组成，其中分支节点可以有多层。 多分支结构：与binary tree不相同的是，btree索引中单root/branch可以有多个子节点(超过2个)。 双向链表：整个叶子节点部分是一个双向链表(后面会描述这个设计的作用)。 单个数据块中包括多条索引记录。 这里先把几个特点罗列出来，后面会说到各自的作用。 结构上Btree与Binary Tree的区别，在于binary中每节点代表一个数值，而balanced中root和Btree节点中记录了多条”值范围”条目(如:[60-70][70-80])，这些”值范围”条目分别指向在其范围内的叶子节点。既root与branch可以有多个分支，而不一定是两个，对数据块的利用率更高。 在Leaf节点中，同样也是存放了多条索引记录，这些记录就是具体的索引列值，和与其对应的rowid。另外，在叶节点层上，所有的节点在组成了一个双向链表。 了解基本结构后，下图展示定位数值82的过程： 演算如下： 读取root节点，判断82大于在0-120之间，走左边分支。 读取左边branch节点，判断82大于80且小于等于120，走右边分支。 读取右边leaf节点，在该节点中找到数据82及对应的rowid。 使用rowid去物理表中读取记录数据块(如果是count或者只select rowid，则最后一次读取不需要)。 在整个索引定位过程中，数据块的读取只有3次。既三次I/O后定位到rowid。 而由于Btree索引对结构的利用率很高，定位高效。当1千万条数据时，Btree索引也是三层结构(依稀记得亿级数据才是3层与4层的分水岭)。定位记录仍只需要三次I/O，这便是开头所说的，100条数据和1千万条数据的定位，在btree索引中的花销是一样的。 平衡扩张 除了利用率高、定位高效外，Btree的另一个特点是能够永远保持平衡，这与它的扩张方式有关。(unbalanced和hotspot是两类问题)，先描述下Btree索引的扩张方式： 新建一个索引，索引上只会有一个leaf节点，取名为Node A，不断的向这个leaf节点中插入数据后，直到这个节点满，这个过程如下图（绿色表示新建/空闲状态，红色表示节点没有空余空间）： 当Node A满之后，我们再向表中插入一条记录，此时索引就需要做拆分处理：会新分配两个数据块NodeB & C，如果新插入的值，大于当前最大值，则将Node A中的值全部插入Node B中，将新插入的值放到Node C中；否则按照5-5比例，将已有数据分别插入到NodeB与C中。 无论采用哪种分割方式，之前的leaf节点A，将变成一个root节点，保存两个范围条目，指向B与C，结构如下图（按第一种拆分形式）： 当Node C满之后，此时 Node A仍有空余空间存放条目，所以不需要再拆分，而只是新分配一个数据块Node D，将在Node A中创建指定到Node D的条目： 如果当根节点Node A也满了，则需要进一步拆分：新建Node E&F&G，将Node A中范围条目拆分到E&F两个节点中，并建立E&F到BCD节点的关联，向Node G插入索引值。此时E&F为branch节点，G为leaf节点，A为Root节点： 在整个扩张过程中，Btree自身总能保持平衡，Leaf节点的深度能一直保持一致。 实际应用中的一些问题 前面说完了Btree索引的结构与扩张逻辑，接下来讲一些Btree索引在应用中的一些问题： 单一方向扩展引起的索引竞争(Index Contention) 若索引列使用sequence或者timestamp这类只增不减的数据类型。这种情况下Btree索引的增长方向总是不变的，不断的向右边扩展，因为新插入的值永远是最大的。 当一个最大值插入到leaf block中后，leaf block要向上传播，通知上层节点更新所对应的“值范围”条目中的最大值，因此所有靠右边的block(从leaf 到branch甚至root)都需要做更新操作，并且可能因为块写满后执行块拆分。 如果并发插入多个最大值，则最右边索引数据块的…

MySQL 索引 B-Tree（B+Tree）
type: item
date: 2023-10-03T16:19:00+00:00
description: 一、索引的分类 1、从存储结构上来划分：BTree索引（B-Tree或B+Tree索引），Hash索引，full-index全文索引，R-Tree索引。这里所描述的是索引存储时保存的形式。 2、从应用层次来分：普通索引，唯一索引，复合索引。 3、根据中数据的物理顺序与键值的逻辑（索引）顺序关系：聚集索引，非聚集索引。 1中所描述的是索引存储时保存的形式，2是索引使用过程中进行的分类，两者是不同层次上的划分。不过平时讲的索引类型一般是指在应用层次的划分。 普通索引：即一个索引只包含单个列，一个表可以有多个单列索引。 唯一索引：索引列的值必须唯一，但允许有空值。 复合索引：即一个索引包含多个列。 聚簇索引(聚集索引)：并不是一种单独的索引类型，而是一种数据存储方式。具体细节取决于不同的实现，InnoDB的聚簇索引其实就是在同一个结构中保存了B-Tree索引(技术上来说是B+Tree)和数据行。 非聚簇索引：不是聚簇索引，就是非聚簇索引。 二、索引的底层实现 MySQL 默认存储引擎 innodb 只显式支持 B-Tree( 从技术上来说是B+Tree)索引，对于频繁访问的表，innodb 会透明建立自适应 hash 索引，即在 B 树索引基础上建立 hash 索引，可以显著提高查找效率，对于客户端是透明的，不可控制的，隐式的。 2.1、Hash索引 基于哈希表实现，只有精确匹配索引所有列的查询才有效，对于每一行数据，存储引擎都会对所有的索引列计算一个哈希码（hash code），并且Hash索引将所有的哈希码存储在索引中，同时在索引表中保存指向每个数据行的指针。 2.2、B-Tree索引（MySQL使用B+Tree） B-Tree能加快数据的访问速度，因为存储引擎不再需要进行全表扫描来获取数据，数据分布在各个节点之中。 2.3、B+Tree索引 是B-Tree的改进版本，同时也是数据库索引索引所采用的存储结构。数据都在叶子节点上，并且增加了顺序访问指针，每个叶子节点都指向相邻的叶子节点的地址。相比 B-Tree 来说，进行范围查找时只需要查找两个节点，进行遍历即可。而 B-Tree 需要获取所有节点，相比之下 B+Tree 效率更高。 结合存储引擎来讨论（一般默认使用 B+Tree） 案例：假设有一张学生表，id为主键 id name birthday 1 Tom 1996-01-01 2 Jann 1996-01-04 3 Ray 1996-01-08 4 Michael 1996-01-10 5 Jack 1996-01-13 6 Steven 1996-01-23 7 Lily 1996-01-25 在MyISAM引擎中的实现（二级索引也是这样实现的） 在InnoDB中的实现 三、问题 问：为什么索引结构默认使用 B-Tree，而不是 hash，二叉树，红黑树？ hash：虽然可以快速定位，但是没有顺序，IO 复杂度高。 二叉树：树的高度不均匀，不能自平衡，查找效率跟数据有关（树的高度），并且IO代价高。 红黑树：树的高度随着数据量增加而增加，IO 代价高。 问：为什么官方建议使用自增长主键作为索引。 结合 B+Tree 的特点，自增主键是连续的，在插入过程中尽量减少页分裂，即使要进行页分裂，也只会分裂很少一部分。并且能减少数据的移动，每次插入都是插入到最后。总之就是减少分裂和移动的频率。