CSDN 技术教程系列:文本与向量检索实战(.NET C# 体系)

系列主题:从内存到 Elasticsearch —— .NET C# 体系下的文本、向量检索技术演进与应用实例教程

目标读者:中高级 .NET 后端开发工程师、AI应用开发者、技术架构师

技术栈:.NET 8/9、C# 12、ONNX Runtime、BGE-M3、CLIP、Elasticsearch、Python Flask


📚 文章系列规划(共5篇)

序号

文章标题

核心技术

1

BGE-M3 多语言向量模型实战:.NET C# 从原理到落地

BGE-M3、ONNX Runtime、Tokenizer

2

内存向量检索引擎设计与实现:C# 轻量级 Milvus 替代方案

内存计算、读写锁、并行检索

3

Elasticsearch 语义搜索实战:.NET 向量+关键词混合检索

ES 8.x、Dense Vector、Hybrid Search

4

CLIP 多模态搜索实战:.NET + Python 跨语言图片检索

OpenCLIP、Python Flask、跨模态

5

从内存到 ES:.NET 企业级向量检索架构演进之路

架构设计、性能优化、容灾策略


文章5:从内存到 ES:.NET 企业级向量检索架构演进之路

📝 文章信息

  • 分类:系统架构 / 技术演进 / 企业级应用 / .NET

  • 标签架构设计, 向量检索, 性能优化, 容灾, .NET, C#

📖 章节大纲

1. 引言:架构演进的驱动力
  • 业务增长带来的挑战

  • 技术选型的权衡

2. 阶段一:内存向量检索(C# 实现)
┌─────────────┐     ┌──────────────────┐     ┌─────────────┐
│   Client    │────▶│  .NET API (C#)   │────▶│   SQL DB    │
└─────────────┘     │                  │     │  (主数据)    │
                    │  ┌────────────┐  │     └─────────────┘
                    │  │ C# 内存向量 │  │
                    │  │ 存储实现    │  │
                    │  │ (1024-dim) │  │
                    │  └────────────┘  │
                    └──────────────────┘
3. 阶段二:多模型并存(.NET + Python)
┌─────────────┐     ┌──────────────────────────────────────────┐
│   Client    │────▶│              .NET API (C#)                │
└─────────────┘     │                                          │
                    │  ┌─────────────┐    ┌─────────────────┐  │
                    │  │ C# BGE-M3   │    │ C# CLIP 向量库   │  │
                    │  │ 内存向量库   │    │ (512-dim)       │  │
                    │  │ (1024-dim)  │    │                 │  │
                    │  └─────────────┘    └─────────────────┘  │
                    │                                          │
                    │  ┌─────────────────────────────────────┐ │
                    │  │        C# 查询策略路由               │ │
                    │  └─────────────────────────────────────┘ │
                    └──────────────────────────────────────────┘
                                        │
                    ┌───────────────────┼───────────────────┐
                    ▼                   ▼                   ▼
              ┌─────────┐        ┌──────────┐        ┌──────────┐
              │ SQL DB  │        │ Python   │        │Elasticsearch│
              │(主数据)  │        │ CLIP服务 │        │ (引入)   │
              └─────────┘        └──────────┘        └──────────┘
4. 阶段三:Elasticsearch 中心化
┌─────────────┐     ┌──────────────────────────────────────────┐
│   Client    │────▶│              .NET API (C#)                │
└─────────────┘     │                                          │
                    │  ┌─────────────────────────────────────┐ │
                    │  │      C# 统一查询接口(优先ES)        │ │
                    │  └─────────────────────────────────────┘ │
                    └──────────────────────────────────────────┘
                                        │
                                        ▼
                    ┌──────────────────────────────────────────┐
                    │       Elasticsearch Cluster              │
                    │  ┌──────────────┐    ┌──────────────┐   │
                    │  │ Dense Vector │    │   Keyword    │   │
                    │  │   (向量检索)  │    │   (关键词)    │   │
                    │  └──────────────┘    └──────────────┘   │
                    │                                          │
                    │  ┌─────────────────────────────────────┐ │
                    │  │        Hybrid Search                │ │
                    │  │    向量相似度 + 关键词匹配           │ │
                    │  └─────────────────────────────────────┘ │
                    └──────────────────────────────────────────┘
//核心代码
                var esDoc = new ProductESDocument
                {
                   
                };

                // 4. 写入ES
                var response = await client.IndexAsync(esDoc, i => i.Index(ProductsIndexName));

                if (response.IsValidResponse)
                {
                    await _db.Updateable<CollectData>()
                        .SetColumns(p => p.EsSyncStatus == 1)
                        .Where(p => p.Id == collectDataId)
                        .ExecuteCommandAsync();
                  
                    return true;
                }
                else
                {
                    await _db.Updateable<CollectData>()
                        .SetColumns(p => p.EsSyncStatus == 2)
                        .Where(p => p.Id == collectDataId)
                        .ExecuteCommandAsync();
          
                    return false;
                }
var searchResponse = await client.SearchAsync<>(s => s
                    .Indices(ProductsIndexName)
                    .Query(q => q
                        .MultiMatch(mm => mm
                            .Fields(new[] { "title^3", "semanticKeywords^2", "description", "brandName" })
                            .Query(request.Query)
                            .Fuzziness(new Fuzziness("AUTO"))
                        )
                    )
                    .Sort(srt => srt.Score(new ScoreSort { Order = SortOrder.Desc }))
                    .From((request.Page - 1) * request.PageSize)
                    .Size(request.PageSize)
                    .SourceExcludes(new[] { "productVector", "imageVector" }) // 排除向量字段,减少返回数据量
                    .Explain() /
                );

                if (!searchResponse.IsValidResponse)
                {
                    _logger.LogError("智能搜索失败: {Error}", searchResponse.ElasticsearchServerError?.Error?.Reason);
                    return new ***
                    {
                        Items = new 
                        Total = 0,
                        Page = request.Page,
                        PageSize = request.PageSize
                    };
                }
5. 容灾与降级策略(C#)
public class ResilientSearchService
{
    private readonly HybridSearchService _esService;
    private readonly InMemoryVectorStore<88> _memoryStore;
    private readonly ILogger<99> _logger;

    public async Task<List<SearchResult>> SearchWithFallbackAsync(string query, int topK)
    {
        try
        {
            // 优先 ES
            return await _esService.SearchAsync(query, topK);
        }
        catch (Exception ex)
        {
            _logger.LogWarning(ex, "ES 查询失败,降级到内存检索");
            
            try
            {
                // 降级到内存检索
                var queryVector = BgeM3EmbeddingGenerator.GenerateEmbedding(query);
                return await _memoryStore.SearchSimilarAsync(queryVector, topK);
            }
            catch (Exception ex2)
            {
                _logger.LogError(ex2, "内存检索也失败,返回空结果");
                return new List<SearchResult>();
            }
        }
    }
}
6. 分层存储策略

层级

存储介质

数据范围

延迟

用途

L1

C# 本地内存

热点 1万条

1ms

实时推荐

L2

Redis

热点 10万条

5ms

缓存加速

L3

Elasticsearch

全量数据

50ms

完整检索

L4

SQL DB

全量数据

100ms

数据持久化

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐