Skip to content

博客

简单对比GraphRAG和LlamaIndex

比较LlamaIndex和GraphRAG的成熟度,需要考虑它们各自的目标、功能以及实际应用情况。以下是一些关键点的比较:

  • 成熟度:
    • LlamaIndex是一个为了简化LLM(大型语言模型)应用中的数据增强检索(RAG)流程的工具。
    • 它专注于提供各种数据连接器、索引和查询工具,以便LLM能够更好地利用外部数据。
    • LlamaIndex在RAG领域中相对成熟,拥有活跃的社区和广泛的文档。
    • 它提供了多种索引类型,支持多种数据源,并且易于使用。
  • 功能:
    • 数据连接器:支持多种数据源,如PDF、网站、数据库等。
    • 数据索引:提供向量索引、树索引、关键词索引等多种索引方式。
    • 查询引擎:提供各种查询方式,支持高级检索。
    • 生态丰富:拥有大量的社区贡献的工具和插件。
  • 应用:
    • 广泛应用于构建问答系统、聊天机器人、知识库等LLM应用。
    • 适用于需要利用外部数据增强LLM能力的场景。
  • 成熟度:
    • GraphRAG是一种新兴的技术,它结合了知识图谱和RAG,旨在提高LLM在处理复杂信息时的准确性和相关性。
    • 虽然GraphRAG的原理已经相对成熟,但其在实际应用中的稳定性和成熟度还处于发展中。
    • GraphRAG的应用案例相对较少,主要集中在一些研究项目和探索性应用中。
  • 功能:
    • 知识图谱构建:将非结构化文本转化为结构化的知识图谱。
    • 图引导检索:利用知识图谱的结构信息,提高检索的准确性和效率。
    • 增强生成:利用知识图谱的结构信息,验证和修正LLM生成的文本。
  • 应用:
    • 适用于需要处理复杂关系和实体信息的场景,如金融、医疗、法律等。
    • 在需要推理和多跳问答的场景中具有潜力。
  • LlamaIndex在RAG领域中相对成熟,拥有更广泛的应用和更丰富的生态。
  • GraphRAG是一种新兴的技术,具有很大的发展潜力,但其成熟度和稳定性还处于发展中。
  • 从一些测试结果上看,GraphRAG在对全局的理解上,确实强大,但是从时间,成本上看,消耗也很大,而LlamaIndex在时间和成本上,占有优势。
  • GraphRAG的回答质量不是在所有场景适用,而是依赖于高质量的知识图谱。

如果需要一个成熟、稳定且易于使用的RAG工具,LlamaIndex是一个不错的选择。 如果您需要处理复杂的知识密集型任务,并且愿意投入更多的时间和资源,GraphRAG可能是一个值得探索的方向。

Embedding

简单来说,Embedding 模型是一种将高维度、离散或复杂的输入数据(例如文字、图片、用户ID、商品ID等)转换为低维度、连续的向量(Vector)表示的技术或模型。这个生成的向量被称为“嵌入”(Embedding)。

想象一下,我们有很多词语,比如“国王”、“女王”、“男人”、“女人”。直接用这些词语本身,计算机很难理解它们之间的关系。Embedding 模型就能学习到将这些词语映射到一个多维空间中的点(向量)。在这个空间里,“国王”和“男人”的距离会比较近,“女王”和“女人”的距离会比较近,而“国王”和“女王”之间可能存在一种类似于“男人”到“女人”的关系向量。

  1. 降维: 将原本可能维度非常高(比如用 one-hot 编码表示词语,维度可能高达几万甚至几十万)或者非结构化的数据,映射到一个维度相对较低(通常是几十到几百维)的连续向量空间。

  2. 保留语义/关系: 这个映射过程不是随机的,而是通过学习大量数据得到的。目标是让转换后的向量能够捕捉到原始数据中的内在含义、相似性或关系。在向量空间中,语义相近或关系类似的对象,它们的向量也会比较接近或具有特定关联。

  3. 利于计算: 计算机更擅长处理数值型的向量。将各种类型的数据转换为统一的向量表示后,就可以方便地进行各种数学运算,如计算相似度(点积、余弦相似度)、距离(欧氏距离)等,进而应用于各种下游任务。

Embedding 模型的能力非常广泛,是许多现代机器学习和人工智能应用的基础模块。主要作用包括:

  1. 语义理解与表示 (Semantic Understanding & Representation):

    • 自然语言处理 (NLP): 这是 Embedding 最经典的应用领域。

      • 词嵌入 (Word Embeddings):Word2Vec, GloVe, FastText,将单词映射为向量,捕捉词语的语义和语法关系。

      • 句子/文档嵌入 (Sentence/Document Embeddings):Sentence-BERT, Universal Sentence Encoder,将整个句子或文档表示为向量,用于文本分类、情感分析、问答系统、文本相似度计算等。

    • 知识图谱 (Knowledge Graphs): 将实体(如人物、地点)和关系(如“出生在”、“工作于”)嵌入到向量空间,用于知识推理和链接预测。

  2. 相似性计算与搜索 (Similarity Calculation & Search):

    • 信息检索/语义搜索: 通过比较查询(Query)的 Embedding 和数据库中文档/物品的 Embedding,找到语义最相关的结果,而不是仅仅基于关键词匹配。例如,搜索“夏天穿的透气鞋子”,能找到包含“凉鞋”、“网面运动鞋”等词语的商品,即使查询中没有这些具体词。

    • 图像/音频检索: 将图像或音频转换为 Embedding,实现以图搜图、以歌搜歌等功能。

  3. 推荐系统 (Recommendation Systems):

    • 协同过滤: 将用户(User)和物品(Item)都嵌入到同一个向量空间。可以通过计算用户 Embedding 和物品 Embedding 的相似度来预测用户可能喜欢的物品,或者找到具有相似兴趣的用户(计算用户 Embedding 之间的相似度)。

    • 内容推荐: 基于物品内容的 Embedding(如文章内容、商品描述)来推荐相似的物品。

  4. 分类与聚类 (Classification & Clustering):

    • 将原始数据转换为 Embedding 后,这些向量可以作为特征输入到传统的分类器(如 SVM、逻辑回归)或聚类算法(如 K-Means)中,通常能提高模型的性能,因为 Embedding 包含了更丰富的语义信息。
  5. 异常检测 (Anomaly Detection):

    • 正常的数据点在 Embedding 空间中可能会聚集在一起,而异常点则可能远离这些聚集区,从而可以被识别出来。
  6. 数据可视化 (Data Visualization):

    • 虽然 Embedding 本身是高维的,但可以使用降维技术(如 t-SNE, PCA)将其投影到二维或三维空间进行可视化,帮助我们直观地理解数据点之间的关系和结构。

总结来说,Embedding 模型的核心价值在于:

Section titled “总结来说,Embedding 模型的核心价值在于:”
  • 将复杂数据转化为计算机易于处理的数值向量。

  • 在转化过程中捕捉并保留数据的内在语义和关系。

  • 作为许多高级 AI 应用(如搜索、推荐、NLP)的基础,提升其效果和智能程度。

它就像是为不同类型的数据(文本、图像、用户行为等)构建了一个通用的“语义坐标系”,使得我们可以在这个统一的空间中进行有意义的比较、查找和分析。

React Native

使用 Gemini 学习新知识效率真的很高

Watchman crawl failed. Retrying once with node crawler

Section titled “Watchman crawl failed. Retrying once with node crawler”

Watchman crawl failed. Retrying once with node crawler. Usually this happens when watchman isn’t running. Create an empty .watchmanconfig file in your project’s root folder or initialize a git or hg repository in your project. Error: watchman —no-pretty get-sockname returned with exit code=1, signal=null,

解决办法:

Terminal window
# Step 1:
watchman watch-del-all
#Step 2:
watchman shutdown-server

1. 修改 gradle.properties 文件(全局配置)

Section titled “1. 修改 gradle.properties 文件(全局配置)”

修改 ~/.gradle/gradle.properties 文件(macOS/Linux)或 C:\Users<YourUserName>.gradle\gradle.properties 文件(Windows) 可以进行全局配置,影响所有使用 Gradle 的项目。

打开或创建上述 gradle.properties 文件。

添加以下配置:

distributionUrl=https\://mirrors.aliyun.com/gradle/gradle-x.x.x-all.zip

2. 使用 init.gradle 文件(更灵活的全局配置)

Section titled “2. 使用 init.gradle 文件(更灵活的全局配置)”

通过 init.gradle 文件可以进行更灵活的全局配置,例如根据不同的条件使用不同的镜像。

  • 在 ~/.gradle/ 目录下创建一个名为 init.gradle 的文件(如果不存在)。

  • 添加以下配置:

allprojects {
buildscript {
repositories {
maven { url 'https://maven.aliyun.com/repository/google/' }
maven { url 'https://maven.aliyun.com/repository/jcenter/' }
maven { url 'https://maven.aliyun.com/repository/central/' }
google()
mavenCentral()
jcenter()
}
}
repositories {
maven { url 'https://maven.aliyun.com/repository/google/' }
maven { url 'https://maven.aliyun.com/repository/jcenter/' }
maven { url 'https://maven.aliyun.com/repository/central/' }
google()
mavenCentral()
jcenter()
}
}

这段配置会将 Maven Central、Google 和 JCenter 仓库都指向阿里云的镜像。

  • 保存文件。

Android NDK (Native Development Kit) 是一套工具,它允许你在 Android 应用中使用 C 和 C++ 等原生代码。 通常情况下,Android 应用主要使用 Java 或 Kotlin 编写,运行在 Android Runtime (ART) 虚拟机上。但有些情况下,使用原生代码可以带来性能上的提升或其他方面的优势。

NDK 的主要用途:

  • 性能密集型任务: 对于需要高性能计算的任务,例如游戏、图形处理、音视频编解码、物理引擎等,使用 C/C++ 等原生代码通常比 Java/Kotlin 更有效率。
  • 代码复用: 如果你已经有使用 C/C++ 编写的现有库或代码,可以使用 NDK 将它们集成到 Android 应用中,避免重复开发。
  • 访问底层硬件: NDK 允许你直接访问一些底层硬件和系统 API,例如传感器、OpenGL ES 等,这在某些特定应用场景下非常有用。
  • 平台移植: 如果你的应用需要在多个平台(包括 Android)上运行,使用 C/C++ 编写核心代码可以更容易地进行移植。

NDK 的组成部分:

  • 编译器和工具链: NDK 包含用于将 C/C++ 代码编译成可在 Android 设备上运行的机器码的编译器(通常是 Clang)和其他构建工具(例如 Make、CMake)。
  • 头文件和库: NDK 提供了一系列头文件和库,允许你访问 Android 平台的各种功能,例如文件 I/O、网络、图形、音频等。
  • 调试器: NDK 提供了用于调试原生代码的工具,例如 GDB。
  • 调试器: NDK提供了用于调试正确代码的工具,例如GDB。
  • JNI (Java Native Interface): JNI 是 Java 和 C/C++ 代码之间进行通信的桥梁。通过 JNI,Java 代码可以调用 C/C++ 函数,C/C++ 代码也可以回调 Java 方法。

构建时报错:

  [1/3] Building CXX object CMakeFiles/appmodules.dir/Users/xiaoxiwang/Work/study-demo/react-native/MyProject/android/app/build/generated/autolinking/src/main/jni/autolinking.cpp.o
  FAILED: CMakeFiles/appmodules.dir/Users/xiaoxiwang/Work/study-demo/react-native/MyProject/android/app/build/generated/autolinking/src/main/jni/autolinking.cpp.o
  ccache /Users/xiaoxiwang/Library/Android/sdk/ndk/25.2.9519653/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang++ --target=aarch64-none-linux-android24 --sysroot=/Users/xiaoxiwang/Library/Android/sdk/ndk/25.2.9519653/toolchains/llvm/prebuilt/darwin-x86_64/sysroot -Dappmodules_EXPORTS -I/Users/xiaoxiwang/Work/study-demo/react-native/MyProject/node_modules/react-native/ReactAndroid/cmake-utils/default-app-setup -I/Users/xiaoxiwang/Work/study-demo/react-native/MyProject/android/app/build/generated/autolinking/src/main/jni -isystem

为什么需要禁用 ccache?

虽然 ccache 通常可以显著提高编译速度,但在某些情况下,它可能会导致问题:

  • 缓存污染: 如果构建环境或依赖项发生重大变化,ccache 的缓存可能变得无效,导致构建错误或不一致的结果。
  • 构建问题排查: 在排查构建问题时,禁用 ccache 可以帮助你确定问题是否与缓存有关。
  • 特定构建配置: 某些特定的构建配置可能与 ccache 不兼容。

通过设置 CCACHE_DISABLE 环境变量是在 React Native 项目中禁用 ccache 的最简单和推荐的方法。

  • 在终端中(临时禁用) : 在运行构建命令之前,在终端中执行以下命令:
Terminal window
export CCACHE_DISABLE=1
npx react-native run-android

或者

Terminal window
CCACHE_DISABLE=1 npx react-native run-android
  • 在 shell 配置文件中(永久禁用): 为了永久禁用 ccache,你可以将 export CCACHE_DISABLE=1 添加到你的 shell 配置文件中, 例如 /.bashrc、/.zshrc 或 ~/.profile。然后,执行 source ~/.bashrc(或相应的命令)来使更改生效。

expo run:androidexpo start --dev-client 命令的区别

Section titled “expo run:android 和 expo start --dev-client 命令的区别”

expo run:androidexpo start --dev-client 是在使用 Expo 开发 React Native 应用时常用的两个命令,它们的功能有所不同,理解它们的区别对于高效开发至关重要。

  • 作用: 启动 Metro Bundler (JavaScript 打包器) 和开发服务器。这个服务器负责提供你的 JavaScript 代码和静态资源给运行在设备或模拟器上的应用程序。
  • 连接方式: 应用程序通过网络连接到这个开发服务器。这意味着你的设备或模拟器需要能够访问运行开发服务器的计算机。
  • 实时更新: 当你修改 JavaScript 代码并保存时,Metro Bundler 会自动重新打包代码,应用程序会自动刷新,实现实时更新 (Fast Refresh)。
  • 开发者菜单: 应用程序可以通过摇晃设备(真机)或使用快捷键(模拟器)打开开发者菜单,其中包含一些调试选项,例如 “Reload”、“Debug JS Remotely” 等。
  • 不构建原生应用: 这个命令本身不构建原生 Android 或 iOS 应用。它只是启动一个开发服务器,供已经构建好的应用连接。
  • 通常与 expo-dev-client 配合使用: 虽然也可以与 Expo Go 配合使用,但为了获得最佳开发体验,尤其是需要使用原生模块或进行更深入调试时,通常与 expo-dev-client 配合使用。
  • 作用: 构建并安装 Android 应用程序到连接的 Android 设备或模拟器。这个命令会: 如果尚未安装 expo-dev-client,则会提示你安装。 根据你的项目配置(包括 JavaScript 代码和原生代码),构建一个包含 expo-dev-client 的 Android 应用程序。 将构建好的应用程序安装到你的设备或模拟器上。
  • 构建原生应用: 这个命令会构建原生 Android 应用(.apk 文件)。
  • 需要设备或模拟器: 这个命令需要在连接的 Android 设备或运行的 Android 模拟器上执行。
  • 首次运行较慢: 首次运行此命令时,由于需要构建应用程序,因此速度较慢。后续运行速度会加快,因为只会重新打包更改的部分。
  • 配合 expo start --dev-client 使用: 构建完成后,通常需要运行 expo start —dev-client 命令来启动开发服务器,以便应用程序可以连接并进行实时更新。

bun add expo-linear-gradient vs bunx expo install expo-linear-gradient

Section titled “bun add expo-linear-gradient vs bunx expo install expo-linear-gradient”

npx expo install expo-linear-gradientbun add expo-linear-gradient 都是在 React Native 项目中安装 expo-linear-gradient 包的命令,但它们使用的包管理器不同,并且针对的项目类型也有所侧重。

  1. npx expo install expo-linear-gradient:
  • 使用的包管理器:Expo CLI (expo)

  • 适用的项目类型:主要用于Expo managed wrokflow 项目

  • 工作方式:

    • npxNode 包执行器,用于执行本地或全局安装的 npm 包。
    • expo installExpo CLI 提供的命令,它不仅会安装指定的包,还会自动处理一些必要的配置,例如更新 package.json 文件、安装兼容的依赖项等。
    • 对于 Expo managed workflow 项目,expo install 会确保安装的包与当前 Expo SDK 版本兼容。这是非常重要的,因为 Expo SDK 版本和依赖包之间存在一定的兼容性要求。
  • 优点:

    • 简单方便,尤其是在 Expo managed workflow 项目中。
    • 自动处理依赖关系和配置,减少手动操作。
    • 确保依赖包与 Expo SDK 版本兼容。
  • 缺点:

    • 只能在安装了 Expo CLI 的环境中使用。
    • 不适用于 bare React Native 项目(除非使用 Expo prebuild)。
  1. bun add expo-linear-gradient:
  • 使用的包管理器: Bun。
  • 适用项目类型: 适用于使用 Bun 作为包管理器的 React Native 项目。包括 Expo managed workflow 项目(在安装了 Bun 的情况下),以及 bare React Native 项目。
  • 工作方式:
    • bun add 是 Bun 提供的命令,用于安装包。
    • Bun 是一个新兴的 JavaScript 运行时和包管理器,旨在提供更快的速度和更好的性能。
  • 优点:
    • 速度通常比 npm 和 yarn 快。
    • 支持多种包管理器格式(npm、yarn、pnpm)。
  • 缺点:
    • 相对较新,生态不如 npm 成熟。
    • 需要单独安装 Bun。

(NOBRIDGE) ERROR Warning: Error: Maximum update depth exceeded.

Section titled “(NOBRIDGE) ERROR Warning: Error: Maximum update depth exceeded.”

在使用Zustnd 时,出现以下错误警告信息:

(NOBRIDGE) ERROR Warning: Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
This error is located at:
in ArtistsScreen
in Unknown (created by Route(index))
in Suspense (created by Route(index))
in Route (created by Route(index))
in Route(index) (created by SceneView)
in StaticContainer
in EnsureSingleNavigator (created by SceneView)
in SceneView (created by NativeStackNavigator)
in RNSScreenContentWrapper (created by ScreenContentWrapper)
in ScreenContentWrapper (created by DebugContainer)
in DebugContainer
in RNSScreen (created by Animated(Anonymous))
in Animated(Anonymous)
in Suspender (created by Freeze)
in Suspense (created by Freeze)
in Freeze (created by DelayedFreeze)
in DelayedFreeze
in InnerScreen (created by Screen)
in Screen
in ScreenStackItem (created by SceneView)
in SceneView (created by NativeStackView)
in RNSScreenStack (created by ScreenStack)
in Unknown (created by ScreenStack)
in ScreenStack (created by NativeStackView)
in RCTView (created by View)
in View (created by SafeAreaProviderCompat)
in SafeAreaProviderCompat (created by NativeStackView)
in NativeStackView (created by NativeStackNavigator)
in PreventRemoveProvider (created by NavigationContent)
in NavigationContent
in Unknown (created by NativeStackNavigator)
in NativeStackNavigator
in Unknown (created by ArtistsScreenLayout)
in RCTView (created by View)
in View (created by ArtistsScreenLayout)
in ArtistsScreenLayout
in Unknown (created by Route(artists))
in Suspense (created by Route(artists))
in Route (created by Route(artists))
in Route(artists) (created by SceneView)
in StaticContainer
in EnsureSingleNavigator (created by SceneView)
in SceneView (created by BottomTabNavigator)
in RCTView (created by View)
in View (created by Screen)
in RCTView (created by View)
in View (created by Animated(View))
in Animated(View) (created by Background)
in Background (created by Screen)
in Screen (created by BottomTabView)
in RNSScreen (created by Animated(Anonymous))
in Animated(Anonymous)
in Suspender (created by Freeze)
in Suspense (created by Freeze)
in Freeze (created by DelayedFreeze)
in DelayedFreeze
in InnerScreen (created by Screen)
in Screen (created by MaybeScreen)
in MaybeScreen (created by BottomTabView)
in RNSScreenNavigationContainer (created by ScreenContainer)
in ScreenContainer (created by MaybeScreenContainer)
in MaybeScreenContainer (created by BottomTabView)
...

原有的实现方式:

export const useArtists = () => {
return useLibraryStore((state) => {
return state.tracks.reduce((acc, track) => {
const existingArtist = acc.find((artist) => artist.name === track.artist);
if (existingArtist) {
existingArtist.tracks.push(track);
} else {
acc.push({
name: track.artist ?? "Unknown",
tracks: [track],
});
}
return acc;
}, [] as Artist[]);
});
};
const artists = useArtists();
const filteredArtists = useMemo(() => {
if (!search) return artists;
return artists.filter(artistNameFilter(search));
}, [artists, search]);

(NOBRIDGE) ERROR Warning: Error: Maximum update depth exceeded. 当组件触发无限循环重新渲染时,通常会发生此错误。

问题在于 useLibraryStore 如何更新 useArtists hook 内的状态。 当前的 useArtists 选择器每次调用时都会创建一个新的Artist对象数组,即使 state.tracks 没有更改也是如此。这是因为reduce总是创建一个新数组。

解决方案:使用 useMemouseArtists 选择器的结果缓存起来,可以避免无限循环。

export const useTracks = () => useLibraryStore((state) => state.tracks);
export const useArtists = () => {
return useTracks().reduce((acc, track) => {
const existingArtist = acc.find((artist) => artist.name === track.artist);
if (existingArtist) {
existingArtist.tracks.push(track);
} else {
acc.push({
name: track.artist ?? "Unknown",
tracks: [track],
});
}
return acc;
}, [] as Artist[]);
};
const artists = useArtists();
const filteredArtists = useMemo(() => {
if (!search) return artists;
return artists.filter(artistNameFilter(search));
}, [artists, search]);

React Native 的 JSI(JavaScript Interface)是 React Native 新架构中的一个关键组成部分,它带来了许多重要的影响,主要体现在性能、灵活性和跨平台一致性等方面。

  1. 性能提升
  • 消除 Bridge 带来的开销:

    • 在旧架构中,JavaScript 和原生代码之间的通信依赖于 Bridge,数据需要在两者之间进行序列化和反序列化,这会带来性能开销。
    • JSI 允许 JavaScript 直接调用原生代码,消除了这种序列化和反序列化的过程,从而提高了通信效率。
  • 同步调用:

    • JSI 使得 JavaScript 可以同步调用原生模块,这对于需要高性能的场景非常有用。
    • 例如,在数据存储、图像处理等场景中,同步调用可以显著提高性能。
  1. 更灵活的集成
  • 直接调用原生模块:
    • JSI 允许 JavaScript 直接持有对 C++ 宿主对象的引用,并对其进行调用,这使得原生模块的集成更加灵活。
    • 开发者可以更方便地使用原生平台的特性和功能。
  • 引擎替换:
    • JSI 使得 JavaScript 引擎(如 JSC)可以更容易地被替换成其他引擎(如 V8)。
    • 这为 React Native 的未来发展提供了更大的灵活性。
  1. 跨平台一致性
  • 共享的 C++ 核心:
    • JSI 为 React Native 提供了一个共享的 C++ 核心,这有助于在不同平台上保持一致的行为和性能。
    • 开发者可以更方便地编写跨平台的原生模块。
  1. 对开发者带来的影响
  • 原生模块开发:
    • JSI 的引入,给原生模块的开发带来了新的工作量。
    • 原生模块的开发者需要使用C++来与javascript进行交互。
  • 性能优化:
    • JSI 为开发者提供了更多优化 React Native 应用性能的手段。
    • 开发者可以利用 JSI 编写高性能的原生模块,从而提高应用的整体性能。

总结

JSI 的引入,是 React Native 架构的一次重大升级,它为 React Native 带来了更高的性能、更大的灵活性和更好的跨平台一致性。

Mysql Explain

在 MySQL 中,EXPLAIN 命令是一个非常有用的工具,它可以帮助我们深入了解 MySQL 执行一条 SQL 语句时的优化过程。 type 列和 extra 列是 EXPLAIN 结果中两个最重要的列,它们能提供关于查询执行方式的详细信息。

注意: EXPLAIN 提供的信息是基于 MySQL 优化器对 SQL 语句的分析,实际执行情况可能会有所不同。

type 列显示了 MySQL 处理查询时所使用的访问类型。不同的访问类型代表不同的查询执行效率,一般来说,访问类型越靠前,查询效率越高。

常见的访问类型包括:

  • system: 表中只有一行记录(系统表),直接返回。
  • const: 表中最多只有一条匹配的行,如主键或唯一索引。
  • eq_ref: PRIMARY KEY 或 UNIQUE index 的所有部分被用在连接中。
  • ref: 非唯一索引列或常数被用在连接。
  • range: 使用索引来检索一个范围的行,如使用 between、<、> 等操作符。
  • index: 全索引扫描,根据索引来获取所需的行。
  • all: 全表扫描,MySQL必须检索表中的所有行以找到匹配的行。

一般来说,我们希望查询的 type 尽可能靠前,比如 system 或 const,这表示 MySQL 能很快地找到数据。而 all 则表示 MySQL 需要扫描整个表,效率最低。

extra 列显示了 MySQL 执行查询时所使用的访问类型的详细信息。一些常见的 extra 值包括:

  • using index: 表示 MySQL 只使用了索引来完成查询,没有读取表中的行数据。
  • using where: 表示 MySQL 在读取索引后,又使用 where 条件对结果集进行了过滤。
  • using temporary: MySQL 需要创建一个临时表来存储结果。
  • using filesort: MySQL 需要对结果集进行排序,这通常发生在排序列没有索引的情况下。

一般来说,我们希望 extra 中出现 Using index,这表示查询效率很高。而 Using temporary 和 Using filesort 则表示查询效率较低,可能需要优化。

Terminal window
MySQL root@localhost:sakila> explain select * from inventory\G
***************************[ 1. row ]***************************
id | 1
select_type | SIMPLE
table | inventory
partitions | <null>
type | ALL
possible_keys | <null>
key | <null>
key_len | <null>
ref | <null>
rows | 4581
filtered | 100.0
Extra | <null>
Terminal window
MySQL root@localhost:sakila> explain select store_id , film_id FROM inventory\G
***************************[ 1. row ]***************************
id | 1
select_type | SIMPLE
table | inventory
partitions | <null>
type | index
possible_keys | <null>
key | idx_store_id_film_id
key_len | 3
ref | <null>
rows | 4581
filtered | 100.0
Extra | Using index

type 列和 extra 列是分析 MySQL 查询性能的重要指标。通过了解它们的含义,我们可以针对性地优化 SQL 语句,提高查询效率。

优化建议:

  • 创建合适的索引: 为经常用在 where 条件、order by 和 group by 子句中的列创建索引。
  • 避免使用 select *: 只查询需要的列,减少数据的传输。
  • 优化 where 条件: 尽量使用索引列,避免使用函数和表达式。
  • 分表: 当数据量过大时,可以考虑分表。

Casbin

Casbin是一个强大且高效的开源访问控制库,支持各种访问控制模型,用于在全局范围内执行授权。

执行一组规则就像在策略文件中列出主题、对象和期望的允许操作(或根据您的需要的任何其他格式)一样简单。 这在所有使用Casbin的流程中都是同义的。 开发者/管理员对布局、执行和授权条件的控制是完全的,这些都是通过模型文件设置的。 Casbin提供了一个Enforcer,用于根据提供给Enforcer的策略和模型文件验证传入的请求。

需要搞清楚两个概念:

  • AuthN 系统主要用于认证 (Authentication),决定谁访问了系统。
  • AuthZ 系统主要用于授权 (Authorization),决定访问者具有什么样的权限。

AuthN 系统是用户登录系统,并且获取用户的身份信息,可以用 JWT 来实现。 AuthZ 系统是根据用户的身份信息,决定用户具有什么权限,可以用 Casbin 来实现。

Casbin是一个授权库,可以在我们希望某个对象或实体被特定用户或主体访问的流程中使用。 访问类型,即动作,可以是读取,写入,删除,或者由开发者设置的任何其他动作。 这就是Casbin最广泛使用的方式,它被称为“标准”或经典的{ 主体, 对象, 动作 }流程。

Casbin能够处理许多复杂的授权场景,而不仅仅是标准流程。 可以添加角色(RBAC)属性(ABAC)等。

  1. 在经典的{ 主体, 对象, 动作 }形式或者你定义的自定义形式中执行策略。 支持允许和拒绝授权。
  2. 处理访问控制模型及其策略的存储。
  3. 管理角色-用户映射和角色-角色映射(也称为RBAC中的角色层次)。
  4. 支持内置的超级用户,如root或administrator。 超级用户可以在没有明确权限的情况下做任何事情。
  5. 提供多个内置操作符以支持规则匹配。 例如,keyMatch可以将资源键/foo/bar映射到模式/foo*。
  1. 身份认证 authentication(即验证用户的用户名和密码),Casbin 只负责访问控制。 应该有其他专门的组件负责身份认证,然后由 Casbin 进行访问控制,二者是相互配合的关系。
  2. 管理用户列表或角色列表。 Casbin 认为由项目自身来管理用户、角色列表更为合适, 用户通常有他们的密码, 但是 Casbin 的设计思想并不是把它作为一个存储密码的容器。 而是存储RBAC方案中用户和角色之间的映射关系。
ModelModel filePolicy file
ACLbasic_model.confbasic_policy.csv
ACL with superuserbasic_with_root_model.confbasic_policy.csv
ACL without usersbasic_without_users_model.confbasic_without_users_policy.csv
ACL without resourcesbasic_without_resources_model.confbasic_without_resources_policy.csv
RBACrbac_model.confrbac_policy.csv
RBAC with resource rolesrbac_with_resource_roles_model.confrbac_with_resource_roles_policy.csv
RBAC with domains/tenantsrbac_with_domains_model.confrbac_with_domains_policy.csv
ABACabac_model.confN/A
RESTfulkeymatch_model.confkeymatch_policy.csv
Deny-overriderbac_with_not_deny_model.confrbac_with_deny_policy.csv
Allow-and-denyrbac_with_deny_model.confrbac_with_deny_policy.csv
Prioritypriority_model.confpriority_policy.csv
Explicit Prioritypriority_model_explicitpriority_policy_explicit.csv
Subject-Prioritysubject_priority_model.confsubject_priority_policyl.csv

Casbin中访问控制模型抽象为PERM元模型的 CONF 文件,PERM 模型至少由四个基础部分组成:[request_definition](策略)、[policy_definition](效果)、[policy_effect](请求)和[matchers](匹配器)。 如果模型使用基于角色的访问控制(RBAC),它还应包括[role_definition]部分。

Casbin中最基本和最简单的模型是ACL。 ACL的模型CONF如下:

# Request definition
[request_definition]
r = sub, obj, act
# Policy definition
[policy_definition]
p = sub, obj, act
# Policy effect
[policy_effect]
e = some(where (p.eft == allow))
# Matchers
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act

定义请求参数。 基本请求是一个元组对象,至少需要一个主体(被访问实体),对象(被访问资源)和动作(访问方法)。

例子中,subobjact代表了经典的访问三元组:主体(访问实体),对象(被访问资源)和动作(访问方法)。也可以定义自己的请求格式

定义访问策略的模型。 它指定了策略规则文档中字段的名称和顺序。

注意:如果未定义eft(策略结果),则不会读取策略文件中的结果字段,匹配策略结果将默认允许。

例子中定义了策略的模型,那么在策略文件中,可以定义相应的策略如:

p, alice, data1, read

策略中的每一行都被称为策略规则,每个策略规则都以策略类型开始。

对匹配器的匹配结果进行逻辑组合判断。

如:

  1. e = some(where (p.eft == allow)), 意味着,如果有任何匹配的allow策略规则,最终效果是allow(也称为允许覆盖)。

  2. e = some(where (p.eft == allow)) && !some(where (p.eft == deny)), 意味着,如果有一个策略匹配到允许的结果,并且没有策略匹配到拒绝的结果,结果为真。

  3. e = !some(where (p.eft == deny)),意味着,如果没有匹配到deny策略规则,最终的结果为真。

定义请求和策略的匹配规则。如示例中的m = r.sub == p.sub && r.obj == p.obj && r.act == p.act,意味着,请求和策略的主体,对象和动作都必须完全匹配。

匹配器中可以使用算术运算符如+, -, *, /和逻辑运算符如&&, ||, !。

用于定义 RBAC 角色继承关系。 Casbin 支持多个实例的 RBAC 系统,其中用户可以拥有角色及其继承关系,资源也可以拥有角色及其继承关系。 这两个 RBAC 系统不会相互干扰。 此部分为可选。 如果你在模型中不使用 RBAC 角色,那么省略此部分。