ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

MMKV 全平台接入与原理详解:基于 mmap 的高性能 key-value 存储框架

MMKV 全平台接入与原理详解:基于 mmap 的高性能 key-value 存储框架 KV存储缓存移动开发存储【免费下载链接】MMKVAn efficient, small mobile key-value storage framework developed by WeChat. Works on Android, iOS, macOS, Windows, POSIX, and OHOS.项目地址https://gitcode.com/gh_mirrors/mm/MMKV点击查看免费下载MMKV 是微信团队开源的高性能通用 key-value 组件基于 mmap 内存映射文件实现底层序列化/反序列化采用 protobuf 协议自 2015 年中期起在微信客户端长期服役其性能与稳定性经过了大规模生产环境的验证。本文以仓库中的 README_CN.md 为主线完整讲解 MMKV 的设计原理并逐一给出 Android、iOS/macOS、Windows、POSIX、HarmonyOS NEXT 与 Kotlin Multiplatform 的安装接入、快速上手与 API 用法同时结合 Core 与 Android/MMKV 源码剖析其底层实现帮助你快速在自己的多端项目中落地这套存储方案。MMKV 源起为什么微信要自研 key-value 组件在微信客户端的日常运营中时不时会爆发特殊文字引起系统 crash 的问题。当时的技术方案是在关键代码前后进行计数器的加减通过检查计数器的异常来定位引发闪退的异常文字。该方案对存储组件提出了两个苛刻要求不能影响滑动性能在会话列表、会话界面等有大量 cell 的场景新加的计时器读写必须足够轻量必须永久存储闪退随时可能发生计数器需要持久化且以实时写入为主。团队考察了 SharedPreferences、NSUserDefaults、SQLite 等常见组件均无法满足要求。最终转向 mmap 内存映射文件——它天然满足实时写入、crash 不丢数据的核心诉求由此诞生了 MMKV。这一背景也解释了 MMKV 设计中写入优先、增量更新的技术取向。MMKV 原理四步设计骨架内存准备mmap 内存映射通过 mmap 将文件映射到一段进程地址空间App 只管往这段内存里写数据由操作系统负责将内存回写write back到文件因此即使进程 crash数据也不会丢失。在 Core/MemoryFile.cpp 中可以看到具体实现m_ptr (char *) ::mmap(m_ptr, m_size, mode, MAP_SHARED, m_diskFile.m_fd, 0);写入后通过msync控制同步时机MemoryFile::msyncauto ret ::msync(m_ptr, m_size, syncFlag ? MS_SYNC : MS_ASYNC);同步标志区分MS_SYNC同步刷盘与MS_ASYNC异步刷盘这正是 MMKVsync()/async()能力的底层来源。文件空间不足时MemoryFile 会先ftruncate扩展文件大小再重新mmap映射到新地址见 MemoryFile.cpp。数据组织protobuf 序列化数据序列化选用 protobuf 协议pb 在性能和空间占用上都有不错的表现。MMKV 并未直接依赖外部 protobuf 库而是在 Core/MiniPBCoder.cpp 中实现了一套精简的自研编码器通过 PBEncodeItem.hpp 描述待编码条目支持 Data、Container、Int32/UInt32、Int64/UInt64、String 等类型。编码时逐个条目写入CodedOutputData解码时由CodedInputData按 varint 规则读取对应 CodedInputData.cpp 与 CodedOutputData.cpp。写入优化append 增量更新MMKV 的主要使用场景是频繁的写入更新因此需要增量更新能力将增量 kv 对象序列化后append 到内存末尾而不是像传统方案那样整体重写。在 Core/MMKV_IO.cpp 的ensureMemorySize()与expandAndWriteBack()中可以看到完整链路——当已有空间不足以容纳新增数据时会触发文件扩展并把整份字典重新写回write back以保证数据连续可读。空间增长性能与空间的折中append 模式持续写入会导致文件无限膨胀所以 MMKV 在空间上做了折中处理。从 MMKV_IO.cpp 的扩展逻辑可以看出默认策略if (lenNeeded fileSize || (needSync (lenNeeded futureUsage) fileSize)) { size_t oldSize fileSize; fileSize * 2; // 成倍扩展 ... }同时提供trim()方法在适当时候收缩文件、clearAllWithKeepingSpace()保留已分配空间的快速清空详见下文 API 一节。数据可靠性CRC 校验与恢复策略文件加载时 MMKV 会做完整性校验。从 MMKV_IO.cpp 的checkDataValid()可以看到通过记录在 meta 文件中的 CRC32 摘要与实际文件内容的 CRC 比对来判断数据是否损坏损坏时依据MMKVRecoverStrategicOnErrorDiscard/OnErrorRecover决定丢弃数据还是尽量恢复这部分能力通过 MMKVHandler.h 暴露给上层回调。Kotlin Multiplatform 指南实验性v2.4.2 的 Kotlin Multiplatform 包为实验性功能目前支持 Android 与 iOS后续版本可能调整 API 或产物结构请以最新发布说明为准。在共享模块中引入kotlin { sourceSets { commonMain.dependencies { implementation(com.tencent:mmkv-kmp:2.4.2) } } }支持 Android、iosArm64、iosSimulatorArm64和iosX64四个目标平台Android 侧要求工程开启 AndroidXgradle.properties中设置android.useAndroidXtrueiOS 的 deployment target 为 13.0。初始化与打包说明请参考 KMP/README.mdAndroid 端在Application.onCreate()中调用MMKV.initialize(this)iOS 端直接调用MMKV.initialize()基础用法val kv MMKV.defaultMMKV()后kv.encodeString(name, MMKV)、kv.decodeString(name)。该包在 Android 上委托给原生 AARcom.tencent:mmkv:2.4.2在 iOS 上通过 C bridge 把 MMKV Core 嵌入发布的 native KLIB因此 iOS 消费者无需再集成 CocoaPods 或 Swift Package Manager 版本也不应在同一 iOS 二进制中同时链接原生 MMKV CocoaPod/SwiftPM 产物否则会产生重复的原生符号。从源码构建需 macOS、Xcode 命令行工具、CMake 与 JDK 11cd KMP ./gradlew :mmkv:assemble -PMMKV_USE_MAVEN_LOCALtrueAndroid 指南安装引入推荐使用 Mavendependencies { implementation com.tencent:mmkv:2.4.2 // replace 2.4.2 with any available version }从 v2.0.0 起MMKV去掉了 32-bit 架构的支持且不再支持 API level 22 及以下如有这类需求请使用 v1.3.x LTS 版本。工程级配置可参考 Android/MMKV/mmkv/gradle.properties 与模块的 build_library.gradle。快速上手MMKV 使用非常简单所有变更立马生效无需调用sync、apply。在 App 启动时初始化 MMKV设定 MMKV 的根目录默认files/mmkv/。从 MMKV.java 源码可见initialize()默认根目录即${filesDir}/mmkvpublic void onCreate() { super.onCreate(); String rootDir MMKV.initialize(this); System.out.println(mmkv root: rootDir); //…… }initialize有多个重载可自定义根目录、日志级别MMKVLogLevel、第三方 so 加载器如 ReLinker以及恢复回调MMKVHandler。MMKV 提供一个全局实例可直接使用import com.tencent.mmkv.MMKV; //…… MMKV kv MMKV.defaultMMKV(); kv.encode(bool, true); boolean bValue kv.decodeBool(bool); kv.encode(int, Integer.MIN_VALUE); int iValue kv.decodeInt(int); kv.encode(string, Hello from mmkv); String str kv.decodeString(string);Android API 全景结合 MMKV.java 源码Android 端提供的能力远不止文档示例多实例MMKV.mmkvWithID(String mmapID)可创建/复用指定 ID 的实例还支持带MMKVConfig、mode单/多进程、cryptKeyAES 加密密钥可选 aes256、rootPath、expectedCapacity预分配空间等重载数据类型bool/int/long/float/double/String/SetString/byte[]bytes均有对应encode/decodeXxx方法解码时均可传入默认值部分接口支持过期时间参数expireDurationInSecond见 MMKV.java日常操作containsKey()、removeValueForKey()、clearAll()、更快的clearAllWithKeepingSpace()、trim()收缩文件、close()关闭实例刷盘控制sync()同步写回、async()异步写回默认策略是写入即可用仅在非常担心掉电与数据损坏的场景才建议显式调用对应源码注释见 MMKV.java。MMKV 支持多进程访问多进程模式下通过文件锁与序列号机制保证跨进程一致性相关底层实现见 Core/InterProcessLock.cpp 与 Core/InterProcessLock_Android.cpp。iOS/macOS 指南安装引入CocoaPods安装 CocoaPods打开命令行cd到项目工程目录执行pod repo update让 CocoaPods 感知最新的 MMKV 版本打开 Podfile添加pod MMKV到你的 app target命令行执行pod install用 Xcode 打开 CocoaPods 自动生成的.xcworkspace文件添加头文件#import MMKV/MMKV.h即可使用。对应 Podspec 见仓库根目录 MMKV.podspec另有 App Extension、Watch Extension 与纯 Core 版本MMKVAppExtension.podspec、MMKVWatchExtension.podspec、MMKVCore.podspec。也可以使用 Swift Package Manager见 Package.swift。快速上手无需任何配置所有变更立马生效无需调用synchronize。在 App 启动时初始化 MMKV例如在-[MyApp application: didFinishLaunchingWithOptions:]里- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // init MMKV in the main thread [MMKV initializeMMKV:nil]; //... return YES; }MMKV 提供一个全局实例可直接使用MMKV *mmkv [MMKV defaultMMKV]; [mmkv setBool:YES forKey:bool]; BOOL bValue [mmkv getBoolForKey:bool]; [mmkv setInt32:-1024 forKey:int32]; int32_t iValue [mmkv getInt32ForKey:int32]; [mmkv setString:hello, mmkv forKey:string]; NSString *str [mmkv getStringForKey:string];iOS/macOS 端同样支持多进程访问与cryptKey加密平台相关适配见 Core/MMKV_OSX.cpp、Core/MemoryFile_OSX.cpp 与 iOS/MMKV 工程。Swift 侧的用法可参考 demo 中的 DemoSwiftUsage.swift。Windows 指南安装引入子工程方式获取 MMKV 源码git clone https://github.com/Tencent/MMKV.git添加工程Core/core.vcxproj到你的项目设置主工程依赖于MMKV工程添加目录$(OutDir)include到主工程的C/C-常规-附加包含目录添加目录$(OutDir)到主工程的链接器-常规-附加库目录添加mmkv.lib到主工程的链接器-输入-附加依赖项添加头文件#include MMKV/MMKV.h即可使用。注意MMKV 默认使用MT/MTd运行时库编译如果主工程配置不一致请修改 MMKV 配置后重新编译MMKV 使用 Visual Studio 2017 开发使用其他版本 Visual Studio 时请把 MMKV 的工具集与主工程调成一致后再编译。快速上手所有变更立马生效无需调用save、sync。在main()中初始化#include MMKV/MMKV.h int main() { std::wstring rootDir getYourAppDocumentDir(); MMKV::initializeMMKV(rootDir); //... }全局实例用法auto mmkv MMKV::defaultMMKV(); mmkv-set(true, bool); std::cout bool mmkv-getBool(bool) std::endl; mmkv-set(1024, int32); std::cout int32 mmkv-getInt32(int32) std::endl; mmkv-set(Hello, MMKV for Windows, string); std::string result; mmkv-getString(string, result); std::cout string result std::endl;Windows 平台适配见 Core/MemoryFile_Win32.cpp、Core/InterProcessLock_Win32.cpp 与 Core/ThreadLock_Win32.cpp完整工程示例可参考 Win32/Win32Demo 与 Win32/Win32DemoProcess。POSIX 指南安装引入CMake获取 MMKV 源码git clone https://github.com/Tencent/MMKV.git打开项目CMakeLists.txt添加add_subdirectory(mmkv/POSIX/src mmkv) target_link_libraries(MyApp mmkv)添加头文件#include MMKV.h即可使用。快速上手所有变更立马生效无需调用save、sync。在main()中初始化#include MMKV.h int main() { std::string rootDir getYourAppDocumentDir(); MMKV::initializeMMKV(rootDir); //... }全局实例用法auto mmkv MMKV::defaultMMKV(); mmkv-set(true, bool); std::cout bool mmkv-getBool(bool) std::endl; mmkv-set(1024, int32); std::cout int32 mmkv-getInt32(int32) std::endl; mmkv-set(Hello, MMKV for Windows, string); std::string result; mmkv-getString(string, result); std::cout string result std::endl;POSIX 版本面向各类类 Unix 平台Linux 上的内存映射实现在 Core/MemoryFile_Linux.cppPOSIX 侧编译入口与示例可参考 POSIX/src/CMakeLists.txt 与 POSIX/demo含 UnitTest.cpp、TestInterProcessLock.cpp 等测试样例。POSIX 目录还附带 golang 绑定与 Python 绑定方便服务端/桌面场景集成。HarmonyOS NEXT 指南安装引入OHPMohpm install tencent/mmkvHarmonyOS 侧工程与示例代码位于 OpenHarmony 目录ets 层的类型声明与工具类见 OpenHarmony/MMKV/src/main/ets如 MMKV.etsnative 桥接在 OpenHarmony/MMKV/src/main/cpp。快速上手所有变更立马生效无需调用save、sync。在EntryAbility.onCreate()中初始化import { MMKV } from tencent/mmkv; export default class EntryAbility extends UIAbility { onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { let appCtx this.context.getApplicationContext(); let mmkvRootDir MMKV.initialize(appCtx); console.info(mmkv rootDir: , mmkvRootDir); …… }全局实例用法import { MMKV } from tencent/mmkv; let mmkv MMKV.defaultMMKV(); mmkv.encodeBool(bool, true); console.info(bool , mmkv.decodeBool(bool)); mmkv.encodeInt32(int32, Math.pow(2, 31) - 1); console.info(max int32 , mmkv.decodeInt32(int32)); mmkv.encodeInt64(int, BigInt(2**63) - BigInt(1)); console.info(max int64 , mmkv.decodeInt64(int)); let str: string Hello OpenHarmony from MMKV; mmkv.encodeString(string, str); console.info(string , mmkv.decodeString(string)); let arrayBuffer: ArrayBuffer StringToArrayBuffer(Hello OpenHarmony from MMKV with bytes); mmkv.encodeBytes(bytes, arrayBuffer); let bytes mmkv.decodeBytes(bytes); console.info(bytes , ArrayBufferToString(bytes));注意 HarmonyOS API 的命名风格与 Java/Objective-C 略有不同读取方法为decodeXxx前缀、写入方法为encodeXxx前缀且整型读写按Int32/Int64分开提供字节类型通过ArrayBuffer承载。更多参考与仓库资源LicenseMMKV 以 BSD 3-Clause 协议开源详见 LICENSE.TXT版本历史各版本变更记录见 CHANGELOG.md参与贡献参见 CONTRIBUTING.mdMMKV 采用了 Contributor Covenant 定义的行为准则详见 CODE_OF_CONDUCT.md核心实现跨平台核心在 Coremmap 映射 MemoryFile.cpp、写入链路 MMKV_IO.cpp、pb 编解码 MiniPBCoder.cppAndroid 封装在 Android/MMKV/mmkv/src/main/java/com/tencent/mmkvKMP 实验性支持见 KMP隐私MMKV 不收集、获取或上传任何个人信息详见《MMKV SDK 个人信息保护规则》官方说明。综上MMKV 以mmap protobuf append 增量更新三个核心技术点支撑起一套跨 Android、iOS、macOS、Windows、POSIX、HarmonyOS NEXT 与 Kotlin Multiplatform 的统一 key-value 存储方案写入实时生效、无需显式同步天然支持多进程访问并针对文件膨胀、CRC 损坏等长期运行问题提供了完整的恢复与收缩机制。你可以直接参照本文各平台的分步指南接入并结合文中所列的源码路径进一步研究其内部实现。赞分享KV存储缓存移动开发存储【免费下载链接】MMKVAn efficient, small mobile key-value storage framework developed by WeChat. Works on Android, iOS, macOS, Windows, POSIX, and OHOS.项目地址https://gitcode.com/gh_mirrors/mm/MMKV点击查看免费下载相关推荐pgai 扩展开发指南从 Docker 开发环境到 Feature Flag 与版本发布的完整工作流pgai 扩展开发指南从 Docker 开发环境到 Feature Flag 与版本发布的完整工作流 本文基于 pgai 仓库 projects/extensKV存储缓存移动开发存储Sled轻量级、高性能的 Rust key-value 存储库Sled轻量级、高性能的 Rust key value 存储库 项目介绍 Sled 是一个用 Rust 编写的键值存储库其设计目标是提供高速度和低延迟的数据mmap-go 内存映射实战Go 可移植 mmap 库的 API 详解与平台实现原理mmap go 内存映射实战Go 可移植 mmap 库的 API 详解与平台实现原理 导读 mmap go 是一个为 Go 语言设计的可移植内存映射memo后端云原生容器编排微服务上一篇EmDash 插件自定义 Portable Text 块类型从编辑器声明到站点渲染的完整实现指南下一篇超强并发队列指南oneTBB线程安全数据传输实战创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表