大数跨境
0
0

比 SQLite 快数千倍? C# 内存数据库 MasterMemory 的真实性能

比 SQLite 快数千倍? C# 内存数据库 MasterMemory 的真实性能 DotNet技术匠
2025-11-26
1
导读:比SQLite快4700倍!C#开发者必知的内存数据库神器。

前言

作为一名 C# 开发,你是否在项目中频繁遇到这样的问题:需要反复读取配置、游戏数据或静态资源,但传统数据库(如 SQLite)因文件 I/O 和 ORM 开销导致性能瓶颈?尤其在高并发或移动端场景下,这种延迟和内存压力尤为明显。

今天要介绍的 MasterMemory,正是为这类“写少读多”场景量身打造的解决方案。它由知名开源团队 Cysharp(UniTask、MessagePack-CSharp 的作者)开发,通过将数据全量加载到内存并利用 Source Generator 自动生成强类型查询接口,在保证类型安全的同时实现极致性能。本文将带你从原理到实战,全面掌握这个技巧。

正文

传统方案的三大痛点

1、性能瓶颈

SQLite 等嵌入式数据库每次查询都涉及磁盘 I/O,在高频读取场景下成为系统瓶颈。

2、缺乏编译期类型安全

SQL 字符串无法在编译时校验,字段名拼错、表结构变更等问题只能在运行时暴露。

3、内存开销大

ORM 层 + 数据库引擎本身占用大量内存,对资源受限环境(如 Unity 游戏、IoT 设备)不友好。

MasterMemory 的核心优势

性能碾压:官方测试显示,其查询速度可达 SQLite 的 4700 倍,且每次查询 零 GC 分配

100% 类型安全:基于 Roslyn 的 Source Generator 在编译期生成强类型 API,杜绝运行时错误。

内存高效:自动字符串驻留、紧凑二进制格式,相同数据体积仅为 SQLite 的 6%(222KB vs 3560KB)。

跨平台支持:完美兼容 .NET 6+ 及 Unity(通过 IL2CPP)。

灵活查询:支持主键、复合索引、范围、最近值等多种查询方式。

安装与配置

# NuGet包管理器
Install-Package MasterMemory

# .NET CLI
dotnet add package MasterMemory

实战代码:从入门到精通

第一步:定义数据模型

public enum Gender
{
    Male, Female, Unknown
}

// 使用MemoryTable标记表定义
[MemoryTable("person"), MessagePackObject(true)]
public record Person
{
    // 主键定义
    [PrimaryKey]
    public required int PersonId { get; init; }
    
    // 多重索引定义
    [SecondaryKey(0), NonUnique]
    [SecondaryKey(1, keyOrder: 1), NonUnique]
    public required int Age { get; init; }
    
    [SecondaryKey(2), NonUnique]
    [SecondaryKey(1, keyOrder: 0), NonUnique]
    public required Gender Gender { get; init; }
    
    public required string Name { get; init; }
}

第二步:构建数据库

// 创建数据库构建器
var builder = new DatabaseBuilder();

// 添加测试数据
builder.Append(new Person[]{
    new() { PersonId = 0, Age = 13, Gender = Gender.Male, Name = "Dana Terry" },
    new() { PersonId = 1, Age = 17, Gender = Gender.Male, Name = "Kirk Obrien" },
    new() { PersonId = 2, Age = 31, Gender = Gender.Male, Name = "Wm Banks" },
    new() { PersonId = 3, Age = 44, Gender = Gender.Male, Name = "Karl Benson" },
    new() { PersonId = 4, Age = 23, Gender = Gender.Male, Name = "Jared Holland" },
    new() { PersonId = 5, Age = 27, Gender = Gender.Female, Name = "Jeanne Phelps" },
    // ... 更多数据
});

// 构建数据库二进制数据
byte[] databaseBinary = builder.Build();

// 也可以直接保存到文件
usingvar fileStream = File.Create("database.bin");
builder.WriteToStream(fileStream);

第三步:高效查询操作

// 创建数据库实例(建议单例模式)
var database = new MemoryDatabase(databaseBinary);

// 主键查询 - 最快速度
Person person = database.PersonTable.FindByPersonId(5);
Console.WriteLine($"Found: {person.Name}"); // "Jeanne Phelps"

// 复合索引查询 - 智能类型推断
RangeView<Person> femaleAge27 = database.PersonTable
    .FindByGenderAndAge((Gender.Female, 27));

// 范围查询 - 灵活筛选
RangeView<Person> ageRange = database.PersonTable
    .FindRangeByAge(2030); // 20-30岁之间

// 最近值查询 - 智能匹配
RangeView<Person> closestAge = database.PersonTable
    .FindClosestByAge(25); // 找到最接近25岁的记录

// 安全查询 - 避免异常
if (database.PersonTable.TryFindByPersonId(999out Person result))
{
    Console.WriteLine($"找到用户: {result.Name}");
}
else
{
    Console.WriteLine("用户不存在");
}

第四步:高级特性应用

动态数据更新

// 使用ImmutableBuilder进行数据更新
var builder = database.ToImmutableBuilder();

// 添加或替换数据(基于主键)
builder.Diff(new Person[]{
    new() { PersonId = 10, Age = 25, Gender = Gender.Female, Name = "New User" }
});

// 删除指定数据
builder.RemovePerson(new[] { 123 });

// 生成新的数据库实例
MemoryDatabase newDatabase = builder.Build();

数据验证机制

[MemoryTable("quest"), MessagePackObject(true)]
publicclassQuest : IValidatable<Quest>
{
    [PrimaryKey]
    publicint QuestId { get; init; }
    
    publicstring Name { get; init; }
    publicint RewardItemId { get; init; }
    publicint RequiredLevel { get; init; }
    
    void IValidatable<Quest>.Validate(IValidator<Quest> validator)
    {
        // 获取外部引用表
        var items = validator.GetReferenceSet<Item>();
        
        // 验证外键关系
        if (RewardItemId > 0)
        {
            items.Exists(x => x.RewardItemId, x => x.ItemId);
        }
        
        // 范围验证
        validator.Validate(x => x.RequiredLevel >= 1 && x.RequiredLevel <= 100);
        
        // 全局唯一性验证
        if (validator.CallOnce())
        {
            var quests = validator.GetTableSet();
            quests.Where(x => x.RewardItemId > 0)
                  .Unique(x => x.RewardItemId);
        }
    }
}

// 执行验证
var validateResult = database.Validate();
if (validateResult.IsValidationFailed)
{
    Console.WriteLine(validateResult.FormatFailedResults());
}

完整应用示例

namespace AppMasterMemory
{
    // 定义枚举
    publicenum Gender
    {
        Male,
        Female,
        Unknown
    }
    
    // 定义数据模型
    [MemoryTable("person"), MessagePackObject(true)]
    public record Person
    {
        // 主键定义
        [PrimaryKey]
        public required int PersonId { get; init; }
        // 多重索引定义
        [SecondaryKey(0), NonUnique]
        [SecondaryKey(1, keyOrder: 1), NonUnique]
        public required int Age { get; init; }
        [SecondaryKey(2), NonUnique]
        [SecondaryKey(1, keyOrder: 0), NonUnique]
        public required Gender Gender { get; init; }
        public required string Name { get; init; }
    }
    
    classProgram
    {
        static void Main(string[] args)
        {
            Console.OutputEncoding = System.Text.Encoding.UTF8;
            Console.WriteLine("🚀 MasterMemory 内存数据库示例");
            Console.WriteLine("================================\n");
            
            // 第一步:创建初始数据库
            var initialDatabase = CreateInitialDatabase();
            // 第二步:演示各种查询操作
            DemonstrateQueries(initialDatabase);
            // 第三步:演示数据更新操作
            var updatedDatabase = DemonstrateDataUpdates(initialDatabase);
            // 第四步:演示批量操作
            DemonstrateBatchOperations(updatedDatabase);
            
            Console.WriteLine("\n✅ 示例执行完成!");
            Console.ReadKey();
        }
        
        /// <summary>
        /// 创建初始数据库
        /// </summary>
        static MemoryDatabase CreateInitialDatabase()
        {
            Console.WriteLine("📦 创建初始数据库...");
            // 创建数据库构建器
            var builder = new DatabaseBuilder();
            // 添加测试数据
            builder.Append(new Person[]
            {
                new() { PersonId = 0, Age = 13, Gender = Gender.Male, Name = "Dana Terry" },
                new() { PersonId = 1, Age = 17, Gender = Gender.Male, Name = "Kirk Obrien" },
                new() { PersonId = 2, Age = 31, Gender = Gender.Male, Name = "Wm Banks" },
                new() { PersonId = 3, Age = 44, Gender = Gender.Male, Name = "Karl Benson" },
                new() { PersonId = 4, Age = 23, Gender = Gender.Male, Name = "Jared Holland" },
                new() { PersonId = 5, Age = 27, Gender = Gender.Female, Name = "Jeanne Phelps" },
                new() { PersonId = 6, Age = 25, Gender = Gender.Female, Name = "Alice Johnson" },
                new() { PersonId = 7, Age = 35, Gender = Gender.Male, Name = "Bob Smith" },
                new() { PersonId = 8, Age = 22, Gender = Gender.Female, Name = "Carol Wilson" },
                new() { PersonId = 9, Age = 28, Gender = Gender.Male, Name = "David Brown" }
            });
            // 构建数据库
            byte[] databaseBinary = builder.Build();
            var database = new MemoryDatabase(databaseBinary);
            Console.WriteLine($"✅ 数据库创建成功!共 {database.PersonTable.All.Count()} 条记录\n");
            return database;
        }
        
        /// <summary>
        /// 演示各种查询操作
        /// </summary>
        static void DemonstrateQueries(MemoryDatabase database)
        {
            Console.WriteLine("🔍 查询操作演示");
            Console.WriteLine("----------------");
            // 1. 主键查询 - 最快速度
            Console.WriteLine("1. 主键查询:");
            var person = database.PersonTable.FindByPersonId(5);
            Console.WriteLine($"   ID=5 的用户: {person.Name}, 年龄: {person.Age}, 性别: {person.Gender}");
            // 2. 复合索引查询
            Console.WriteLine("\n2. 复合索引查询(性别+年龄):");
            var femaleAge27 = database.PersonTable.FindByGenderAndAge((Gender.Female, 27));
            foreach (var p in femaleAge27)
            {
                Console.WriteLine($"   女性,27岁: {p.Name}");
            }
            // 3. 范围查询
            Console.WriteLine("\n3. 年龄范围查询(20-30岁):");
            var ageRange = database.PersonTable.FindRangeByAge(2030);
            foreach (var p in ageRange)
            {
                Console.WriteLine($"   {p.Name}{p.Age}岁");
            }
            // 4. 性别分组查询
            Console.WriteLine("\n4. 按性别查询:");
            var females = database.PersonTable.FindByGender(Gender.Female);
            Console.WriteLine($"   女性用户数量: {females.Count()}");
            foreach (var p in females)
            {
                Console.WriteLine($"   - {p.Name}{p.Age}岁");
            }
            // 5. 安全查询 - 避免异常
            Console.WriteLine("\n5. 安全查询(用户不存在的情况):");
            if (database.PersonTable.TryFindByPersonId(999out Person result))
            {
                Console.WriteLine($"   找到用户: {result.Name}");
            }
            else
            {
                Console.WriteLine("   用户ID=999不存在");
            }
            // 6. LINQ查询
            Console.WriteLine("\n6. LINQ复杂查询(年龄>25的男性,按年龄排序):");
            var complexQuery = database.PersonTable.All
                .Where(p => p.Gender == Gender.Male && p.Age > 25)
                .OrderBy(p => p.Age);
            foreach (var p in complexQuery)
            {
                Console.WriteLine($"   {p.Name}{p.Age}岁");
            }
            Console.WriteLine();
        }
        
        /// <summary>
        /// 演示数据更新操作
        /// </summary>
        static MemoryDatabase DemonstrateDataUpdates(MemoryDatabase database)
        {
            Console.WriteLine("🛠️ 数据更新操作演示");
            Console.WriteLine("--------------------");
            // 使用ImmutableBuilder进行数据更新
            var builder = database.ToImmutableBuilder();
            Console.WriteLine("1. 添加新用户:");
            // 添加或替换数据(基于主键)
            var newUsers = new Person[]
            {
                new() { PersonId = 10, Age = 25, Gender = Gender.Female, Name = "New User" },
                new() { PersonId = 11, Age = 30, Gender = Gender.Male, Name = "Another User" },
                new() { PersonId = 12, Age = 26, Gender = Gender.Female, Name = "Third User" }
            };
            builder.Diff(newUsers);
            foreach (var user in newUsers)
            {
                Console.WriteLine($"   添加: {user.Name}{user.Age}岁, {user.Gender}");
            }
            Console.WriteLine("\n2. 删除指定用户:");
            var idsToRemove = new[] { 123 };
            // 在删除前显示要删除的用户信息
            foreach (var id in idsToRemove)
            {
                if (database.PersonTable.TryFindByPersonId(id, outvar userToDelete))
                {
                    Console.WriteLine($"   删除: {userToDelete.Name} (ID={id})");
                }
            }
            builder.RemovePerson(idsToRemove);
            // 生成新的数据库实例
            MemoryDatabase newDatabase = builder.Build();
            Console.WriteLine($"\n✅ 更新完成!原数据库: {database.PersonTable.All.Count()} 条记录");
            Console.WriteLine($"   新数据库: {newDatabase.PersonTable.All.Count()} 条记录\n");
            return newDatabase;
        }
        
        /// <summary>
        /// 演示批量操作
        /// </summary>
        static void DemonstrateBatchOperations(MemoryDatabase database)
        {
            Console.WriteLine("📊 批量操作演示");
            Console.WriteLine("----------------");
            // 统计信息
            var allPersons = database.PersonTable.All.ToList();
            var totalCount = allPersons.Count;
            var maleCount = allPersons.Count(p => p.Gender == Gender.Male);
            var femaleCount = allPersons.Count(p => p.Gender == Gender.Female);
            var avgAge = allPersons.Average(p => p.Age);
            Console.WriteLine("1. 数据统计:");
            Console.WriteLine($"   总用户数: {totalCount}");
            Console.WriteLine($"   男性用户: {maleCount}");
            Console.WriteLine($"   女性用户: {femaleCount}");
            Console.WriteLine($"   平均年龄: {avgAge:F1}岁");
            // 年龄分组统计
            Console.WriteLine("\n2. 年龄分组统计:");
            var ageGroups = allPersons
                .GroupBy(p => p.Age / 10 * 10// 按十年分组
                .OrderBy(g => g.Key);
            foreach (vargroupin ageGroups)
            {
                Console.WriteLine($"   {group.Key}-{group.Key + 9}岁: {group.Count()}人");
            }
            // 姓名长度统计
            Console.WriteLine("\n3. 姓名长度分析:");
            var nameLengths = allPersons
                .GroupBy(p => p.Name.Length)
                .OrderBy(g => g.Key);
            foreach (vargroupin nameLengths)
            {
                Console.WriteLine($"   {group.Key}个字符: {group.Count()}人");
            }
            // 查找特定条件的用户
            Console.WriteLine("\n4. 条件查找:");
            var youngAdults = allPersons
                .Where(p => p.Age >= 20 && p.Age <= 30)
                .OrderBy(p => p.Age)
                .ToList();
            Console.WriteLine($"   20-30岁的用户 ({youngAdults.Count}人):");
            foreach (var person in youngAdults.Take(5)) // 只显示前5个
            {
                Console.WriteLine($"   - {person.Name}{person.Age}岁, {person.Gender}");
            }
            if (youngAdults.Count > 5)
            {
                Console.WriteLine($"   ... 还有 {youngAdults.Count - 5} 人");
            }
        }
    }
    
    /// <summary>
    /// 实际应用示例:用户管理服务
    /// </summary>
    publicclassUserManagementService
    {
        private MemoryDatabase _database;
        public UserManagementService(MemoryDatabase database)
        {
            _database = database;
        }
        
        /// <summary>
        /// 根据年龄范围获取用户
        /// </summary>
        public IEnumerable<Person> GetUsersByAgeRange(int minAge, int maxAge)
        {
            return _database.PersonTable.FindRangeByAge(minAge, maxAge);
        }
        
        /// <summary>
        /// 获取指定性别的用户
        /// </summary>
        public IEnumerable<Person> GetUsersByGender(Gender gender)
        {
            return _database.PersonTable.FindByGender(gender);
        }
        
        /// <summary>
        /// 搜索用户(按姓名模糊匹配)
        /// </summary>
        public IEnumerable<Person> SearchUsersByName(string namePattern)
        {
            return _database.PersonTable.All
                .Where(p => p.Name.Contains(namePattern, StringComparison.OrdinalIgnoreCase));
        }
        
        /// <summary>
        /// 获取用户统计信息
        /// </summary>
        public UserStatistics GetStatistics()
        {
            var allUsers = _database.PersonTable.All.ToList();
            returnnew UserStatistics
            {
                TotalUsers = allUsers.Count,
                MaleUsers = allUsers.Count(p => p.Gender == Gender.Male),
                FemaleUsers = allUsers.Count(p => p.Gender == Gender.Female),
                AverageAge = allUsers.Average(p => p.Age),
                MinAge = allUsers.Min(p => p.Age),
                MaxAge = allUsers.Max(p => p.Age)
            };
        }
        
        /// <summary>
        /// 批量更新数据库
        /// </summary>
        public MemoryDatabase UpdateDatabase(
            IEnumerable<Person> usersToAdd,
            IEnumerable<int> userIdsToRemove
)

        {
            var builder = _database.ToImmutableBuilder();
            if (usersToAdd?.Any() == true)
            {
                builder.Diff(usersToAdd.ToArray());
            }
            if (userIdsToRemove?.Any() == true)
            {
                builder.RemovePerson(userIdsToRemove.ToArray());
            }
            return builder.Build();
        }
    }
    
    /// <summary>
    /// 用户统计信息
    /// </summary>
    public record UserStatistics
    {
        publicint TotalUsers { get; init; }
        publicint MaleUsers { get; init; }
        publicint FemaleUsers { get; init; }
        publicdouble AverageAge { get; init; }
        publicint MinAge { get; init; }
        publicint MaxAge { get; init; }
    }
}

进阶技巧

并行构建new MemoryDatabase(bin, maxDegreeOfParallelism: Environment.ProcessorCount)

内存调优:禁用字符串驻留 internString: false

序列化优化:使用 [Key] 替代自动映射

代码裁剪:通过条件编译符号移除验证等非必要功能

总结

MasterMemory 通过将数据预编译为内存中的只读结构,并结合 Source Generator 自动生成类型安全的查询 API,彻底解决了传统嵌入式数据库在“读密集型”场景下的性能与维护难题。

它不是通用数据库的替代品,而是针对特定场景(配置、静态数据、游戏资源等)的精准优化工具。如果你的项目中有大量只读数据需要高频访问,MasterMemory 值得一试。

关键词

#MasterMemory#内存数据库、.NET 8、#Source Generator、类型安全、#高性能、多租户、AI Agent、#微服务、C#

最后
如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。也可以加入微信公众号[DotNet技术匠] 社区,与其他热爱技术的同行一起交流心得,共同成长!

作者:技术老小子

出处:mp.weixin.qq.com/s/xvuSfe5KTvTSLT4sT4djow
声明:网络内容,仅供学习,尊重版权,侵权速删,歉意致谢!



END



方便大家交流、资源共享和共同成长
纯技术交流群,需要加入的小伙伴请扫码,并备注加群



推荐阅读






一套工具搞定工业通讯全场景?让 PLC、Modbus、TCP 通信一目了然
基于 JSON 配置的 .NET 桌面应用自动更新方案
开源免费 WPF 控件库 — 高颜值、功能齐全、支持工业场景

基于 .NET 的可视化流程编辑工业视觉框架

WPF 轻量级插件框架:动态菜单、浮动窗口、热加载 DLL,开箱即用

WPF 智能仓储上位机系统,集成数据采集与轻量级 MES 功能

C# + FFmpeg 一键转码,轻松解决海康视频网页播放难题

C# 工业级全局键鼠行为监测与日志记录工具

开源 .NET 工作流引擎 + 可视化设计,轻松搞定 OA/CRM/ERP 开发
WinForm + STM32 打造稳定好用的工业设备远程升级工具
Visual Studio 2026 上手体验,AI 懂你、界面清爽、协作无缝
C# 打造轻量级上位机,高效打通 MES 与视觉检测系统

不用 GPU 也能跑的 WPF 视觉检测软件(Emgu CV + SQLite)

C# 工业级扫码难题破解,用微信实现精准扫码并自动填入任意应用

工业软件缺好 UI?这套 .NET 控件库从 IO 灯到圆角按钮全搞定

WPF 如何支撑一个灵活的流程图编辑器?

基于 WinForm GDI+ 的高性能矢量画布组件


觉得有收获?不妨分享让更多人受益

关注「DotNet技术匠」,共同提升技术实力


收藏
点赞
分享
在看

【声明】内容源于网络
0
0
DotNet技术匠
「DotNet技术匠」聚焦.NET核心,分享深度干货、实战技巧、最新资讯、优质资源,助你领跑技术赛道,赋能开发者成长。
内容 1715
粉丝 0
DotNet技术匠 「DotNet技术匠」聚焦.NET核心,分享深度干货、实战技巧、最新资讯、优质资源,助你领跑技术赛道,赋能开发者成长。
总阅读13
粉丝0
内容1.7k