JVM
1、 垃圾回收器Cms、G1
2、 Jvm五大内存结构,程序计数器、java栈、本地方法栈、堆、元空间(方法区)
3、 堆区E、S1、S2、老年代的垃圾回收算法:标记&清除、标记&整理、复制算法
4、 字节码的增强技术,ASM的应用场景有AOP,cglib就是基于ASM,ASM可以直接操作字节码,动态修改类的行为
5、 类加载机制,双亲委托,BootStrapClassload,扩展类加载、Application类加载器、自定义类加载器 如何打破双亲委托,通过自定义类加载器可以打破,继承ClassLoader类并重写loadClass。 通过SPI机制,使用ServiceLoader.load方法可以自动加载类路径下的META-INF/services文件夹中定义的类,这种方式在很多框架扩展中都有应用。通过这种方式,可以绕过双亲委派模型的限制,加载特定的类。
6、 Java内存模型 Cpu寄存器、L1L2L3缓存、内存(主寸)。 为了优化程序性能,编译器和处理器会对程序指令进行重排序,这种重排序在单线程模式下是没有任何问题的,多线程可能会出现可见性的问题,需要通过内存屏障去禁止重排序 有四种内存屏障,storestore storeload loadstore loadload 其中storeload是万能的,也是性能最差的
7、 Volatile关键字,就是通过在读写的前后,进行了这四种屏障的插入,保证了可见效。从规则的定义可知,如果多个线程同时操作volatile变量,那么对该变量的写操作必须在读操作之前执行(禁止重排序),并且写操作的结果对读操作可见(强缓存一致性)。volatile写是在前面和后面分别插入内存屏障,而volatile读操作是在后面插入两个内存屏障。 在每个 volatile 写操作的前面插入一个 StoreStore 屏障。在每个 volatile 写操作的后面插入一个 StoreLoad 屏障。在每个 volatile 读操作的后面插入一个 LoadLoad 屏障。在每个 volatile 读操作的后面插入一个 LoadStore 屏障。
8、 记一次OOM故障的排查过程 JVM的一些指令 jmap、jstat、top 、HeapDump 阿里巴巴arthas工具 遇到OOM之后重点还是离线分析堆内存快照 自动产生OOM快照:
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=log/dump/
手动生成: jmap -dump:format=b,file=message-23973.hprof 23973
jstat -gcutil pid 查看垃圾回收情况
用jvisualvm,查看大对象。
9、标签服务, QlExpress的缓存无法gc,导致OOM
JAVA集合框架
1、 HashMap
谈谈你理解的 HashMap,讲讲其中的 get put 过程?
1.8 做了什么优化?
是线程安全的嘛?
不安全会导致哪些问题?
如何解决?有没有线程安全的并发容器?
ConcurrentHashMap 是如何实现的? 1.7、1.8 实现有何不同?为什么这么做?
为什么resize总是2的幂次方,因为是通过&来计算的,每次只要关注高位即可
JAVA并发
1、 Synchronized 简单来说在JVM中monitorenter和monitorexit字节码依赖于底层的操作系统的Mutex Lock来实现的,但是由于使用Mutex Lock需要将当前线程挂起并从用户态切换到内核态来执行,这种切换的代价是非常昂贵的;然而在现实中的大部分情况下,同步方法是运行在单线程环境(无锁竞争环境)如果每次都调用Mutex Lock那么将严重的影响程序的性能。不过在jdk1.6中对锁的实现引入了大量的优化,如锁粗化(Lock Coarsening)、锁消除(Lock Elimination)、轻量级锁(Lightweight Locking)、偏向锁(Biased Locking)、适应性自旋(Adaptive Spinning)等技术来减少锁操作的开销。
2、 CAS CAS操作是原子性的,所以多线程并发使用CAS更新数据时,可以不使用锁。JDK中大量使用了CAS来更新数据而防止加锁(synchronized 重量级锁)来保持原子更新。CAS会自旋,不会释放cpu。 AtomicStampedReference来解决ABA问题。这个类的compareAndSet方法的作用是首先检查当前引用是否等于预期引用,并且检查当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值 AtomicReference类来保证引用对象之间的原子性,就可以把多个变量放在一个对象里来进行CAS操作。 CAS循环时间大,怎么解决? 对于资源竞争严重(线程冲突严重)的情况,CAS 自旋的概率会比较大,从而浪费更多的 CPU 资源,效率低于 synchronized。
3、 LockSupport LockSupport只有一个私有构造函数,无法被实例化。 如果在park()之前执行了unpark()会怎样?线程不会被阻塞,直接跳过park(),继续执行后续内容# LockSupport.park()会释放锁资源吗?不会,它只负责阻塞当前线程,释放锁资源实际上是在Condition的await()方法中实现的。
4、CompletableFuture是高级的多线程功能,支持自定义线程池和系统默认的线程池forkJoin,是多线程,高并发里面,经常需要用到的比直接创建线程,要简单易用的方法。
JAVA IO
输入流、输出流、字节流、字符流
大量使用了装饰器模式
inputStream/Reader
OutputStrean/Writer
BufferedInputStream和BufferedOutputStream 缓存操作
FileInputStream f = new FileInputStream(filePath);
BufferedInputStream bf = new BufferedInputStream(f);
BIO:同步并阻塞
流失单向的,服务器为每一个新连接建立一个新线程处理,如果连接没有数据也会阻塞等待。
NIO:同步非阻塞
面向channel,客户端发送的请求连接会注册到多路复用器上,多路复用器轮训到有io请求时处理。
nio: client->buffer->channel <-> channel -> buffer ->server。 selector
多个channel以事件的方式注册到同一个selector,这样selector可监控多个通道是否有事件发生,一个线程管理多个通道的连接和请求。
buffer: position、limit、capacity flush();
关于零拷贝
在 Java NIO 中的通道(Channel)就相当于操作系统的内核空间(kernel space)的缓冲区,而缓冲区(Buffer)对应的相当于操作系统的用户空间(user space)中的用户缓冲区(user buffer)。
- 通道(Channel)是全双工的(双向传输),它既可能是读缓冲区(read buffer),也可能是网络缓冲区(socket buffer)。
- 缓冲区(Buffer)分为堆内存(HeapBuffer)和堆外内存(DirectBuffer),这是通过 malloc() 分配出来的用户态内存。
堆外内存(DirectBuffer)在使用后需要应用程序手动回收,而堆内存(HeapBuffer)的数据在 GC 时可能会被自动回收。因此,在使用 HeapBuffer 读写数据时,为了避免缓冲区数据因为 GC 而丢失,NIO 会先把 HeapBuffer 内部的数据拷贝到一个临时的 DirectBuffer 中的本地内存(native memory),这个拷贝涉及到 sun.misc.Unsafe.copyMemory() 的调用,背后的实现原理与 memcpy() 类似。 最后,将临时生成的 DirectBuffer 内部的数据的内存地址传给 I/O 调用函数,这样就避免了再去访问 Java 对象处理 I/O 读写。
Spring
IOC: 控制反转,通过容器管理bean的生命周期
AOP:面向切面编程
Spring默认在目标类实现接口时是通过JDK代理实现的,只有非接口的是通过Cglib代理实现的。当设置proxy-target-class为true时在目标类不是接口或者代理类时优先使用cglib代理实现。
循环依赖:
1、spirng只能解决单例模式下属性依赖的循环问题,spring为了解决循环依赖问题,使用了三级缓存。分别是:
singletomObjects: 成熟对象缓存map
earlySingletonObjects: 尚未属性赋值,半成品对象
singletomFactories:单例工厂的缓存
分析getSingleton()的整个过程,Spring首先从一级缓存singletonObjects中获取。若是获取不到,而且对象正在建立中,就再从二级缓存earlySingletonObjects中获取。若是仍是获取不到且容许singletonFactories经过getObject()获取,就从三级缓存singletonFactory.getObject()(三级缓存)获取,若是获取到了则从三级缓存移动到了二级缓存。
此处就是解决循环依赖的关键,这段代码发生在createBeanInstance以后,也就是说单例对象此时已经被建立出来的。这个对象已经被生产出来了,虽然还不完美(尚未进行初始化的第二步和第三步),可是已经能被人认出来了(根据对象引用能定位到堆中的对象),因此Spring此时将这个对象提早曝光出来让你们认识,让你们使用。
好比“A对象setter依赖B对象,B对象setter依赖A对象”,A首先完成了初始化的第一步,而且将本身提早曝光到singletonFactories中,此时进行初始化的第二步,发现本身依赖对象B,此时就尝试去get(B),发现B尚未被create,因此走create流程,B在初始化第一步的时候发现本身依赖了对象A,因而尝试get(A),尝试一级缓存singletonObjects(确定没有,由于A还没初始化彻底),尝试二级缓存earlySingletonObjects(也没有),尝试三级缓存singletonFactories,因为A经过ObjectFactory将本身提早曝光了,因此B可以经过ObjectFactory.getObject拿到A对象(半成品),B拿到A对象后顺利完成了初始化阶段一、二、三,彻底初始化以后将本身放入到一级缓存singletonObjects中。此时返回A中,A此时能拿到B的对象顺利完成本身的初始化阶段二、三,最终A也完成了初始化,进去了一级缓存singletonObjects中,并且更加幸运的是,因为B拿到了A的对象引用,因此B如今hold住的A对象完成了初始化。
Mybatis
Mybatis 是一个半 ORM(对象关系映射)框架,它内部封装了 JDBC,开发时只需要关注 SQL 语句本身,不需要花费精力去处理加载驱动、创建连接、创建statement 等繁杂的过程。程序员直接编写原生态 sql,可以严格控制 sql 执行性能,灵活度高。
sqlSessionFactory-》sqlSession-〉执行器-》结果处理与映射
参数映射、sql解析、sql执行、结果处理与映射
预编译
-
定义:
- SQL 预编译指的是数据库驱动在发送 SQL 语句和参数给 DBMS 之前对 SQL 语句进行编译,这样 DBMS 执行 SQL 时,就不需要重新编译。
-
为什么需要预编译
- JDBC 中使用对象 PreparedStatement 来抽象预编译语句,使用预编译。预编译阶段可以优化 SQL 的执行。预编译之后的 SQL 多数情况下可以直接执行,DBMS 不需要再次编译,越复杂的SQL,编译的复杂度将越大,预编译阶段可以合并多次操作为一个操作。同时预编译语句对象可以重复利用。把一个 SQL 预编译后产生的 PreparedStatement 对象缓存下来,下次对于同一个SQL,可以直接使用这个缓存的 PreparedState 对象。Mybatis默认情况下,将对所有的 SQL 进行预编译。
- 还有一个重要的原因,防止SQL注入
简述Mybatis的插件运行原理,以及如何编写一个插件。
-
Mybatis仅可以编写针对ParameterHandler、ResultSetHandler、StatementHandler、Executor这4种接口的插件,Mybatis使用JDK的动态代理,为需要拦截的接口生成代理对象以实现接口方法拦截功能,每当执行这4种接口对象的方法时,就会进入拦截方法,具体就是InvocationHandler的invoke()方法,当然,只会拦截那些你指定需要拦截的方法。
-
实现Mybatis的Interceptor接口并复写intercept()方法,然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可,记住,别忘了在配置文件中配置你编写的插件。
Mybatis的一级缓存、二级缓存
MyBatis 是一个持久层框架,它提供了一级缓存和二级缓存来优化数据库访问性能。
一级缓存是指在同一个 SqlSession 中的缓存,它默认开启,并且可以提高查询性能。当查询相同的数据时,MyBatis 会先从一级缓存中查找,如果找到则直接返回,减少了对数据库的访问次数。
二级缓存是指在同一个 namespace(即同一个 Mapper 接口)下的缓存,它可以跨越多个 SqlSession 使用。二级缓存需要手动开启,可以跨越多个 SqlSession,对于频繁查询且不经常变化的数据,可以提高整体性能。
使用缓存有助于减少数据库访问次数,提高性能,但需要谨慎使用,特别是对于频繁更新的数据,需要考虑缓存的及时更新和失效策略。
-
缓存内容:一级缓存缓存的是执行过的 SQL 语句的查询结果以及相应的参数。
-
键值对:一级缓存的键由 SQL 语句的内容(包括语句的 id 和参数)组成,值则是查询结果对象。
Mysql
MySQL架构
1、Client Connectors 连接层
主要完成一些类似于连接处理、授权认证、及相关的安全方案。
2、Service 服务层
核心服务功能,包括查询解析、分析、优化、缓存、以及所有的内置函数,所有跨存储引擎的功能也都在这一层实现,包括触发器、存储过程、视图等
3、引擎层
存储引擎层,存储引擎真正的负责了MySQL中数据的存储和提取,服务器通过API与存储引擎进行通信。不同的存储引擎具有的功能不同,这样我们可以根据自己的实际需要进行选取
4、存储层
主要是将数据存储在运行于该设备的文件系统之上,并完成与存储引擎的交互
sql的执行过程:
请求——》连接器(权限校验等)——〉缓存——》分析器(语法分析)-〉优化器
-》执行器-〉去引擎层获取数据返回
存储引擎
InnoDB、MyISAM、Memory
InnoDB 现在是 MySQL 默认的存储引擎,支持事务、行级锁定和外键**
**
MyISAM 物理文件结构为:
.frm文件:与表相关的元数据信息都存放在frm文件,包括表结构的定义信息等.MYD(MYData) 文件:MyISAM 存储引擎专用,用于存储MyISAM 表的数据.MYI(MYIndex)文件:MyISAM 存储引擎专用,用于存储MyISAM 表的索引相关信息
InnoDB 物理文件结构为:
-
.frm文件:与表相关的元数据信息都存放在frm文件,包括表结构的定义信息等 -
.ibd文件或.ibdata文件: 这两种文件都是存放 InnoDB 数据的文件,之所以有两种文件形式存放 InnoDB 的数据,是因为 InnoDB 的数据存储方式能够通过配置来决定是使用共享表空间存放存储数据,还是用独享表空间存放存储数据。
区别:
InnoDB 支持事务,MyISAM 不支持事务
InnoDB 支持外键,而 MyISAM 不支持
InnoDB 是聚簇索引,MyISAM 是非聚簇索引
MyISAM 是非聚集索引,数据文件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的
InnoDB不保存表的行数,myisam保存,这跟 InnoDB 的事务特性有关,由于多版本并发控制(MVCC)的原因
InnoDB 最小的锁粒度是行锁,MyISAM 最小的锁粒度是表锁
索引
-
索引本身也很大,不可能全部存储在内存中,一般以索引文件的形式存储在磁盘上
-
主键索引,唯一且不能为null
-
唯一索引,除null之外,唯一
-
普通索引,全文索引
优点:
加快查询、
缺点:
占用存储,update也要更新索引,性能下降
首先要明白索引(index)是在存储引擎(storage engine)层面实现的,而不是server层面。
MyISAM 和 InnoDB 存储引擎,都使用 B+Tree的数据结构,它相对与 B-Tree结构,所有的数据都存放在叶子节点上,且把叶子节点通过指针连接到一起,形成了一条数据链表,以加快相邻数据的检索效率。
B-tree,每个节点都存储data
B+tree,只有叶子结点存储data
从上一节中的B-Tree结构图中可以看到每个节点中不仅包含数据的key值,还有data值。而每一个页的存储空间是有限的,如果data数据较大时将会导致每个节点(即一个页)能存储的key的数量很小,当存储的数据量很大时同样会导致B-Tree的深度较大,增大查询时的磁盘I/O次数,进而影响查询效率。在B+Tree中,所有数据记录节点都是按照键值大小顺序存放在同一层的叶子节点上,而非叶子节点上只存储key值信息,这样可以大大加大每个节点存储的key值数量,降低B+Tree的高度。IO次数取决于b+数的高度h
**索引的最左匹配特性。
**
**为什么非主键索引结构叶子节点存储的是主键值?
保证数据一致性和节省存储空间
**
哪些要加索引
1、主键、频繁作为查询条件的字段、order by的字段、group by的字段
哪些不要加索引
1、表记录少的、经常改的更新的、数据重复高的(age),where条件用不到的
使用explain,可以通过输出的extra列来判断,对于一个索引覆盖查询,显示为using index,MySQL查询优化器在执行查询前会决定是否有索引覆盖查询
count(1)、count(*)、count(列名),in和exists
事务
MVCC 多版本并发控制
InnoDB 的 MVCC,是通过在每行记录后面保存两个隐藏的列来实现。这两个列,一个保存了行的创建时间,一个保存行的过期时间(删除时间)。当然存储的并不是真实的时间,而是系统版本号(system version number)。每开始一个新的事务,系统版本号都会自动递增。事务开始时刻的系统版本号会作为事务的版本号,用来和查询到的每行记录的版本号进行比较。
-
InnoDB只查找版本早于当前事务版本的数据行,这样可以确保事务读取的行,要么是在开始事务之前已经存在要么是事务自身插入或者修改过的
-
行的删除版本号要么未定义,要么大于当前事务版本号,这样可以确保事务读取到的行在事务开始之前未被删除
**事务是如何通过日志来实现的,说得越深入越好?
**
**重做日志redo和回滚日志undo
**
事务开启时,事务中的操作,都会先写入存储引擎的日志缓冲中,在事务提交之前,这些缓冲的日志都需要提前刷新到磁盘上持久化,这就是DBA们口中常说的“日志先行”(Write-Ahead Logging)。当事务提交之后,在Buffer Pool中映射的数据文件才会慢慢刷新到磁盘。此时如果数据库崩溃或者宕机,那么当系统重启进行恢复时,就可以根据redo log中记录的日志,把数据库恢复到崩溃前的一个状态。未完成的事务,可以继续提交,也可以选择回滚,这基于恢复的策略而定。
undo log记录了数据在每个操作前的状态,如果事务执行过程中需要回滚,就可以根据undo log进行回滚操作。单个事务的回滚,只会回滚当前事务做的操作,并不会影响到其他的事务做的操作。,redo_log是恢复提交事务修改的页操作,而undo_log是回滚行记录到特定版本。二者记录的内容也不同,redo_log是物理日志,记录页的物理修改操作,而undo_log是逻辑日志,根据每行记录进行记录。
1、读锁、写锁
2、锁粒度的概念
表级锁:开销小,加锁快
行级锁:开销大,加锁慢;会出现死锁;
页面锁:开销和加锁时间界于表锁和行锁之间
InnoDB 行锁
InnoDB 实现了以下两种类型的行锁:
- 共享锁(S):允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。
- 排他锁(X):允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排他写锁。
为了允许行锁和表锁共存,实现多粒度锁机制,InnoDB 还有两种内部使用的意向锁(Intention Locks),这两种意向锁都是表锁:
- 意向共享锁(IS):事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的 IS 锁。
- 意向排他锁(IX):事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的 IX 锁。
索引失效会导致行锁变表锁。比如 vchar 查询不写单引号的情况。
记录锁(Record Locks)
SELECT * FROM table WHERE id = 1 FOR UPDATE;
间隙锁(Gap Locks)
当我们使用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁。对于键值在条件范围内但并不存在的记录,叫做“间隙”。
使用间隙锁锁住的是一个区间,而不仅仅是这个区间中的每一条数据。
mysql复制代码SELECT * FROM table WHERE id BETWEN 1 AND 10 FOR UPDATE;
即所有在(1,10)区间内的记录行都会被锁住,所有id 为 2、3、4、5、6、7、8、9 的数据行的插入会被阻塞,但是 1 和 10 两条记录行并不会被锁住。
Hbase
zhuanlan.zhihu.com/p/664225235
Region Server
Region Server 直接对接用户的读写请求,是真正的干活的节点,主要工作职责如下。
- 管理 HMaster 为其分配的 Region;
- 负责与底层的 HDFS 交互,存储数据到 HDFS;
- 负责 Region 变大以后的拆分以及 StoreFile 的合并工作。
与 HMaster 的协同:当某个 RegionServer 宕机之后,ZK 会通知 Master 进行失效备援。下线的 RegionServer 所负责的 Region 暂时停止对外提供服务,Master 会将该 RegionServer 所负责的 Region 转移到其他 RegionServer 上,并且会对所下线的 RegionServer 上存在 MemStore 中还未持久化到磁盘中的数据由 WAL 重播进行恢复。
下面给大家详细介绍下 Region Serve数据存储的基本结构,如下图所示。一个 Region Server 是包含多个 Region 的,这里仅展示一个。
-
Region:每一个 Region 都有起始 RowKey 和结束 RowKey,代表了存储的Row的范围,保存着表中某段连续的数据。一开始每个表都只有一个 Region,随着数据量不断增加,当 Region 大小达到一个阀值时,Region 就会被 Regio Server 水平切分成两个新的 Region。当 Region 很多时,HMaster 会将 Region 保存到其他 Region Server 上。
-
Store:一个 Region 由多个 Store 组成,每个 Store 都对应一个 Column Family, Store 包含 MemStore 和 StoreFile。
-
MemStore:作为HBase的内存数据存储,数据的写操作会先写到 MemStore 中,当MemStore 中的数据增长到一个阈值(默认64M)后,Region Server 会启动 flasheatch 进程将 MemStore 中的数据写人 StoreFile 持久化存储,每次写入后都形成一个单独的 StoreFile。当客户端检索数据时,先在 MemStore中查找,如果MemStore 中不存在,则会在 StoreFile 中继续查找。
-
StoreFile:MemStore 内存中的数据写到文件后就是StoreFile,StoreFile底层是以 HFile 的格式保存。HBase以Store的大小来判断是否需要切分Region。
当一个Region 中所有 StoreFile 的大小和数量都增长到超过一个阈值时,HMaster 会把当前Region分割为两个,并分配到其他 Region Server 上,实现负载均衡。
-
HFile:HFile 和 StoreFile 是同一个文件,只不过站在 HDFS 的角度称这个文件为HFile,站在HBase的角度就称这个文件为StoreFile。
-
HLog:负责记录着数据的操作日志,当HBase出现故障时可以进行日志重放、故障恢复。例如,磁盘掉电导致 MemStore中的数据没有持久化存储到 StoreFile,这时就可以通过HLog日志重放来恢复数据。
当执行删除操作时,HBase 新插入一条相同的 Key-Value 数据,但是 keyType=Delete,这便意味着数据被删除了,直到发生 Major_compaction 操作,数据才会真正地被从磁盘上删除。
HBase这种基于标记删除的方式是按顺序写磁盘的的,因此很容易实现海量数据的快速删除,有效避免了在海量数据中查找数据、执行删除及重建索引等复杂的流程。
ES
index:库
type:表
document: 行、记录
倒排索引,可以通过http接口访问es
sniper是将表通过esSpark写入es的,每天切换index索引
交并差的dsl语言会转成es的sql,提交给es执行。es写非常慢
搜索引擎,特点是倒排索引和分词器,以及基于segment机制构建的准实时、高性能的搜索查询。
全文检索和复杂查询
ClickHouse
1、分析型OLAP数据录,列存储,方便压缩
2、它具有 OLAP、在线实时查询、完整的 DBMS 功能支持、列式存储、不需要任何数据预处理、支持批量更 新、拥有非常完善的 SQL 支持和函数、支持高可用、不依赖 Hadoop 复杂生态、开箱即用等许多特点。
3、缺点
- 没有完整的事务支持;
- 稀疏索引导致 ClickHouse 不擅长细粒度或者 key-value 类型数据的查询需求;
- 缺少高频率,低延迟的修改或删除已存在数据的能力。仅能用于批量删除或修改数据 ;
- 不擅长 join 操作;
分析引擎一般都有的特点:
- 数据排序
- 数据分片+分布式查询
- 列存储
- 主键索引 + 二级索引 + 位图索引 + 布隆索引 等等各种索引技术
- 定制引擎
CK的引擎
ck的引擎其实可以分为 数据库引擎 和 表引擎 两种
库引擎:目前支持 5 种,分别是:Ordinary,Dictionary, Memory, Lazy, MySQL,其实 Ordinary 是默认库引擎,在此类型库引擎下,可以使用任意类型的表引擎。
其中:Memory引擎: 所有数据只会保存在内存中,服务重启数据消失,该数据库引擎只能够创建 Memory 引擎表 ;
MySQL引擎: 改引擎会自动拉取远端 MySQL 中的数据,并在该库下创建 MySQL 表引擎的数据表;
表引擎:
**ck表引擎提供了四个系列(Log、MergeTree、Integration、Special)
**
**MergeTree 系列表引擎又和两种特殊表引擎(Replicated,Distributed)正交形成多种具备不同功能的 MergeTree 表引擎。
**
MergeTree 作为家族中最基础的表引擎,提供了主键索引、数据分区、数据副本和数据采样等基本能力,而家族中其他的表引擎则在 MergeTree 的基础 之上各有所长:
**关于 MergeTree 表引擎类型:
**
-
支持数据分区、存储有序、主键索引、稀疏索引、数据 TTL 等,MergeTree 中主键并不用于去重
-
ReplacingMergeTree 引擎,用来做去重。ReplacingMergeTree 确保数据最终被去重,但是无法保证查询过程中主键不重复。因为相同主键的数据可能被 shard 到不同的节点,但是 compaction 只能在一个节点中进行。
-
CollapsingMergeTree 引擎要求在建表语句中指定一个标记列 Sign(插入的时候指定为 1,删除的时候指定为 -1),后台 Compaction 时会将主键相同、Sign 相反的行进行折叠,也即删除。来消除 ReplacingMergeTree 的限制。
-
解决聚合场景,ClickHouse 通过 SummingMergeTree 来支持对主键列进行预先聚合。在后台 Compaction 时,会将主键相同的多行进行 sum 求 和,然后使用一行数据取而代之,从而大幅度降低存储空间占用,提升聚合计算性能。同理还有 AggregatingMergeTree用来预聚合平均值。
CREATE TABLE [IF NOT EXISTS] [db_name.]table_name ( name1 [type] [DEFAULT|MATERIALIZED|ALIAS expr], name2 [type] [DEFAUErEMAMLERLALLIZED|ALIAS expr], 省略... ) ENGINE = MergeTree() [PARTITION BY expr] [ORDER BY expr] [PRIMARY KEY expr] [SAMPLE BY expr] [SETTINGS name=value, 省略...]
-
PARTITION BY: 分区键。指定表数据以何种标准进行分区。分区键既可以是单个列字段,也可以通过元组的形式使用多个列字段,同时它也支持使用列表达 式。选填。如果没有执行分区字段,则所有数据,都在一个分区里面: all;
-
ORDER BY: 排序键,用于指定在一个数据片段内,数据以何种标准排序。默认情况下主键(PRIMARY KEY)与排序键相同。必填。
-
PRIMARY KEY: 主键。声明后会依照主键字段生成一级索引。默认情况下,主键与排序键(ORDER BY)相同,所以通常直接使用 ORDER BY 代为指定主键。
-
SETTINGS: index_granularity 选项表示索引的粒度,默认值为 8192。MergeTree 索引在默认情况下,每间隔 8192 行数据才生成一条索引。选填。
-
SAMPLE BY:抽样表达式,用于声明数据以何种标准进行采样。选填。
MergeTree的主键索引是稀疏索引
CK工作原理
1、数据分区
- 分区目录:20190710_20190711_1_5_1,一个分区可能会有多个不同的目录,该目录下存储该分区的数据及其他各种形式的数据。后台会执行合并,把相同分 区的多个目录合并到一个分区。
- checksums.txt:校验文件。使用二进制格式存储。它保存了余下各类文件(primary.idx、count.txt等)的 size 大小及 size 的哈希值,用于快速校 验文件的完整性和正确性。
- columns.txt:列信息文件,使用明文格式存储。用于保存此数据分区下的列字段信息 。
- count.txt:计数文件,使用明文格式存储。用于记录当前数据分区目录下数据的总行数。
- primary.idx:一级索引文件,主键索引文件。
- xxx.bin:数据文件,使用压缩格式存储,默认为 LZ4 压缩格式,用于存储某一列的数据,每一列都对应一个该文件,如列 date 为 date.bin。
- xxx.mrk:列字段标记文件。
- xxx.mrk2:列字段标记文件,如果使用了自适应大小的索引间隔,则标记文件以 .mrk2 命名,否则以 .mrk 命名。它建立 primary.idx 稀疏索引与 xxx.bin 数据文件之间的映射关系,先通过主键索引找到数据的偏移量,然后去 xxx.bin 数据文件中找到真实数据
- 还有二级索引 和 分区键相关信息文件,跳数索引文件等等
1、T1写入一批数据,创建2分区
2、T2新写入一批数据,创建3分区
3、T3后台触发合并,相同分区进行合并,旧目录设置为非激活
4、T4删除非激活分区
**2、**一级索引
一级索引常驻内存。总的来说: 一级索引和标记文件一一对齐,两个 索引标记之间的数据,就是一个数据区间,在数据文件中,这个数据区间的所有数据,生成一个压缩数据块。
3、二****级索引
跳数索引
4、数据标记
数据块偏移量和解压后的偏移量
HIVE
问题 1:什么是Hive?
答案:Hive是一个基于Hadoop的数据仓库工具,它提供了类似SQL的查询语言(HiveQL)来进行大规模数据的分析和处理。Hive将结构化数据映射到Hadoop的分布式文件系统(HDFS)上,并通过MapReduce任务执行查询操作。
问题 2:Hive的主要特性是什么?
答案:Hive的主要特性包括: 类SQL查询语言:Hive提供了类似SQL的查询语言(HiveQL)来方便用户进行数据查询和分析。 数据映射:Hive将结构化数据映射到Hadoop的分布式文件系统(HDFS)上,以便进行分布式数据处理。 扩展性:Hive能够处理大规模的数据集,并在Hadoop集群上进行并行处理。 用户定义函数(UDF):Hive允许用户编写自定义函数来扩展查询和数据处理的能力。 元数据存储:Hive使用元数据存储来管理表、分区和表结构等信息。
问题 3:Hive的工作原理是什么?
答案:Hive的工作原理可以概括为以下几个步骤: 用户使用HiveQL编写查询语句,类似于SQL。 查询语句被Hive编译成MapReduce任务(或其他支持的计算引擎,如Apache Tez或Apache Spark)。 查询任务通过Hive驱动程序提交给计算引擎进行执行。 计算引擎将查询任务转换为MapReduce作业(或其他适当的计算模型)进行分布式计算。 结果从计算引擎返回给Hive,然后返回给用户。
问题 4:Hive的数据存储是如何组织的? 答案:Hive使用Hadoop分布式文件系统(HDFS)来存储数据。数据以表的形式组织,每个表包含一系列行和列。在HDFS上,表被划分为多个分区,每个分区是HDFS上的一个目录,包含特定的数据。分区可以根据数据的某个属性进行划分,例如时间、地区等。
问题 5:Hive的查询语言是什么?
答案:Hive使用一种类似SQL的查询语言称为HiveQL(Hive Query Language)。HiveQL允许用户以SQL风格的语法编写查询语句,包括SELECT、INSERT、JOIN、GROUP BY等常见的SQL操作。HiveQL提供了对Hive的元数据和数据的访问、数据转换和聚合等功能。
问题 6:Hive和传统关系型数据库之间有什么区别?
答案:Hive和传统关系型数据库之间有几个主要区别: 存储方式:Hive将数据存储在Hadoop分布式文件系统(HDFS)上,而传统关系型数据库使用表格和行存储。 数据处理模型:Hive使用MapReduce任务进行数据处理,而传统关系型数据库使用基于索引的查询优化器。 查询语言:Hive使用类SQL的查询语言(HiveQL),而传统关系型数据库使用标准的SQL查询语言。 数据模型:Hive支持半结构化数据和非规范化数据,而传统关系型数据库更适合处理结构化数据。
问题 7:Hive支持哪些数据格式?
答案:Hive支持多种数据格式,包括文本文件(如CSV、TSV)、Apache Parquet、Apache ORC(Optimized Row Columnar)等。这些数据格式可以在Hive中定义表时进行指定,以便Hive可以正确解析和处理数据。
问题 8:Hive中的分区是什么?
答案:在Hive中,分区是将表中的数据根据某个列的值进行逻辑划分的一种方式。分区可以基于日期、地区、部门等属性进行划分。分区的使用可以提高查询效率,因为它可以帮助Hive仅处理满足特定条件的分区数据,而不是整个表的数据。
问题 9:Hive的性能调优技巧有哪些?
答案:Hive的性能调优技巧包括以下几个方面: 数据存储格式选择:选择合适的数据存储格式,如Apache Parquet或Apache ORC,以提高查询性能和压缩比。 数据分区和分桶:合理使用数据分区和分桶技术,以便减少查询数据的范围和提高查询效率。 数据压缩:使用数据压缩技术可以减少存储空间和数据传输的开销,同时提高查询性能。 合理设置并行度:根据集群的规模和资源配置,调整Hive的并行度参数,以充分利用集群资源。 使用合适的数据类型和索引:选择适当的数据类型和创建索引,以提高查询的效率。
问题 10:Hive与Spark之间有什么关系? 答案:Hive和Spark是两个独立的项目,但它们可以相互配合使用。Hive可以利用Spark作为计算引擎来执行查询任务,从而提高查询的性能。通过使用Spark作为Hive的计算引擎,可以充分利用Spark的内存计算和优化执行引擎,加快查询速度,并支持更复杂的查询操作。
Spark
标签圈选的实现(dSL和交并差)
标签圈选
标签数据会每日回流到Clichouse,包括简单的profile标签数据,和行为偏好数据。
SELECT bitmapAndnot(l.result, r.result) AS result, idFROM ( SELECT groupBitmapOrState(result) AS result, id FROM ( SELECT groupBitmapState(toUInt64(user_id)) AS result, _shard_num AS id FROM music_icircle_v2.online_ads_erised_artist_fans_dd WHERE dateDiff( 'day', toDate(last_action_time), toDate('${etl_date}') ) < 180 and action_type IN ('one_way_concern') and resource_id IN (12641765) and intimacy_level IN ('低') and dt = '${etl_date}' settings distributed_group_by_no_merge = 0 UNION ALL
SELECT groupBitmapState(toUInt64(user_id)) AS result, _shard_num AS id FROM music_icircle_v2.online_ads_erised_album_user_action_dd WHERE dateDiff( 'day', toDate(last_action_time), toDate('${etl_date}') ) < 60 and action_type IN ('collect') and resource_id IN (147994018) and dt = '${etl_date}' settings distributed_group_by_no_merge = 0 ) GROUP BY id settings distributed_group_by_no_merge = 0 ) AS l INNER JOIN \ n( SELECT groupBitmapState(toUInt64(user_id)) AS result, _shard_num AS id FROM music_icircle_v2.online_ads_erised_song_user_action_with_prefer_dd WHERE dateDiff( 'day', toDate(last_action_time), toDate('${etl_date}') ) < 5 and action_type IN ( 'effective_play', 'collect', 'like', 'share', 'comment', 'amount' ) and resource_id IN (1963344815) and prefer_level IN ('低') and dt = '${etl_date}' settings distributed_group_by_no_merge = 0 ) AS r ON l.id = r.id settings distributed_group_by_no_merge = 0
在ClickHouse (CK) 中,“distributed_group_by_no_merge” 是一个设置选项,用于控制在分布式查询中进行聚合操作时的行为。当设置为 1 时,它告诉 ClickHouse 在分布式查询中对于 GROUP BY 操作不要进行合并,而是在每个分布式节点上分别进行局部的聚合操作,最终将结果汇总。这可以提高性能,尤其是在数据分布不均匀的情况下。
位图函数的用法:blog.csdn.net/Bertil/arti…
select bitmapCardinality(bitmapBuild([1, 2, 3, 4, 5])) as result
基数是指位图中非零位的个数,这条语句中会返回5
bitmapAndnot
groupBitmapOrState
groupBitmapState
roaringbitmap的用法
brands.cnblogs.com/tencentclou…
人群服务
规则模型,支持多实体
- 离线人群服务化
- 增量人群上线
- 规则模型
- 规则人群
- 元数据管理
- 稳定性建设
com.google.common.util.concurrent.RateLimiter是Google Guava库中的一个类,用于实现限流功能。它可以用来限制某个操作在一定时间单位内的执行次数,比如限制每秒执行的次数。这个类可以帮助控制对一些资源的访问速率,以防止资源被过度使用。
限流:
令牌桶算法是网络流量整形(Traffic Shaping)和速率限制(Rate Limiting)中最常使用的一种算法。先有一个木桶,系统按照固定速度,往桶里加入Token,如果桶已经满了就不再添加。当有请求到来时,会各自拿走一个Token,取到Token 才能继续进行请求处理,没有Token 就拒绝服务。
令牌桶允许一定程度的突发,而漏桶主要目的是平滑流入速率
计数器限流算法也是比较常用的,主要用来限制总并发数,比如数据库连接池大小、线程池大小、程序访问并发数等都是使用计数器算法。也是最简单粗暴的算法。
降级:
close-》open-〉half open
方案:Sentinel 是阿里中间件团队开源的,面向分布式服务架构的轻量级高可用流量控制组件,主要以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度来帮助用户保护服务的稳定性。
Redis&tair-rdb
string、hash、list、set、zset、bitmap、hyperloglog
String:value最大512M,String 类型的底层的数据结构实现主要是 int 和 SDS(简单动态字符串),SDS 不光能存放文本数据,而且能保存图片、音频、视频、压缩文件这样的二进制数据。
SDS API 是安全的,拼接字符串不会造成缓冲区溢出。因为 SDS 在拼接字符串之前会检查 SDS 空间是否满足要求,如果空间不够会自动扩容,所以不会导致缓冲区溢出的问题。
三种编码,int、embstr(长度小于32字节)、raw(长度大于32字节)
embstr编码的字符串是只读的,修改会转化成raw
SET、MSET等命令
因为 Redis 处理命令是单线程,所以执行命令的过程是原子的。因此 String 数据类型适合计数场景,比如计算访问次数、点赞、转发、库存数量等等
分布式锁
setNx
解锁因为有两步,先比较后删除,所以需要用lua脚本保证原子性
list:使用quicklist实现,先前有压缩列表和双向列表实现
lpush、rpop 、brpop
可用于作为消息队列
1、顺序性 lpush、rpop实现
2、重复消费,生产者自行实现全局唯一 ID;
3、可靠性,备份的list , 为了留存消息,List 类型提供了 BRPOPLPUSH 命令,这个命令的作用是让消费者程序从一个 List 中读取消息,同时,Redis 会把这个消息再插入到另一个 List(可以叫作备份 List)留存。
Hash:通过listpack 数据结构实现,先前是压缩列表和hash表
一般对象用 String + Json 存储,对象中某些频繁变化的属性可以考虑抽出来用 Hash 类型存储。
购物车场景
-
添加商品:
HSET cart:{用户id} {商品id} 1 -
添加数量:
HINCRBY cart:{用户id} {商品id} 1 -
商品总数:
HLEN cart:{用户id} -
删除商品:
HDEL cart:{用户id} {商品id} -
获取购物车所有商品:
HGETALL cart:{用户id}
SET:Set 类型的底层数据结构是由哈希表或整数集合实现的:
多个集合可以做交并差运算
因此 Set 类型比较适合用来数据去重和保障数据的唯一性,还可以用来统计多个集合的交集、错集和并集等,当我们存储的数据是无序并且需要去重的情况下,比较适合使用集合类型进行存储。
场景:
点赞。因为要去重
共同关注。集合的交集
ZSET 有序集合类型,相比于 Set 类型多了一个排序属性 score(分值),对于有序集合 ZSet 来说,每个存储元素相当于有两个值组成的,一个是有序结合的元素值,一个是排序值。
由**压缩列表或跳表实现,redis7后由,**压缩列表数据结构已经废弃了,交由 listpack 数据结构来实现了。
应用场景:排行榜
有序集合比较典型的使用场景就是排行榜。例如学生成绩的排名榜、游戏积分排行榜、视频播放排名、电商系统中商品的销量排名等。
bitmap
Bitmap 本身是用 String 类型作为底层数据结构实现的一种统计二值状态的数据类型。
String 类型是会保存为二进制的字节数组,所以,Redis 就把字节数组的每个 bit 位利用起来,用来表示一个元素的二值状态,你可以把 Bitmap 看作是一个 bit 数组。
应用场景:统计签到
Bitmap 类型非常适合二值状态统计的场景,这里的二值状态就是指集合元素的取值就只有 0 和 1 两种,在记录海量数据时,Bitmap 能够有效地节省内存空间。
一天签到的用户、连续7天签到的数量,等等
SETBIT uid:sign:100:202206 2 1
判断用户登陆状态
HyperLogLog
简单来说 HyperLogLog 提供不精确的去重计数。Redis HyperLogLog 优势在于只需要花费 12 KB 内存,就可以计算接近 2^64 个元素的基数,和元素越多就越耗费内存的 Set 和 Hash 类型相比,HyperLogLog 就非常节省空间。
场景:百万级网页 UV 计数
PFADD page1:uv user1 user2 user3 user4 user5
PFCOUNT page1:uv
持久化
RDB?
AOF?
缓存穿透、缓存雪崩?
redis的集群化部署?
redis的哈希槽?
缓存和数据库的一致性,怎么保证?
Cache-Aside 中是先更新数据库,再删除缓存。
-
读的时候,先读缓存,缓存没有的话,就读数据库,然后取出数据后放入缓存,同时返回响应
-
更新的时候,先更新数据库,然后再删除缓存
RPC&dubbo&故障演练
dubbo的优点
-
透明化的远程方法调用:就像调用本地方法一样调用远程方法,只需简单配置,没有任何API侵入。
-
软负载均衡及容错机制:可在内网替代 F5 等硬件负载均衡器,降低成本,减少单点。
-
服务自动注册与发现:不再需要写死服务提供方地址,注册中心基于接口名查询服务提供者的IP地址,并且能够平滑添加或删除服务提供者。
核心功能
Remoting:网络通信框架,提供对多种NIO框架抽象封装,包括“同步转异步”和“请求-响应”模式的信息交换方式。
Cluster:服务框架,提供基于接口方法的透明远程过程调用,包括多协议支持,以及软负载均衡,失败容错,地址路由,动态配置等集群支持。
Registry:服务注册,基于注册中心目录服务,使服务消费方能动态的查找服务提供方,使地址透明,使服务提供方可以平滑增加或减少机器。
服务提供者、消费者、注册中心、container、monitor
dubbo的分层
细的话有10个分层,从上到下分别是:
- 接口服务层(Service):该层与业务逻辑相关,根据 provider 和 consumer 的业务设计对应的接口和实现
- 配置层(Config):对外配置接口,以 ServiceConfig 和 ReferenceConfig 为中心
- 服务代理层(Proxy):服务接口透明代理,生成服务的客户端 Stub 和 服务端的 Skeleton,以 ServiceProxy 为中心,扩展接口为 ProxyFactory
- 服务注册层(Registry):封装服务地址的注册和发现,以服务 URL 为中心,扩展接口为 RegistryFactory、Registry、RegistryService
- 路由层(Cluster):封装多个提供者的路由和负载均衡,并桥接注册中心,以Invoker 为中心,扩展接口为 Cluster、Directory、Router 和 LoadBlancce
- 监控层(Monitor):RPC 调用次数和调用时间监控,以 Statistics 为中心,扩展接口为 MonitorFactory、Monitor 和 MonitorService
- 远程调用层(Protocal):封装 RPC 调用,以 Invocation 和 Result 为中心,扩展接口为 Protocal、Invoker 和 Exporter
- 信息交换层(Exchange):封装请求响应模式,同步转异步。以 Request 和Response 为中心,扩展接口为 Exchanger、ExchangeChannel、ExchangeClient 和 ExchangeServer
- 网络 传输 层(Transport):抽象 mina 和 netty 为统一接口,以 Message 为中心,扩展接口为 Channel、Transporter、Client、Server 和 Codec
- 数据序列化层(Serialize):可复用的一些工具,扩展接口为 Serialization、ObjectInput、ObjectOutput 和 ThreadPool
Dubbo集群提供了哪些负载均衡策略?
-
Random LoadBalance: 随机选取提供者策略,有利于动态调整提供者权重。截面碰撞率高,调用次数越多,分布越均匀。
-
RoundRobin LoadBalance: 轮循选取提供者策略,平均分布,但是存在请求累积的问题。
-
LeastActive LoadBalance: 最少活跃调用策略,解决慢提供者接收更少的请求。
-
ConstantHash LoadBalance: 一致性 Hash 策略,使相同参数请求总是发到同一提供者,一台机器宕机,可以基于虚拟节点,分摊至其他提供者,避免引起提供者的剧烈变动。
Dubbo SPI 和 Java SPI 区别?
-
JDK SPI:
JDK 标准的 SPI 会一次性加载所有的扩展实现,如果有的扩展很耗时,但也没用上,很浪费资源。所以只希望加载某个的实现,就不现实了
-
DUBBO SPI:
1、对 Dubbo 进行扩展,不需要改动 Dubbo 的源码
2、延迟加载,可以一次只加载自己想要加载的扩展实现。
3、增加了对扩展点 IOC 和 AOP 的支持,一个扩展点可以直接 setter 注入其它扩展点。
4、Dubbo 的扩展机制能很好的支持第三方 IoC 容器,默认支持 Spring Bean。
Dubbo 支持分布式事务吗?
-
目前暂时不支持,可与通过 tcc-transaction 框架实现
-
介绍:tcc-transaction 是开源的 TCC 补偿性分布式事务框架
-
TCC-Transaction 通过 Dubbo 隐式传参的功能,避免自己对业务代码的入侵。
Http和Rpc区别
不必每次通信都要像http一样去3次握手什么的,减少了网络开销;其次就是RPC框架一般都有注册中心,有丰富的监控管理;发布、下线接口、动态扩展等,对调用方来说是无感知、统一化的操作。第三个来说就是安全性。最后就是最近流行的服务化架构、服务化治理,RPC框架是一个强力的支撑。
**Dubbo的扩展点,**通过spi实现扩展
Dubbo 扩展点,简单来说,就是 Dubbo 框架中的一个组件,可以被扩展或者替换。Dubbo 提供了一套扩展点加载机制,让我们可以通过配置文件或注解的方式,来指定某个接口使用具体的实现类。
Dubbo 中,SPI 主要有两种用法,一种是加载固定的扩展类,另一种是加载自适应扩展类。这两种方式会在下面详细的介绍。 需要特别注意的是: 在 Dubbo 中,基于 SPI 扩展加载的类是单例的。
协议扩展、负载均衡扩展、注册中心扩展、序列化扩展,非常多
**官方:cn.dubbo.apache.org/zh-cn/overv…
**
故障演练
故障前:这个阶段主要是预防,例如在问题上升为故障前,通过自动化测试、流程规范管控、监控报警、等手段快速发现。
故障中:这个阶段主要是发现、恢复和定位故障。需要快速发现系统出现的故障,及时采取措施进行恢复,并准确定位故障原因,最大化降低对用户的影响。
故障后:这个阶段主要对故障发生的原因进行分析和总结,找出不足之处并采取措施进行改进,思考通过标准化、流程化、自动化的手段,避免同样的故障二次出现。
云音乐的哨兵collector机制。
设计模式&DDD
防腐层
工厂模式
适配器模式
策略模式
消息队列
rebalance负载均衡,在rocketmq中,一个topic下的一个queue只会被一个consumer消费,如果集群中consumer的数量发生变成,会触发rebalance。
rocketmq支持队列粒度的负载均衡,高版本的支持消息粒度的负载均衡,
过程:消费者在启动的时候,会立刻触发一次负载均衡,为消费者分配消息队列。为了保证消费者拿到的主题路由信息是最新的(topic下有几个消息队列、消息队列的分布信息等),消费者会向NameServer发送请求,更新每一个主题的路由信息,保证路由信息是最新的。
分配策略:平均策略、轮训策略、一致性hash分配、固定数量、优先分配同机房的消费者。
消费者启动时触发、Broker发现消费组变更时触发、Broker发现消费组变更时触发
NamerServer:主要负责对于源数据的管理,包括了对于Topic和路由信息的管理。
Broker:负责存储和转发消息
Producer:负责生产消息,同步,异步,单向
Consumer:消息消费,push或者pull
消息去重;使用业务端逻辑保持幂等性
消息重复:没有内置消息去重的解决方案
消息刷盘,同步刷,异步刷,commitLog
消息顺序:如果想全局消息顺序,就要保证只有一个queue
一个topic下有多个队列,为了保证发送有序,RocketMQ提供了MessageQueueSelector队列选择机制,他有三种实现:
可以通过hash将一个订单下的创建、支付、发货等消息发到一个queue以保证顺序
分布式事务:
半消息,投递后暂时无法消费,必须二次确认是commit还是rollback
Zk
分布式协调服务,为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。
ZooKeeper 的目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户。
Watch机制
标签服务的元数据监听。
zk基于临时顺序节点和wathc机制
分布式锁
- 依赖于临时顺序节点
- 判断当前client的顺序号是否是最小的,如果是获取到锁。
- 没有获取到锁的节点监听最小节点的删除事件(比如lock_key_001)
- 锁释放,最小节点删除,剩余节点重新开始获取锁。
- 重复步骤二到四。
QlExpress
比较耗时的脚本编译过程可以缓存在本地机器
可以自定义operator
轻量级的规则引擎。
Prompt调优
业界产品:dify,langchain,扣子,千帆大模型
框架:
6大方法,如何产品化,step by step
zhuanlan.zhihu.com/p/673101835
RAG
检索增强生成。y
ReAct
function call