SQL实践篇(二):为什么微信用SQLite存储聊天记录?

简介

SQLite是一个嵌入式的开源数据库引擎,大小只有3M左右,因此我们可以将整个SQLite嵌入到应用中,而不再需要采用传统的客户端/服务器(CS)架构。这样的好处在于非常轻便,很多智能设备和应用中都可以使用SQLite,比如说微信和QQ就采用了SQLite作为本地聊天记录的存储。

本节将从以下几个方面,深入了解一下SQLite:

  • 什么是SQLite?它有哪些优点和不足?
  • 如何在Python中使用SQLite?
  • 如何编写SQL,来通过SQLite查找微信的聊天记录?

什么是SQLite

SQLite是2000年左右发布的,其采用C语言编写,而不是C++,从而一定程度上尽量提升底层代码的执行效率

它的优势在于非常轻量级,存储数据非常高效,查询和操作也很方便。而且它不需要安装和配置,很方便迁移,也方便嵌入到很多应用程序中。相比需要托管在服务器上的RDBMS,SQLite的约束更加少,更容易操作。

不足也很明显,它不适用高并发的情况,一般常用于小型或者中型的数据存储。比如说在微信本地可以使用SQLite,即时是几百M的数据文件,对SQLite来讲也是信手拈来,但是微信本身的服务器就不能用SQLite了,缺点太明显了,比如说影响最大的一点:SQLite同一时间只允许一个写操作,吞吐量非常有限。

而且,作为一个简化版的数据库,SQLite没有用户管理功能,在语法上也跟常规的SQL有所不同,充斥了一部分的"方言"。

比如说SQLite 不支持right join,仅支持只读视图(只能创建、读取和删除,不能修改)

但总体来讲,基本还是支持SQL标准的。

在python中使用SQLite

在这里插入图片描述

python中集成了SQLite3,直接加载相应的包就可以使用。

import sqlite3

创建数据库连接:

conn = sqlite3.connect("wucai.db")

如果没有这个文件,那么这一步会在相应的工程路径里创建指定的文件,用来存放数据。否则会直接连接现有文件。

然后我们可以使用conn来操作连接,创建游标:

cur = conn.cursor()

得到游标之后,我们就可以通过游标来调用execute()执行各种各样的SQL语句。

比如说创建一个heros数据表:

cur.execute("CREATE TABLE IF NOT EXISTS heros (id int primary key, name text, hp_max real, mp_max real, role_main text)")

再插入一条数据:

cur.execute('insert into heros values(?, ?, ?, ?, ?)', (10000, '夏侯惇', 7350, 1746, '坦克'))

或者使用executemany()来批量插入数据:

cur.executemany('insert into heros values(?, ?, ?, ?, ?)', 
           ((10000, '夏侯惇', 7350, 1746, '坦克'),
            (10001, '钟无艳', 7000, 1760, '战士'),
          (10002, '张飞', 8341, 100, '坦克'),
          (10003, '牛魔', 8476, 1926, '坦克'),
          (10004, '吕布', 7344, 0, '战士')))

执行查询:

cur.execute("SELECT id, name, hp_max, mp_max, role_main FROM heros")

注意,这时候游标会指向结果集的第一条数据,我们需要通过以下几种方式来获取更多的数据:

  • cur.fetchone():获取一条记录;
  • cur.fetchmany(n):获取n条记录;
  • cur.fetchall():获取全部数据行。

比如说想要获取全部结果集,可以写成:

result = cur.fetchall()

如果我们对事务操作完了,可以执行以下命令来提交事务:

conn.commit()

同样的,如果不再需要使用数据库,也需要关闭游标和数据库连接:

cur.close()
conn.close()

上面过程的完整代码摘录如下:

import sqlite3
# 创建数据库连接
conn = sqlite3.connect("wucai.db")
# 获取游标
cur = conn.cursor()
# 创建数据表
cur.execute("CREATE TABLE IF NOT EXISTS heros (id int primary key, name text, hp_max real, mp_max real, role_main text)")
# 插入英雄数据
cur.executemany('insert into heros values(?, ?, ?, ?, ?)', 
           ((10000, '夏侯惇', 7350, 1746, '坦克'),
            (10001, '钟无艳', 7000, 1760, '战士'),
          (10002, '张飞', 8341, 100, '坦克'),
          (10003, '牛魔', 8476, 1926, '坦克'),
          (10004, '吕布', 7344, 0, '战士')))
cur.execute("SELECT id, name, hp_max, mp_max, role_main FROM heros")
result = cur.fetchall()
print(result)
# 提交事务 
conn.commit()
# 关闭游标
cur.close()
# 关闭数据库连接
conn.close()

我们也可以通过DBeaver或者navicat等数据库可视化工具,来查看生成的SQLite文件。

通过SQLite查询微信的聊天记录

纯兴趣了解吧。主要是因为教程里讲解本节的时候,是以IPhone为例,我只是个平平无奇的安卓,因此没法仿照做完整的实践,所以就直接把教程里的过程贴出来吧。

第一步,使用 iTunes 备份 iPhone;

第二步,在电脑中查找备份文件。当我们备份好数据之后,需要在本地找到备份的文件,如果是 windows 可以在 C:\Users\XXXX\AppData\Roaming\Apple Computer\MobileSync\Backup 这个路径中找到备份文件夹。

第三步,查找 Manifest.db。在备份文件夹中会存在 Manifest.db 文件,这个文件定义了苹果系统中各种备份所在的文件位置。

第四步,查找 MM.sqlite。Manifest.db 本身是 SQLite 数据文件,通过 SQLite 我们能看到文件中包含了 Files 数据表,这张表中有 fileID、domain 和 relativePath 等字段。

微信的聊天记录文件为 MM.sqlite,我们可以直接通过 SQL 语句来查询下它的位置(也就是 fileID)。

SELECT * FROM Files WHERE relativePath LIKE '%MM.sqlite'

第五步,分析找到的 MM.sqlite。

这里我们需要在备份文件夹中查找相关的 fileID,比如 f71743874d7b858a01e3ddb933ce13a9a01f79aa。

找到这个文件后,我们可以复制一份,取名为 weixin.db,这样就可以使用 navicat 对这个数据库进行可视化管理,如下图所示:

在这里插入图片描述

微信会把你与每一个人的聊天记录都保存成一张数据表,在数据表中会有 MesLocalID、Message、Status 等相应的字段,它们分别代表在当前对话中的 ID、聊天内容和聊天内容的状态).

可以通过以下代码,来查看有多少聊天对象表:

SELECT name FROM sqlite_master WHERE type = 'table' AND name LIKE 'Chat\_%' escape '\'

2023-11-9 15:22:46 注意,看了一下教程里的评论,有人说现在的备份文件是message_1.sqlite,因此可以将第四步里的查询SQL替换成:

SELECT name FROM sqlite_master WHERE type = 'table' AND name LIKE 'Chat\_%' escape '\'

参考文献

  1. 40丨SQLite:为什么微信用SQLite存储聊天记录?