Skip to content

python_setup_cn

guoling edited this page Apr 19, 2024 · 8 revisions

MMKV for Python (on POSIX)

MMKV 是基于 mmap 内存映射的 key-value 组件,底层序列化/反序列化使用 protobuf 实现,性能高,稳定性强。从 2015 年中至今在微信上使用,其性能和稳定性经过了时间的验证。现已移植到 Android / macOS / Windows / Python (on POSIX) / Golang (on POSIX) 平台,一并开源。

安装指南

基本要求

  • Python 2.7 或以上版本;
  • CMake 3.8.0 或以上版本;
  • C++ 编译器需支持 C++ 17 标准;
  • MMKV 支持 Linux(Ubuntu, Arch Linux, CentOS, Gentoo)、Unix(macOS, FreeBSD, OpenBSD) 等 POSIX 平台。

通过源码编译安装

  1. 从 GitHub 拉取源码:
git clone https://github.com/Tencent/MMKV.git
  1. 解决 pybind11 相关依赖。
    2.1 拉取 pybind11 (v2.5.0) 源码:
cd MMKV
git submodule update --init --recursive

2.2 如果是 Linux,需要安装 python-dev (for Python 2.x),或 python3-dev (for Pythond 3.x)。如果是 macOS,则无需安装。具体参见 pybind11 docs
例如,假设我们在 Ubuntu 上使用 Python 3.x:

sudo apt-get install python3-dev
  1. 编译并安装 MMKV for Python 原生库 (安装时可能需要 root 权限)。假设我们用的是 Python 3.x:
cd POSIX/Python
python3 setup.py install
# or if you're not root
sudo python3 setup.py install

这就全部 OK 了。

  • 注: 如果你不想安装 MMKV 到系统,你可以只编译原生库:
cd POSIX/Python
mkdir build
cd build
cmake ..
make -j8

留意 cmake 的输出,确认用了哪个 Python 来编译 MMKV。日志大概是这样:

-- Found PythonInterp: /usr/bin/python3.6 (found version "3.6.9") .

如果发现不是你想用的版本 (例如你要用 Python 2.7),你可以给 cmake 设置参数 PYBIND11_PYTHON_VERSION:

cmake -DPYBIND11_PYTHON_VERSION=2.7 ..

编译完成后,将生成的 mmkv.so (具体名字不同操作系统不同设备不一样,例如可能叫 mmkv.cpython-36m-x86_64-linux-gnu.so) 拷到 Python 的库目录 PYTHONPATH。如果你用 Python 3,也可以直接拷到脚本目录下。

  1. 测试 MMKV:
# cd 到 POSIX/Python
python3 demo.py
python3 unit_test.py

使用指南

MMKV 的使用非常简单,所有变更立马生效,无需调用 savesync

配置 MMKV 根目录

  • 在程序启动时初始化 MMKV,设定 MMKV 的根目录,例如在 main() 函数里:

    import mmkv
    
    if __name__ == '__main__':
        mmkv.MMKV.initializeMMKV('/tmp/mmkv')

CRUD 操作

  • MMKV 提供一个全局的实例,可以直接使用:

    kv = mmkv.MMKV.defaultMMKV()
    
    kv.set(True, 'bool')
    print('bool = ', kv.getBool('bool'))
    
    kv.set(-1 * (2 ** 31), 'int32')
    print('int32 = ', kv.getInt('int32'))
    
    kv.set((2 ** 32) - 1, 'uint32')
    print('uint32 = ', kv.getUInt('uint32'))
    
    kv.set(2 ** 63, 'int64')
    print('int64 = ', kv.getLongInt('int64'))
    
    kv.set((2 ** 64) - 1, 'uint64')
    print('uint64 = ', kv.getLongUInt('uint64'))
    
    kv.set(3.1415926, 'float')
    print('float = ', kv.getFloat('float'))
    
    kv.set('Hello world, MMKV for Python!', 'string')
    print('string = ', kv.getString('string'))
    
    lst = range(0, 10)
    kv.set(bytes(lst), 'bytes')
    bt = kv.getBytes('bytes')
    print('raw bytes = ', bt, ', decoded bytes = ', list(bt))

    可以看到,MMKV 在使用上还是比较简单的。

  • 删除 & 查询

    kv = mmkv.MMKV.defaultMMKV()
    print('keys before remove:', sorted(kv.keys()))
    
    kv.remove('bool')
    print('"bool" exist after remove: ', ('bool' in kv))
    
    kv.remove(['int32', 'float'])
    print('keys after remove:', sorted(kv.keys()))
  • 如果不同业务需要区别存储,也可以单独创建自己的实例:

    kv = mmkv.MMKV('test_python')
    kv.set(True, 'bool')
  • 如果业务需要多进程访问,那么在初始化的时候加上标志位 MMKV.MULTI_PROCESS_MODE

    kv = mmkv.MMKV('test_python', mmkv.MMKVMode.MultiProcess)
    kv.set(True, 'bool')

支持的数据类型

  • 支持以下基础类型:

    • Boolean, int, float

    注意: 由于 protobuf 的具体实现限制,整数类型按 int32, uint32, int64, uint64 划分,有不同的序列化/反序列化实现。所以在 get 的时候,需要按照 value 的可能范围,调用不同的 getXXXInt() 函数。具体每个整数类型的取值范围,可以参考 MMKV/POSIX/Python/unit_test.py。set 的时候无需区分,MMKV 会自动选择最小的类型进行存储。

  • 支持以下类:

    • str, bytes

日志

  • MMKV 默认不打印任何日志,你可以在初始化的时候打开日志开关:

    mmkv.MMKV.initializeMMKV('/tmp/mmkv', mmkv.MMKVLogLevel.Info)
  • MMKV 会将日志输出到 stdout,对于解析器执行、现网问题跟进都不方便。你可以在程序启动时重定向 MMKV 的日志。实现 (logLevel: mmkv.MMKVLogLevel, file: str, line: int, function: str, message: str) -> None 类型的回调函数,注册为日志 handler. 然后记得在程序退出之前反注册日志 handler (或调用 MMKV.onExit()),否则程序可能无法正常退出。

def logger(log_level, file, line, function, message):
    level = {
        mmkv.MMKVLogLevel.NoLog : 'N',
        mmkv.MMKVLogLevel.Debug : 'D',
        mmkv.MMKVLogLevel.Info : 'I',
        mmkv.MMKVLogLevel.Warning : 'W',
        mmkv.MMKVLogLevel.Error : 'E'
    }
    # use your logging tools instead of print()
    print('[{0}] <{1}:{2}:{3}> {4}'.format(level[log_level], file, line, function, message))


if __name__ == '__main__':
    # enable logging & redirect logging
    mmkv.MMKV.initializeMMKV('/tmp/mmkv', mmkv.MMKVLogLevel.Info, logger)
    
    # some logic
    ...

    # unregister before exit, otherwise the script won't exit properly
    mmkv.MMKV.unRegisterLogHandler()

    # or just call onExit() will do the job
    # mmkv.MMKV.onExit()

备份 & 恢复

  • MMKV 提供了备份和恢复接口,可用于备份数据到其他目录,并稍后恢复原有数据。

      root_dir = "/tmp/mmkv_backup"
      // backup one instance
      ret = mmkv.MMKV.backupOneToDirectory(mmap_id, root_dir)
      // backup all instances
      count = mmkv.MMKV.backupAllToDirectory(root_dir)
    
      // restore one instance
      ret = mmkv.MMKV.restoreOneFromDirectory(mmap_id, root_dir)
      // restore all instances
      count = mmkv.MMKV.restoreAllFromDirectory(root_dir)

自动过期

  • v1.3.0 起你可以升级 MMKV 到 key 自动过期特性。注意这是格式不向下兼容的升级操作。一旦升级到 key 自动过期,旧版 MMKV (<= v1.2.16) 将无法正常读写该文件。

  • 全局过期. 最简单的用法是给整个文件设定统一的过期间隔。

    // expire in a day
    kv.enableAutoKeyExpire(24 * 60 * 60);

    或者,你可以选择只升级 MMKV,但不设置全局过期间隔。这种情况下,默认每个 key 不过期。

    // enable auto key expiration without global duration
    kv.enableAutoKeyExpire(0);
  • 单独过期. 你可以给每个 key 设置单独的过期间隔,区分于文件的全局过期间隔。注意,你仍然需要先升级 MMKV 为 key 自动过期

    // enable auto key expiration with an hour duration
    kv.enableAutoKeyExpire(60 * 60);
    
    // set a key with the file's global expiration duration, aka 60 * 60
    kv.set("some value", "key_1");
    
    // set a special key that expires in two hours
    kv.set("some value", "key_2", 2 * 60 * 60);
    
    // set a special key that never expires
    kv.set("some value", "key_3", 0);

    或者,你可以选择只升级 MMKV,但不设置全局过期间隔。这种情况下,默认每个 key 不过期。

    // enable auto key expiration without global duration
    kv.enableAutoKeyExpire(0);
    
    // set a key that never expires
    kv.set("some value", "key_1");
    
    // set a special key that expires in an hour
    kv.set("some value", "key_2", 60 * 60);
  • 过期间隔的单位是秒。你可以使用任意自定义间隔,例如一周是 7 * 24 * 60 * 60

下一步

Clone this wiki locally