VillageSQL深度解析:MySQL的"无许可创新"革命,如何让老牌数据库拥抱AI时代
引言:MySQL的创新困境
2026年2月,一个名为VillageSQL的开源项目悄然发布,却引发了数据库社区的广泛讨论。这不是一个普通的数据库项目——它是MySQL的跟踪分支(tracking fork),旨在解决一个长期困扰MySQL生态的核心问题:创新差距。
作为全球使用最广泛的开源数据库,MySQL在DB-Engines排名中长期位居第二,仅次于Oracle。然而,近年来数据库创新的焦点明显向PostgreSQL倾斜。为什么?答案藏在一个词里:扩展框架。
PostgreSQL的扩展生态系统让开发者可以独立于核心版本发布周期,自主开发和迭代新功能。而MySQL虽然拥有插件机制和组件框架,但其限制性设计严重阻碍了社区创新。在AI和Agent工作负载爆发的今天,这种差距变得更加致命——向量搜索、语义检索、嵌入存储……这些RAG(检索增强生成)应用的核心能力,在标准MySQL发行版中至今未见原生支持。
VillageSQL的诞生,正是为了填补这一空白。本文将从技术架构、实现原理、扩展开发、性能优化等多个维度,深入剖析这个"MySQL版的PostgreSQL扩展框架"。
一、创新差距的本质:为什么MySQL需要变革
1.1 PostgreSQL的"无许可创新"模式
PostgreSQL的成功并非偶然。其扩展框架的设计哲学可以用一个词概括:permissionless innovation(无许可创新)。
-- PostgreSQL扩展安装示例
CREATE EXTENSION IF NOT EXISTS vector;
CREATE EXTENSION IF NOT EXISTS pg_trgm;
CREATE EXTENSION IF NOT EXISTS hstore;
这三行SQL语句背后,是一个强大的扩展生态系统:
- pgvector:向量索引和相似性搜索
- pg_trgm:三元组相似度匹配
- hstore:键值对存储
开发者无需等待PostgreSQL核心团队批准,就可以发布新功能。任何人都可以创建扩展,分享给社区。这种开放模式催生了数百个高质量扩展,形成了正向循环的创新生态。
1.2 MySQL的插件困境
MySQL也有插件机制,但存在根本性限制:
// MySQL插件API的局限性示例
struct st_mysql_plugin {
int type; // 插件类型被严格限定
void *info; // 信息结构体
const char *name; // 名称
const char *author; // 作者
const char *desc; // 描述
int license; // 许可证
int (*init)(void *); // 初始化函数
int (*deinit)(void *); // 反初始化函数
unsigned short version;
struct st_mysql_show_var *status_vars;
struct st_mysql_sys_var **system_vars;
void *reserved;
};
问题在于:
- 类型限制:只支持预定义的插件类型(存储引擎、全文解析器、守护进程等)
- 功能边界:无法添加新的数据类型、操作符或索引方法
- 分发困难:插件必须编译进服务器或放在特定目录,缺乏统一的包管理机制
这导致一个尴尬的现实:你想为MySQL添加向量搜索?请等待Oracle官方版本。你想添加自定义数据类型?对不起,不支持。
1.3 AI时代的功能性缺口
当AI应用成为主流,MySQL的功能性缺口变得更加明显:
| 功能需求 | PostgreSQL | MySQL(标准版) |
|---|---|---|
| 向量搜索 | pgvector(原生) | 需外部服务 |
| 相似性匹配 | pg_trgm | LIKE(性能差) |
| JSON路径查询 | 原生支持 | 有限支持 |
| 全文搜索 | 多语言分词 | 依赖插件 |
| 自定义类型 | CREATE TYPE | 不支持 |
这种差距正在推动开发者转向其他数据库。VillageSQL的使命就是让这些开发者重新考虑MySQL。
二、VillageSQL架构设计:跟踪分支的艺术
2.1 什么是跟踪分支(Tracking Fork)?
VillageSQL不是传统意义上的分支。传统分支通常会与上游分道扬镳,形成独立演进路线。而跟踪分支则承诺:
- 持续同步上游:所有MySQL官方更新都会合并到VillageSQL
- 完全兼容:现有MySQL应用可以无缝迁移
- 增量增强:仅添加扩展框架,不修改核心逻辑
MySQL Official (Oracle)
│
│ 定期合并
▼
VillageSQL (Community)
│
│ 扩展框架层
▼
┌─────────────────────────────────┐
│ Extension Framework │
│ ┌─────┬─────┬─────┬─────┐ │
│ │UUID │ IPv6│ AI │Crypto│ │
│ └─────┴─────┴─────┴─────┘ │
└─────────────────────────────────┘
这种设计让VillageSQL成为MySQL的"超集"而非"替代品"。
2.2 扩展框架核心组件
VillageSQL的扩展框架由三个核心组件构成:
2.2.1 扩展描述文件(Extension Manifest)
每个扩展需要一个元数据描述文件:
// vsql_uuid.extension.json
{
"name": "vsql_uuid",
"version": "1.0.0",
"author": "VillageSQL Team",
"description": "UUID data type support for MySQL",
"provides": {
"types": ["UUID"],
"functions": [
"UUID()",
"UUID_TO_BIN()",
"BIN_TO_UUID()",
"IS_UUID()"
],
"operators": ["=", "<>", "<", ">", "<=", ">="]
},
"requires": {
"mysql_version": ">=8.0",
"villagesql_version": ">=0.1.0"
},
"entry_point": "libvsql_uuid.so",
"init_function": "vsql_uuid_init",
"deinit_function": "vsql_uuid_deinit"
}
2.2.2 扩展加载器(Extension Loader)
VillageSQL服务器启动时,扩展加载器会:
// 简化的扩展加载逻辑
class ExtensionLoader {
public:
bool load_extension(const std::string& name) {
// 1. 解析扩展描述文件
auto manifest = parse_manifest(name + ".extension.json");
// 2. 加载动态库
void* handle = dlopen(manifest.entry_point.c_str(), RTLD_NOW);
if (!handle) return false;
// 3. 注册数据类型
for (const auto& type : manifest.provides.types) {
TypeRegistry::register_type(type,
get_type_handler(handle, type));
}
// 4. 注册函数
for (const auto& func : manifest.provides.functions) {
FunctionRegistry::register_function(func,
get_function_handler(handle, func));
}
// 5. 初始化扩展
auto init_fn = (int(*)(void))dlsym(handle, manifest.init_function);
return init_fn() == 0;
}
};
2.2.3 类型注册表(Type Registry)
这是VillageSQL最关键的创新——允许注册自定义数据类型:
// 类型处理器接口
class TypeHandler {
public:
virtual ~TypeHandler() = default;
// 存储相关
virtual size_t storage_size() const = 0;
virtual bool serialize(const FieldValue& value,
std::vector<uint8_t>& buffer) const = 0;
virtual bool deserialize(const uint8_t* buffer,
size_t size, FieldValue& value) const = 0;
// 比较操作
virtual int compare(const FieldValue& a,
const FieldValue& b) const = 0;
// 类型转换
virtual bool from_string(const std::string& str,
FieldValue& value) const = 0;
virtual std::string to_string(const FieldValue& value) const = 0;
};
// UUID类型处理器示例
class UUIDTypeHandler : public TypeHandler {
size_t storage_size() const override { return 16; }
int compare(const FieldValue& a, const FieldValue& b) const override {
const auto& ua = a.get<std::array<uint8_t, 16>>();
const auto& ub = b.get<std::array<uint8_t, 16>>();
return memcmp(ua.data(), ub.data(), 16);
}
// ... 其他方法实现
};
2.3 "Myonic"扩展:MySQL风格的设计哲学
VillageSQL团队创造了"Myonic"这个词,意为"MySQL-idiomatic"(MySQL风格)。扩展的使用方式高度贴合MySQL语法:
-- 安装扩展(比PostgreSQL更简洁)
INSTALL EXTENSION 'vsql_uuid';
-- 使用扩展类型创建表
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT UUID(),
username VARCHAR(50),
email VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 使用扩展函数
INSERT INTO users (id, username, email)
VALUES (UUID(), 'alice', 'alice@example.com');
-- 查询验证
SELECT id, BIN_TO_UUID(id) as uuid_string,
UUID_TO_BIN(BIN_TO_UUID(id)) as uuid_bin
FROM users;
对比PostgreSQL的CREATE EXTENSION语法,VillageSQL的INSTALL EXTENSION更加直观,符合MySQL用户的习惯。
三、内置扩展深度解析
VillageSQL Alpha版本已提供多个实用扩展,让我们深入分析每个扩展的实现细节。
3.1 vsql_uuid:UUID原生支持
MySQL 8.0虽然引入了UUID()函数,但返回的是字符串,存储效率低下。VillageSQL的原生UUID类型带来显著优势:
-- 传统方式(36字节字符串)
CREATE TABLE old_users (
id CHAR(36) PRIMARY KEY,
name VARCHAR(100)
);
-- VillageSQL方式(16字节二进制)
INSTALL EXTENSION 'vsql_uuid';
CREATE TABLE new_users (
id UUID PRIMARY KEY DEFAULT UUID(),
name VARCHAR(100)
);
-- 存储效率对比
-- 100万条记录:
-- old_users: ~36MB (仅ID列)
-- new_users: ~16MB (仅ID列)
-- 节省约55%存储空间
实现原理:
// UUID类型的核心实现
class UUID {
public:
// 版本4(随机)UUID生成
static UUID generate_v4() {
UUID uuid;
// 使用加密安全的随机数生成器
RAND_bytes(uuid.bytes_.data(), 16);
// 设置版本位(版本4)
uuid.bytes_[6] = (uuid.bytes_[6] & 0x0F) | 0x40;
// 设置变体位(RFC 4122)
uuid.bytes_[8] = (uuid.bytes_[8] & 0x3F) | 0x80;
return uuid;
}
// 字符串解析(支持多种格式)
static std::optional<UUID> parse(std::string_view str) {
// 支持格式:
// 6ba7b810-9dad-11d1-80b4-00c04fd430c8
// 6ba7b8109dad11d180b400c04fd430c8
// {6ba7b810-9dad-11d1-80b4-00c04fd430c8}
// urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8
std::array<uint8_t, 16> bytes;
// ... 解析逻辑
return UUID(bytes);
}
// 二进制序列化(用于存储)
std::array<uint8_t, 16> to_bytes() const {
return bytes_;
}
private:
std::array<uint8_t, 16> bytes_;
};
3.2 vsql_network:网络地址类型
支持IPv4、IPv6和MAC地址的原生类型:
INSTALL EXTENSION 'vsql_network';
-- IPv6地址存储
CREATE TABLE servers (
id INT AUTO_INCREMENT PRIMARY KEY,
ipv6_addr INET6 NOT NULL,
mac_addr MACADDR,
hostname VARCHAR(255)
);
-- 插入数据
INSERT INTO servers (ipv6_addr, mac_addr, hostname)
VALUES (
'2001:db8::1',
'08:00:2b:01:02:03',
'server1.example.com'
);
-- 网络查询
SELECT * FROM servers
WHERE ipv6_addr <<= '2001:db8::/32'; -- 属于该网段
IPv6地址的高效存储:
// IPv6类型存储(16字节)
class INET6 {
public:
struct IPv6Addr {
uint8_t bytes[16];
};
IPv6Addr addr;
uint8_t prefix_len; // 用于CIDR表示法
// 子网判断
bool is_in_subnet(const INET6& network) const {
if (prefix_len == 0) return true;
int full_bytes = prefix_len / 8;
int remaining_bits = prefix_len % 8;
// 比较完整字节
for (int i = 0; i < full_bytes; i++) {
if (addr.bytes[i] != network.addr.bytes[i]) {
return false;
}
}
// 比较剩余位
if (remaining_bits > 0) {
uint8_t mask = (0xFF << (8 - remaining_bits)) & 0xFF;
if ((addr.bytes[full_bytes] & mask) !=
(network.addr.bytes[full_bytes] & mask)) {
return false;
}
}
return true;
}
};
3.3 vsql_crypto:加密函数库
内置多种加密算法,无需外部依赖:
INSTALL EXTENSION 'vsql_crypto';
-- 密码哈希
CREATE TABLE auth_users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) UNIQUE,
password_hash CHAR(60), -- bcrypt固定长度
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 注册用户
INSERT INTO auth_users (username, password_hash)
VALUES ('alice', CRYPTO_BCRYPT('secure_password_123', 12));
-- 密码验证(应用层)
-- SELECT * FROM auth_users
-- WHERE username = 'alice'
-- AND CRYPTO_BCRYPT_VERIFY(password_hash, 'secure_password_123');
-- 其他加密函数
SELECT
CRYPTO_SHA256('hello world') as sha256,
CRYPTO_SHA512('hello world') as sha512,
CRYPTO_HMAC_SHA256('secret_key', 'message') as hmac,
CRYPTO_AES_ENCRYPT('key_32_bytes_here___', 'plaintext') as encrypted,
CRYPTO_AES_DECRYPT('key_32_bytes_here___',
CRYPTO_AES_ENCRYPT('key_32_bytes_here___', 'plaintext')) as decrypted;
3.4 vsql_complex:复数类型
数学和科学计算场景的复数支持:
INSTALL EXTENSION 'vsql_complex';
CREATE TABLE signal_samples (
id INT AUTO_INCREMENT PRIMARY KEY,
frequency FLOAT,
amplitude COMPLEX,
phase COMPLEX,
recorded_at TIMESTAMP
);
-- 插入复数数据
INSERT INTO signal_samples (frequency, amplitude)
VALUES (1000.0, COMPLEX(3.0, 4.0)); -- 3+4i
-- 复数运算
SELECT
amplitude,
COMPLEX_ABS(amplitude) as magnitude, -- |3+4i| = 5
COMPLEX_ARG(amplitude) as argument, -- arg(3+4i) ≈ 0.927 rad
COMPLEX_CONJ(amplitude) as conjugate -- 3-4i
FROM signal_samples;
-- 复数FFT预处理
SELECT
frequency,
amplitude * COMPLEX(1, 0) as normalized,
COMPLEX_EXP(COMPLEX(0, -2*PI()*frequency/1000)) as twiddle
FROM signal_samples;
3.5 vsql_ai:AI Prompt函数
这是VillageSQL最具前瞻性的扩展——在SQL中直接调用大模型:
INSTALL EXTENSION 'vsql_ai';
-- 配置AI后端(支持OpenAI、Anthropic、本地模型)
SET @ai_endpoint = 'https://api.openai.com/v1/chat/completions';
SET @ai_model = 'gpt-4';
SET @ai_api_key = ENV('OPENAI_API_KEY');
-- 直接在SQL中使用AI
CREATE TABLE product_descriptions (
id INT AUTO_INCREMENT PRIMARY KEY,
product_name VARCHAR(255),
raw_features TEXT,
ai_description TEXT GENERATED ALWAYS AS (
AI_PROMPT(
'Generate a compelling product description based on: ' + raw_features,
@ai_model
)
) STORED,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 智能数据分类
SELECT
customer_feedback,
AI_CLASSIFY(
customer_feedback,
'positive,negative,neutral,mixed'
) as sentiment,
AI_EXTRACT(
customer_feedback,
'product_name,issue_type,urgency'
) as structured_data
FROM customer_reviews;
-- RAG查询增强
SELECT
id,
content,
AI_EMBED(content) as embedding,
AI_SIMILARITY(
AI_EMBED('用户查询内容'),
stored_embedding
) as relevance_score
FROM knowledge_base
ORDER BY relevance_score DESC
LIMIT 10;
AI Prompt函数的实现架构:
class AIPromptFunction : public SQLFunction {
public:
FieldValue execute(const std::vector<FieldValue>& args) override {
std::string prompt = args[0].get_string();
std::string model = args.size() > 1 ? args[1].get_string() : "default";
// 构建HTTP请求
nlohmann::json request = {
{"model", model},
{"messages", {
{{"role", "user"}, {"content", prompt}}
}},
{"temperature", 0.7},
{"max_tokens", 2048}
};
// 异步HTTP调用(避免阻塞SQL线程)
auto future = http_client_.post_async(endpoint_, request);
// 处理响应
auto response = future.get();
if (response.status == 200) {
auto json = nlohmann::json::parse(response.body);
return json["choices"][0]["message"]["content"];
}
throw SQLException("AI API error: " + response.body);
}
private:
HttpClient http_client_;
std::string endpoint_;
};
四、扩展开发实战:从零构建自定义扩展
让我们开发一个实际有用的扩展——vsql_jsonpath,提供比MySQL原生JSON路径查询更强大的功能。
4.1 扩展结构
vsql_jsonpath/
├── vsql_jsonpath.extension.json # 扩展描述文件
├── src/
│ ├── jsonpath_parser.y # 路径语法解析器
│ ├── jsonpath_evaluator.cpp # 路径求值引擎
│ └── jsonpath_functions.cpp # SQL函数实现
├── include/
│ └── jsonpath.h
├── tests/
│ └── jsonpath_test.cpp
├── CMakeLists.txt
└── README.md
4.2 扩展描述文件
{
"name": "vsql_jsonpath",
"version": "1.0.0",
"author": "Your Name",
"description": "Advanced JSONPath query support for MySQL",
"provides": {
"types": [],
"functions": [
"JSONPATH_QUERY(json, path)",
"JSONPATH_EXISTS(json, path)",
"JSONPATH_MODIFY(json, path, value)",
"JSONPATH_KEYS(json, path)"
],
"operators": []
},
"requires": {
"mysql_version": ">=8.0",
"villagesql_version": ">=0.1.0"
},
"entry_point": "libvsql_jsonpath.so",
"init_function": "vsql_jsonpath_init",
"deinit_function": "vsql_jsonpath_deinit",
"config": {
"max_json_depth": 100,
"max_path_length": 1024
}
}
4.3 JSONPath解析器
// jsonpath_parser.y - 使用Bison语法
%require "3.2"
%language "c++"
%define api.parser.class {JSONPathParser}
%define api.namespace {vsql}
%token DOT DOTDOT LBRACKET RBRACKET STAR COLON COMMA
%token <std::string> STRING
%token <int> NUMBER
%token <std::string> IDENTIFIER
%type <JSONPathNode*> path expr predicate
%%
path:
'$' { $$ = new RootNode(); }
| path '.' IDENTIFIER {
$$ = new MemberNode($1, $3);
}
| path DOTDOT IDENTIFIER {
$$ = new RecursiveMemberNode($1, $3);
}
| path '[' expr ']' {
$$ = new IndexNode($1, $3);
}
| path '[' STAR ']' {
$$ = new WildcardNode($1);
}
| path '[' NUMBER ':' NUMBER ']' {
$$ = new SliceNode($1, $3, $5);
}
| path '[' predicate ']' {
$$ = new FilterNode($1, $3);
}
;
expr:
STRING { $$ = new LiteralNode($1); }
| NUMBER { $$ = new LiteralNode($1); }
| path { $$ = $1; }
| expr ',' expr { $$ = new UnionNode($1, $3); }
;
predicate:
path '=' expr { $$ = new ComparisonNode($1, $3, Op::EQ); }
| path '<' expr { $$ = new ComparisonNode($1, $3, Op::LT); }
| predicate "&&" predicate { $$ = new LogicalNode($1, $3, Op::AND); }
| predicate "||" predicate { $$ = new LogicalNode($1, $3, Op::OR); }
;
%%
4.4 求值引擎
// jsonpath_evaluator.cpp
class JSONPathEvaluator {
public:
// 求值入口
std::vector<nlohmann::json> evaluate(
const nlohmann::json& document,
const JSONPathNode* path
) {
context_.clear();
path->accept(*this, document);
return context_;
}
// 访问者模式实现
void visit(const RootNode& node, const nlohmann::json& current) {
// 根节点,返回整个文档
context_.push_back(current);
}
void visit(const MemberNode& node, const nlohmann::json& current) {
if (!current.is_object()) return;
if (current.contains(node.member())) {
node.child()->accept(*this, current[node.member()]);
}
}
void visit(const RecursiveMemberNode& node, const nlohmann::json& current) {
// 递归搜索所有层级
std::function<void(const nlohmann::json&)> search =
[&](const nlohmann::json& doc) {
if (doc.is_object()) {
if (doc.contains(node.member())) {
node.child()->accept(*this, doc[node.member()]);
}
for (auto& [key, value] : doc.items()) {
search(value);
}
} else if (doc.is_array()) {
for (auto& item : doc) {
search(item);
}
}
};
search(current);
}
void visit(const FilterNode& node, const nlohmann::json& current) {
if (!current.is_array()) return;
for (const auto& item : current) {
if (evaluate_predicate(node.predicate(), item)) {
node.child()->accept(*this, item);
}
}
}
private:
std::vector<nlohmann::json> context_;
bool evaluate_predicate(
const PredicateNode* pred,
const nlohmann::json& context
) {
// 谓词求值逻辑
// ...
}
};
4.5 SQL函数实现
// jsonpath_functions.cpp
#include "vsql_extension.h"
#include "jsonpath_evaluator.h"
extern "C" {
// JSONPATH_QUERY函数
class JSONPathQueryFunction : public SQLFunction {
public:
std::string name() const override { return "JSONPATH_QUERY"; }
FieldValue execute(const std::vector<FieldValue>& args) override {
if (args.size() != 2) {
throw SQLException("JSONPATH_QUERY requires 2 arguments");
}
// 解析JSON文档
nlohmann::json doc = nlohmann::json::parse(
args[0].get_string()
);
// 解析路径表达式
JSONPathParser parser;
auto path = parser.parse(args[1].get_string());
// 求值
JSONPathEvaluator evaluator;
auto results = evaluator.evaluate(doc, path.get());
// 返回结果数组
nlohmann::json result_array = nlohmann::json::array();
for (const auto& r : results) {
result_array.push_back(r);
}
return FieldValue(result_array.dump());
}
std::vector<FunctionParameter> parameters() const override {
return {
{"json", FieldType::STRING},
{"path", FieldType::STRING}
};
}
FieldType return_type() const override {
return FieldType::STRING; // JSON字符串
}
};
// 扩展初始化
int vsql_jsonpath_init(ExtensionAPI* api) {
// 注册函数
api->register_function(std::make_unique<JSONPathQueryFunction>());
api->register_function(std::make_unique<JSONPathExistsFunction>());
api->register_function(std::make_unique<JSONPathModifyFunction>());
api->register_function(std::make_unique<JSONPathKeysFunction>());
return 0;
}
// 扩展清理
int vsql_jsonpath_deinit(ExtensionAPI* api) {
return 0;
}
} // extern "C"
4.6 使用示例
-- 安装扩展
INSTALL EXTENSION 'vsql_jsonpath';
-- 创建测试表
CREATE TABLE orders (
id INT AUTO_INCREMENT PRIMARY KEY,
order_data JSON,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 插入测试数据
INSERT INTO orders (order_data) VALUES (
'{
"orderId": "ORD-001",
"customer": {
"id": "CUST-123",
"name": "Alice",
"email": "alice@example.com"
},
"items": [
{"productId": "P001", "name": "Widget", "price": 29.99, "qty": 2},
{"productId": "P002", "name": "Gadget", "price": 49.99, "qty": 1}
],
"shipping": {
"address": {"city": "NYC", "zip": "10001"},
"method": "express"
},
"status": "processing"
}'
);
-- 使用JSONPath查询
-- 1. 获取所有产品名称
SELECT JSONPATH_QUERY(order_data, '$.items[*].name') as product_names
FROM orders;
-- 结果: ["Widget", "Gadget"]
-- 2. 递归查找所有ID
SELECT JSONPATH_QUERY(order_data, '$..id') as all_ids
FROM orders;
-- 结果: ["ORD-001", "CUST-123", "P001", "P002"]
-- 3. 过滤条件查询
SELECT JSONPATH_QUERY(
order_data,
'$.items[?(@.price > 30)].name'
) as expensive_items
FROM orders;
-- 结果: ["Gadget"]
-- 4. 检查路径是否存在
SELECT JSONPATH_EXISTS(order_data, '$.shipping.address.city')
FROM orders;
-- 结果: 1 (true)
-- 5. 修改JSON数据
SELECT JSONPATH_MODIFY(
order_data,
'$.status',
'"shipped"'
) as updated_order
FROM orders;
-- 结果: {"orderId": "ORD-001", ..., "status": "shipped"}
-- 6. 获取所有键名
SELECT JSONPATH_KEYS(order_data, '$.customer')
FROM orders;
-- 结果: ["id", "name", "email"]
五、向量搜索扩展:让MySQL拥抱AI
VillageSQL路线图中最重要的功能是向量索引和向量搜索。让我们预览这一革命性扩展的设计。
5.1 向量类型设计
-- 预期语法(未来版本)
INSTALL EXTENSION 'vsql_vector';
-- 创建向量列
CREATE TABLE embeddings (
id INT AUTO_INCREMENT PRIMARY KEY,
content TEXT,
embedding VECTOR(1536), -- OpenAI embedding维度
metadata JSON,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 创建向量索引(HNSW)
CREATE VECTOR INDEX idx_embedding
ON embeddings(embedding)
WITH (metric = 'cosine', m = 16, ef_construction = 64);
-- 插入向量数据
INSERT INTO embeddings (content, embedding)
VALUES ('Hello world', VECTOR_FROM_JSON('[0.1, 0.2, ...]'));
-- 向量相似性搜索
SELECT
content,
embedding,
VECTOR_DISTANCE(embedding, VECTOR_FROM_JSON('[0.15, 0.25, ...]')) as distance
FROM embeddings
ORDER BY embedding <=> VECTOR_FROM_JSON('[0.15, 0.25, ...]')
LIMIT 10;
5.2 HNSW索引实现预览
// hnsw_index.cpp - 层次化可导航小世界图索引
class HNSWIndex {
public:
struct Node {
uint32_t id;
std::vector<float> vector;
std::vector<std::vector<uint32_t>> neighbors; // 每层邻居
int max_layer;
};
// 插入向量
void insert(const std::vector<float>& vec, uint32_t id) {
int node_layer = random_layer();
Node new_node{id, vec, {}, node_layer};
new_node.neighbors.resize(node_layer + 1);
// 从入口点开始搜索
uint32_t entry = entry_point_;
// 自顶向下搜索最近邻居
for (int layer = max_layer_; layer > node_layer; --layer) {
entry = search_layer(vec, entry, 1, layer)[0];
}
// 在目标层插入
for (int layer = std::min(node_layer, max_layer_); layer >= 0; --layer) {
auto neighbors = search_layer(
vec, entry, m_ * 2, layer
);
new_node.neighbors[layer] = select_neighbors(
neighbors, m_, layer
);
// 连接双向边
for (uint32_t neighbor_id : new_node.neighbors[layer]) {
connect(neighbor_id, id, layer);
}
}
if (node_layer > max_layer_) {
max_layer_ = node_layer;
entry_point_ = id;
}
nodes_[id] = std::move(new_node);
}
// 相似性搜索
std::vector<uint32_t> search(
const std::vector<float>& query,
int k,
int ef_search = 50
) {
std::vector<uint32_t> result;
uint32_t entry = entry_point_;
// 自顶向下搜索
for (int layer = max_layer_; layer > 0; --layer) {
entry = search_layer(query, entry, 1, layer)[0];
}
// 底层搜索
result = search_layer(query, entry, ef_search, 0);
// 返回top-k
std::partial_sort(
result.begin(), result.begin() + k, result.end(),
[&](uint32_t a, uint32_t b) {
return distance(query, nodes_[a].vector) <
distance(query, nodes_[b].vector);
}
);
result.resize(k);
return result;
}
private:
float distance(
const std::vector<float>& a,
const std::vector<float>& b,
const std::string& metric = "cosine"
) {
if (metric == "cosine") {
float dot = 0, norm_a = 0, norm_b = 0;
for (size_t i = 0; i < a.size(); i++) {
dot += a[i] * b[i];
norm_a += a[i] * a[i];
norm_b += b[i] * b[i];
}
return 1 - dot / (sqrt(norm_a) * sqrt(norm_b));
} else if (metric == "l2") {
float sum = 0;
for (size_t i = 0; i < a.size(); i++) {
float diff = a[i] - b[i];
sum += diff * diff;
}
return sqrt(sum);
}
return 0;
}
std::unordered_map<uint32_t, Node> nodes_;
uint32_t entry_point_;
int max_layer_ = -1;
int m_ = 16; // 每层最大连接数
int ef_construction_ = 64;
};
5.3 RAG应用完整示例
-- 完整的RAG知识库实现
-- 1. 创建文档表
INSTALL EXTENSION 'vsql_vector';
INSTALL EXTENSION 'vsql_ai';
CREATE TABLE knowledge_docs (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255),
content TEXT,
embedding VECTOR(1536),
metadata JSON,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
-- 全文索引支持混合检索
FULLTEXT INDEX ft_content (content)
);
-- 2. 创建向量索引
CREATE VECTOR INDEX idx_embedding
ON knowledge_docs(embedding)
WITH (metric = 'cosine', m = 32, ef_construction = 128);
-- 3. 文档入库存储过程
DELIMITER $$
CREATE PROCEDURE insert_document(
IN p_title VARCHAR(255),
IN p_content TEXT,
IN p_metadata JSON
)
BEGIN
DECLARE v_embedding VECTOR(1536);
-- 调用AI生成嵌入
SET v_embedding = AI_EMBED(p_content, 'text-embedding-3-small');
INSERT INTO knowledge_docs (title, content, embedding, metadata)
VALUES (p_title, p_content, v_embedding, p_metadata);
END$$
DELIMITER ;
-- 4. 批量导入
CALL insert_document(
'MySQL Performance Tuning',
'MySQL性能调优的关键在于索引优化、查询重构和缓存策略...',
'{"category": "database", "tags": ["mysql", "performance"]}'
);
-- 5. 混合检索存储过程(向量 + 关键词)
DELIMITER $$
CREATE PROCEDURE hybrid_search(
IN p_query TEXT,
IN p_limit INT
)
BEGIN
DECLARE v_query_embedding VECTOR(1536);
-- 生成查询嵌入
SET v_query_embedding = AI_EMBED(p_query, 'text-embedding-3-small');
-- 混合检索:向量相似度 + 全文匹配
SELECT
id,
title,
content,
metadata,
-- 综合评分公式
(
0.7 * (1 - VECTOR_DISTANCE(embedding, v_query_embedding)) +
0.3 * MATCH(content) AGAINST(p_query IN NATURAL LANGUAGE MODE)
) as relevance_score
FROM knowledge_docs
WHERE
embedding <=> v_query_embedding < 0.3 -- 向量阈值
OR MATCH(content) AGAINST(p_query IN BOOLEAN MODE) > 0
ORDER BY relevance_score DESC
LIMIT p_limit;
END$$
DELIMITER ;
-- 6. 执行搜索
CALL hybrid_search('如何优化MySQL查询性能', 10);
-- 7. 带重排序的高级搜索
DELIMITER $$
CREATE PROCEDURE rag_search_with_rerank(
IN p_query TEXT,
IN p_top_k INT,
IN p_rerank_top INT
)
BEGIN
DECLARE v_query_embedding VECTOR(1536);
DECLARE v_candidates JSON;
SET v_query_embedding = AI_EMBED(p_query, 'text-embedding-3-small');
-- 第一阶段:向量召回
DROP TEMPORARY TABLE IF EXISTS tmp_candidates;
CREATE TEMPORARY TABLE tmp_candidates AS
SELECT
id,
title,
content,
metadata,
VECTOR_DISTANCE(embedding, v_query_embedding) as vec_distance
FROM knowledge_docs
ORDER BY embedding <=> v_query_embedding
LIMIT p_top_k;
-- 第二阶段:LLM重排序
SELECT
id,
title,
content,
metadata,
-- 使用LLM评分
AI_RERANK(
p_query,
JSON_ARRAYAGG(
JSON_OBJECT('id', id, 'content', content)
)
) as llm_score
FROM tmp_candidates
GROUP BY id, title, content, metadata
ORDER BY llm_score DESC
LIMIT p_rerank_top;
END$$
DELIMITER ;
六、性能优化与生产部署
6.1 扩展性能考量
自定义扩展的性能取决于实现质量。以下是一些优化策略:
// 1. 使用SIMD加速向量计算
#include <immintrin.h>
float cosine_similarity_simd(
const float* a,
const float* b,
size_t dim
) {
__m256 dot = _mm256_setzero_ps();
__m256 norm_a = _mm256_setzero_ps();
__m256 norm_b = _mm256_setzero_ps();
for (size_t i = 0; i < dim; i += 8) {
__m256 va = _mm256_loadu_ps(a + i);
__m256 vb = _mm256_loadu_ps(b + i);
dot = _mm256_add_ps(dot, _mm256_mul_ps(va, vb));
norm_a = _mm256_add_ps(norm_a, _mm256_mul_ps(va, va));
norm_b = _mm256_add_ps(norm_b, _mm256_mul_ps(vb, vb));
}
// 水平求和
float dot_result = horizontal_sum(dot);
float norm_a_result = horizontal_sum(norm_a);
float norm_b_result = horizontal_sum(norm_b);
return dot_result / (sqrtf(norm_a_result) * sqrtf(norm_b_result));
}
// 2. 内存池优化
class ExtensionMemoryPool {
public:
void* allocate(size_t size) {
std::lock_guard<std::mutex> lock(mutex_);
// 从预分配池中获取
for (auto& chunk : chunks_) {
if (chunk.available && chunk.size >= size) {
chunk.available = false;
return chunk.memory;
}
}
// 分配新块
void* mem = aligned_alloc(64, size);
chunks_.push_back({mem, size, false});
return mem;
}
void deallocate(void* ptr) {
std::lock_guard<std::mutex> lock(mutex_);
for (auto& chunk : chunks_) {
if (chunk.memory == ptr) {
chunk.available = true;
return;
}
}
}
private:
struct Chunk {
void* memory;
size_t size;
bool available;
};
std::vector<Chunk> chunks_;
std::mutex mutex_;
};
6.2 生产部署架构
┌─────────────────────────────────────────────────────────────┐
│ Application Layer │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ API GW │ │ Backend │ │ ML Service │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
└─────────┼────────────────┼────────────────┼────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────┐
│ VillageSQL Cluster │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Primary Node │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ vsql_uuid │ │ vsql_ai │ │ vsql_vector │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ └────────────────────────────────────────────────────────┘ │
│ │ Binlog Replication │
│ ▼ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Replica Nodes │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ Replica1 │ │ Replica2 │ │ Replica3 │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ │ │
│ └────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────────────┐
│ Extensions │ │ Vector Storage │
│ Directory │ │ (External) │
│ ┌───────────┐ │ │ ┌─────────────────────┐│
│ │vsql_uuid/ │ │ │ │ Milvus/Qdrant/ ││
│ │vsql_ai/ │ │ │ │ Weaviate ││
│ │vsql_vector│ │ │ └─────────────────────┘│
│ └───────────┘ │ └─────────────────────────┘
└─────────────────┘
6.3 配置优化
# villagesql.cnf
[mysqld]
# 扩展配置
extension_dir = /var/lib/villagesql/extensions
extension_max_loaded = 32
extension_load_on_demand = 1
# AI扩展配置
vsql_ai_endpoint = https://api.openai.com/v1
vsql_ai_default_model = gpt-4
vsql_ai_timeout_ms = 30000
vsql_ai_max_tokens = 4096
vsql_ai_cache_enabled = 1
vsql_ai_cache_ttl_seconds = 3600
# 向量扩展配置
vsql_vector_hnsw_m = 32
vsql_vector_hnsw_ef_construction = 128
vsql_vector_hnsw_ef_search = 64
vsql_vector_distance_metric = cosine
# 内存配置
extension_memory_pool_size = 256M
extension_stack_size = 8M
[extension_vsql_vector]
# 向量索引专用配置
index_threads = 4
index_batch_size = 1000
index_sync_interval = 60
七、与PostgreSQL扩展生态的对比
| 特性 | VillageSQL | PostgreSQL |
|---|---|---|
| 安装语法 | INSTALL EXTENSION | CREATE EXTENSION |
| 自定义类型 | ✓ | ✓ |
| 自定义函数 | ✓ | ✓ |
| 自定义索引 | 路线图中 | ✓ |
| 自定义操作符 | 规划中 | ✓ |
| 扩展仓库 | GitHub | PGXN |
| 版本管理 | 扩展描述文件 | control文件 |
| 依赖管理 | 支持 | 支持 |
| 热加载 | ✓ | ✓ |
八、局限性与注意事项
8.1 当前限制
- Alpha阶段:不建议生产使用
- 索引扩展:尚未支持(路线图中)
- 操作符扩展:尚未支持(规划中)
- Windows支持:仅Linux/macOS
- MySQL版本:需要MySQL 8.0+
8.2 性能考量
-- 扩展函数的性能开销
-- 建议:对频繁调用使用存储过程缓存
-- 不推荐:每次查询都调用AI函数
SELECT AI_CLASSIFY(content) FROM large_table;
-- 推荐:批量处理 + 结果缓存
CREATE TABLE classification_cache (
content_hash CHAR(64) PRIMARY KEY,
classification VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 缓存查询
SELECT
COALESCE(
cc.classification,
(
SELECT AI_CLASSIFY(lt.content)
FROM large_table lt
WHERE lt.id = large_table.id
)
) as classification
FROM large_table
LEFT JOIN classification_cache cc
ON cc.content_hash = SHA2(content, 256);
九、未来展望:MySQL的AI时代
VillageSQL代表了MySQL社区的一个重要转折点。它不仅解决了当下的功能缺口,更打开了一扇通往AI时代的大门。
9.1 路线图亮点
- 向量索引:原生HNSW/IVF索引
- 嵌入生成:本地嵌入模型支持
- Agent支持:数据库内AI Agent框架
- 流式处理:实时向量更新
9.2 生态预测
如果VillageSQL成功,我们可能看到:
- MySQL版pgvector:高性能向量搜索
- MySQL版pg_cron:内置任务调度
- MySQL版PostGIS:地理空间扩展
- MySQL版TimescaleDB:时序数据处理
十、总结
VillageSQL是一个大胆的实验,它试图回答一个关键问题:MySQL能否在AI时代保持其市场地位?
通过引入PostgreSQL风格的扩展框架,VillageSQL为MySQL社区提供了"无许可创新"的能力。开发者不再需要等待Oracle官方发布新功能,可以自主构建和分享扩展。
对于技术选型而言,VillageSQL提供了一个有趣的中间选项:
- 如果你需要MySQL的成熟稳定 + PostgreSQL的扩展能力
- 如果你已有大量MySQL资产但想拥抱AI
- 如果你想参与塑造MySQL的未来
VillageSQL仍处于Alpha阶段,但它代表的方向值得密切关注。毕竟,正如项目口号所说:It Takes a Village(众人拾柴火焰高)。
扩展信息:
- 官网:https://villagesql.com
- GitHub:https://github.com/villagesql/villagesql-server
- Discord:https://discord.gg/KSr6whd3Fr
相关阅读:
- PostgreSQL扩展开发指南
- MySQL插件API深入解析
- 向量数据库性能对比:pgvector vs Milvus vs Qdrant