[内容转载] 微信全平台开源终端数据库WCDB迎来重大升级!

news2024/11/25 15:57:29

第一次看到这么详实的文章,转给大家看看,很多提出问题和如何解决问题的方法因为太细了,就没有完全转载,感兴趣的可以去看原文。

一、简介

WCDB 是微信团队开源的一款基于 SQLite 的终端数据库。自 2017 年 6 月开源以来,它在业界得到了广泛认可并被大量应用,迄今已经推出了十多个版本。在这个过程中,WCDB 一直保持良好的向后兼容性,不断完善原有接口的细节并添加新功能。

二、挑战

作为国内乃至全球范围内使用数据库最频繁的 App,微信内部涉及上百种不同业务的数据库,存储的消息条数可达百万乃至千万级别。这种庞大的数据量和日益丰富的应用场景,给 WCDB 带来了不断更新的需求和挑战,原有的代码框架逐渐难以应对。

三、重大升级

自 2019 年起,微信团队决定放弃接口的向后兼容性,全力打造一个更加强大的新版 WCDB。经过多次迭代,WCDB 的接口层和核心逻辑层已经得到了全面改进,同时也积累了许多新功能。如今,微信团队已经重大升级的新版WCDB进行了开源,主要变化及更新包括:

变化一:更丰富的开发语言支持

WCDB 1.0 版本支持 Objective-C、Swift、Java 三种开发语言,但是三种语言的 WCDB 除了共用同一个版本的 SQLite 和共用同一套备份修复逻辑,其他代码都是独立开发的。随着 WCDB 不断迭代,WCDB 的很多新能力都是在 ObjC 版本上开发完成和上线验证,Swift和Java版本基本处于停止迭代的状态,他们之间的差异也越来越大。在理想的状态下,不同语言版本的 WCDB 应该拥有同样的能力,但是如果把 ObjC 版本的新逻辑重新在 Swift 和 Java上实现一遍,不仅工作量大,还容易出错,需要再次上线验证,不太现实。

幸运的是,ObjC 版本的 WCDB 的核心逻辑都是用 C++ 实现的,ObjC 只是用来实现接口层的逻辑。很多支持多种开发语言的库都是使用 C++ 语言来实现核心逻辑,其他语言只是用来实现接口层,比如很热门的客户端 NoSQL 数据库组件realmDB就是如此。WCDB 也可以按照这个思路来设计,这样 ObjC 版本的 WCDB 只需小幅调整,将核心逻辑完全改用 C++ 来实现,Swift 和 Java 通过桥接方法来接入 C++ 核心逻辑。此外,为了充分支持微信各端不同场景的数据库开发需求,WCDB还扩展支持了C++ 和 Kotlin,这样就完整覆盖了现在终端开发的主流语言。

接口层代码结构如下:

 

在这种代码架构下,不同语言的 WCDB 可以按需集成到同个项目中,有利于节省代码和减少包大小,还可以避免不同语言接口逻辑的冲突,甚至使用不同语言的接口来使用同一个DB都不用担心有任何逻辑冲突。

变化二:更强大的 SQL 表达能力

WCDB 1.0 提供了Winq(WCDB Integrated Query,WCDB集成查询)来方便数据库开发者拼写 SQL 语句。1.0 版本的 Winq 使用 C++ 语言抽象和实现了 SQLite 的 SQL 语法规则,使得开发者可以告别字符串拼接的胶水代码。通过和接口层的 ORM 结合,使得即便是很复杂的查询,也可以通过一行代码完成,并借助 IDE 的代码提示和编译检查的特性,大大提升了开发效率。比如一个 SQLite_sequence表的查询语句,使用 Winq 来编写可以是这样:

WCDB::StatementSelect().select({WCDB::ColumnResult(WCTSequence.seq)})
            .from("sqlite_sequence")
            .where(WCTSequence.seq > 1000)
            .orderBy({WCTSequence.seq.order(WCTOrderedAscending)})
            .limit(10)
            .offset(100)

可以看到,Winq 将 SQL 语句中的 Token抽象成 C++类,将不同的 Token 的连接能力抽象成了C++类的接口,并通过链式调用的方式,让Winq拼接出来的SQL语句读起来跟实际的SQL语句接近,可读性好。但随着在微信中的应用推广,这一版的Winq还有下面几个明显的问题:

  1. 每次接口调用之后,是立即 append 对应内容到 SQL 字符串。这就要求接口的调用顺序必须严格符合 SQL 的语法顺序,而且调用之后无法再修改原内容。这样不符合链式调用的使用直觉,比较容易犯错。
  2. Winq 创建的语句没有独立保存它内部各个Token的配置状态,只保存一个 SQL 字符串。这样当内部逻辑接收到业务逻辑调用的 Winq 语句时,它面对的只是SQL 字符串,很难对 Winq 语句做一些语法分析或者修改 Winq 语句,限制了 WCDB 的功能扩展。
  3. Java、Kotlin、Swift这些不能使用 C++ 的语言上也需要使用 Winq。
  4. 不支持表达全部的 SQL 语句,一些少用的复杂语句就只能手写 SQL 字符串了。
  5. 一些接收 Token 的接口在使用的时候还不够简洁,比如 .select()中接收 ORM 的 Property 时需要先构造 WCDB::ColumnResult,再显式转成数组传入。

为了解决这些问题,微信团队采取了诸多解决措施,具体可以 查看原文

变化三:更安全的数据存储能力

前面两节让大家对如何使用 WCDB 有了个整体感受,这部分的设计目标是让大家能够更便捷得存储数据,而如何更安全地存储数据,是数据库设计更重要的目标,这一直是微信团队不断思考的问题,也是其需要扩展强化 SQLite 的最初动机。因为聊天记录作为用户在微信上产生的最重要数字信息,只存储在用户的终端设备上。如果出现数据库损坏,聊天记录将会永久性丢失,这是绝大部分用户无法接受的。为了提高数据安全性,新版 WCDB 有了下面两个新设计:

1、新数据备份和修复方案

WCDB 1.0 中我们推出了一种数据库备份和修复方案,这里有详细介绍,它的整体逻辑是这样的:

SQLite 数据库是以页为单位的双层的 BTree 的结构,上层是 SQLite 的 master 表,下层是每个用户定义的表,其叶子页就是真正的数据所在的地方。当数据库损坏发生在某一中间节点时,它下面的所有支路的数据都将因为找不到而丢失。我们可以备份下层表的表名到根结点页码的映射,那么可以解决最严重的问题,即上层表损坏。当下层表损坏时,也只会丢失单个表。

WCDB 1.0 的备份和修复方案解决了当时数据库损坏后数据就全部丢失的燃眉之急,平均修复率有 70~80%。但是数据库损坏通常发生在磁盘损坏的时候,一般都是一大片数据坏了,所以经常修回来也依然是一片狼藉。所以新版 WCDB 就干脆一点,除了备份 master 表,还增加备份普通表的表名到它叶子页页号和crc校验值的映射,这样就能一步到位,修复的时候根据页号就可以直接找到普通表的数据,校验 crc 值没变,就可以确认数据没有损坏或者变更,从而可以将未损坏的数据完整恢复到新数据库。

2、防止外部逻辑写坏数据库

使用备份和修复来保护数据属于比较被动的方法,数据出错了才补救,修复率无法做到100%,还是不够安全。在磁盘损坏这种低概率发生的场景,备份和修复方法还能应对,但是在外部逻辑出现Bug导致大规模写坏数据库的场景,就难以应对了。我们需要一种更主动的方法来防止数据库被写坏,防患于未然。

外部逻辑写坏数据库的情况会有两种,一种是误用了数据库的路径或者误删了数据库,这个很难出现,要保护也是通过hook系统调用的方式来做,无法集成在WCDB内部;另一种是误用了数据库的文件句柄,这种相对常见,微信就遇到了好几次这种问题,要重点处理。

要防止文件句柄被误用时写坏数据库,一个简单的想法是尽量打开数据库文件时都是只读打开,这样外部逻辑就无法用这个句柄来更改数据库了。对于大部分数据库组件来讲,要实现这点,还是挺复杂。打开句柄时要能够判断下这个操作会不会修改数据库,只读打开之后还要遇到更改数据库的操作时,又要重新打开数据库文件句柄。

而 WCDB 的 WAL 模式是采用独立线程异步执行 checkpoint 的,在这种配置下,业务逻辑即便是要写入数据到数据库,也不需要修改到主 DB 文件,只需要修改 WAL 文件,只有到 checkpoint 时才需要修改主 DB 文件。所以 WCDB 可以在业务逻辑读写数据库时全部只读打开主 DB 文件,只有在 checkpoint 时才可写打开主DB文件。这样就能最大限度地减少主 DB 文件的可写句柄的存活时间,防止外部逻辑误写。

 

变化四:更灵活的数据扩展能力

随着用户数据的积累和功能的复杂化,早期数据库表设计会越来越难以满足需求,微信在迭代的过程中也遇到了很多这类问题:

  • • 早期功能开发的时候为了方便,将多种没有直接关联的表格存放到同一个数据库中存储。因为 SQLite 不支持并行写入,这样也就限制了不同表格的并行更新性能,在数据积累多了和调用频繁了之后,容易造成性能瓶颈;同时,因为数据库是会损坏的,读写越频繁越容易损坏,把数据都放到一个数据库会大幅增加数据损坏和丢失的风险。
  • • 很多业务的表一般都会有一两个字段用来存储 XML、Json、PB 之类的序列化数据,这些数据容易随着业务发展,新增属性越来越多,数据越来越长,数据库也越来越大,我们的业务场景里面有些xml长度都达到了 10k 以上。这不仅不会导致空间占用问题,还会影响读写性能,而且这个问题发现的时候都是在已经积累了相当量数据之后的,存量数据已经难以处理了。
  • • 随着功能的扩展,不仅需要在 XML 或者 Json 字段中加内容,还可能需要对原有的数据表添加新列,如果在旧表没有添加新字段之前就对新字段进行读写,就会出现读写错误。

针对这两类场景,WCDB 给出了业界首创的解决方法,分别是数据迁移能力、数据压缩能力和自动添加新列能力。

1、数据迁移能力

iOS微信早期在业务逻辑层面做过两次数据迁移,一次是收消息操作指令数据迁移,因为数据量较小,可以阻塞式一步迁移到位之后再使用迁移后的数据;另一次是联系人数据迁移,因为数据量较大,需要采用非阻塞式迁移的方案。非阻塞式迁移过程中,数据可能处于三种状态,未迁移状态只有旧表,迁移完成后只有新表,而在迁移中则两张表都有,开发者需要对所有业务涉及的代码都做这三种状态的区分,并且在迁移中合并旧表和新表的数据。这部分代码并不难,但是冗长、而且和业务耦合严重,比较难开发和维护,更尴尬的是,很难找到一个适合的删除兼容代码的时间,兼容代码可能需要一直存在,很影响后续的迭代。

为了解决这个问题,WCDB 就提出了一个概念,由 WCDB 来解决兼容问题,让开发者可以 以迁移已经完成为假定前提 进行开发。同时因为是框架层代码,天然就是 code once, run everywhere,所以开发也不需要花费时间在灰度上。

WCDB 的数据迁移方案是这样的,当数据库操作的请求过来时,会先对其使用的数据库句柄进行迁移配置,如果是跨 db 的迁移,会把另一个 db attach 到当前句柄,以实现跨 db 的 SQL。然后检测旧表是否存在,如果不存在则说明迁移已经完成,直接执行 SQL。如果存在则创建一个 temp view,用作后续的兼容。然后 WCDB 会预处理数据库的操作请求,再进行真正的执行。这个预处理是类似于 hook 的逻辑,WCDB 会拦截开发者需要执行的 SQL,然后进行一些修改和处理,以给开发者提供一个迁移已经完成的假象。这里主要针对增删查改中的操作进行处理。迁移流程如下:

 

2、数据压缩能力

要解决数据库中 XML、Json、PB等序列化数据过长的问题,一个直接的方法是把这些数据都压缩一下再写入数据库。一般来讲,开发者要做数据压缩,首先是是要选择一个合适的压缩算法,然后需要在数据读写的各个环节引入加解压逻辑,要对压缩的数据做好标记,然后要想办法处理存量数据,要做极致性能优化的话还要想办法缓存加解压过程的各种内存状态。这些事情处理起来都是不小的工作量,而 WCDB 提出的数据压缩能力可以帮助开发者一步到位解决这些麻烦,只需要一个简单的无侵入配置就好。

在压缩算法方面,肯定是要选择无损压缩算法。早期的无损压缩算法主要分为哈夫曼编码和算术编码两大类。哈夫曼编码相信大家都非常熟悉,它通过将高概率出现的字符编码为更短的码点来实现压缩。这类算法的优势在于编码速度快,但只有当各个字符的出现概率都是 2 的负整数次幂时,哈夫曼编码的压缩率才能达到香农极限,其他情况下都无法达到,因此压缩率较低。

与之相对的是算术编码,它根据整个字符串出现的概率,将整个字符串转换为一个介于 0 到 1 之间的小数。由于这个小数能精确表示字符串的出现概率,因此算术编码的压缩率能够逼近香农极限。然而,由于编解码过程涉及大量乘除法,其性能相较于哈夫曼编码较差。

ANS+FSE编码是 2014 年发布的一种新算法,它将整个字符串编码成一个大于 1 的整数,这个整数与字符串的出现概率精确相关,因此这个算法的压缩率也能够逼近香农极限。同时,由于其计算过程仅涉及加法、移位和掩码计算,性能上更接近哈夫曼编码,因此它目前被认为是压缩率和性能综合最优的算法。这个算法的最佳实现便是众所周知的 Zstd。

然而,Zstd 的普通压缩模式仅能解决单个 XML 或 Json 内部的冗余度。由于不同的 XML 或 Json 具有相似的标签,不断存储这些标签也会产生很多冗余。为了解决这个问题,Zstd 的字典压缩模式可以有效消除不同数据之间的相似部分,显著提高压缩率并提升性能。因此,Zstd 字典压缩模式被认为是当前压缩序列化数据的最优解。预计在未来,也不太可能出现能明显提升压缩率的压缩算法。因此,WCDB 主要采用 Zstd 字典压缩算法来进行数据压缩。

确定了压缩算法之后,我们看下数据压缩的整体框架和流程:

外部逻辑写入的新数据的时候,在 WCDB 的内部会把数据压缩了之后,再写入文件;读取数据的时候,对于已经压缩的数据,WCDB 也是解压后再给到外部。同时,WCDB 也会在子线程处理存量数据,把未压缩的数据读取出来,压缩后再更新回去。这样外部只需要配置数据库的哪个表的哪个字段需要压缩,CRUD的时候,都可以假定数据都是没压缩的来操作数据,不需要关注数据压缩的实现细节和内部状态,整个加解压过程可以做到外部无感知和无侵入。这样数据压缩就可以很方便在不同业务场景和不同平台扩展。

外部逻辑 CRUD 的时候,为了隐藏数据加解压的细节,需要在WCDB的内部,对要执行的 SQL 做一些处理和转换。首先,如果一个表有字段配置了压缩字段的话,底下就会给这个表的压缩字段逐个添加一个对应的,存储压缩状态的字段,状态字段存储了是否压缩的状态,以及压缩所用的算法,然后还要预处理 SQL,把SQL 中对压缩字段的读写,转换成对压缩字段和压缩状态字段的合并读写,这样就能把加解压逻辑引入进来。

数据压缩CRUD兼容方法:

 

3、自动补全新列能力

业务逻辑在开发迭代的过程中可能会给原有的表格添加新列,SQLite 是支持给已有的表格添加新列的,WCDB 也会在调用 createTable 的时候自动添加 ORM 类中新配置的列,但是在我们实践过程中这类错误还是很常见。一个原因是可能是开发同学的疏漏,必须要在使用表格之前先主动调用添加新列的逻辑,依赖开发同学的自觉,在多人协作开发时更容易疏漏;另一个原因也可能是确实找不到合适的时机添加新列,比如很多个表对应统一个 ORM 类的场景。如果要对这些表添加一个新列,是找不到一个统一的处理时机的,因为重度用户可能有几千个这样的表,如果一起处理的话,会很耗时,容易造成卡顿;如果每次读写这些表时都判断一下是否需要添加新列,又会明显降低性能。

一个表格的所有列都是在其对应的 ORM 类中配置的。在理想的情况下,开发者在 ORM 类中配置了新列之后,就应该让这个配置可以视为立即生效,开发者无需关心添加新列的时机。为了达到这个效果,WCDB 添加了自动补全新列的能力,其核心的思想是这样,当读写数据库的时候如果报错有未识别的列,则立即检查读写的表格对应的 ORM 类是否有新配置的列跟这个未识别的列同名,如果存在的话,就将新配置的列添加到这个表格,再重试出错的逻辑。采用这种出错再检查的方式,可以将检查新列的逻辑的调用时机降低到最少,又能全面处理新列没及时添加数据库时造成的问题。

自动补全新列流程:

自动补全新列的能力在性能影响和解决问题完整程度上看都比较理想,但实现起来也比较有难度。主要要解决两个问题,一个是如何在执行出错时获取到这个表格对应的 ORM 类,一个是如何避免将错误的列添加到表格中。

对于第一个问题,因为要使用 ORM 类配置的列时,都是从这个类的内部信息中去获取这个列配置的列名,这样才能用列名构造一个 Column 对象用于 Winq 中组装语句,比如上文例子中用到的 WCTSequence.seq就是调用 WCTSequence 这个类的方法来获取 seq 属性配置的列名来构造一个 Column对象。所以我们可以在使用这种途径构造 Column时,将整个 ORM 类的数据库配置信息一并传入,并保存在 Column中,这样就可以在 Winq 语句中获取到其中所用到的列所在的 ORM 类的全部配置信息。因为 ORM 信息是保存在堆上的全局量,所以这个改动实际上只是多传递和保存一个指针,并不会给 Winq 的使用带来性能影响。

实现了这些之后还不够,我们实际需要知道的是 Winq 语句中涉及到的表格对应的 ORM 信息,而不是列的。这里我们采用了舍弃部分场景的方法,只处理读写单个表格的场景,缺失的列在 Winq 语句中对应两个不同的 ORM 类也放弃处理,在一个 SQL 语句中操作多个表格或者使用多个 ORM 类的情况在实际应用中还是极少见。

对于第二个问题,主要存在下面两种情况需要解决:

  1. 防止 SQLite 误报未识别列。比如 SELECT city FROM China WHERE city MATCH '广东: 广州'会报错 no such column: 广东,但实际并不存在这一列,只是 fts 的搜索语法误把冒号前面这部分识别为列名。这种情况可以通过提取报错信息中的列名去匹配 Winq 语句中的列名来解决。
  2. 防止开发者用错 ORM 类时把这个类配置的列都误添加进来。开发者在编写 Winq 语句时,即便是有输入提示,编写错误的情况还是无法完全避免。这种情况可以通过检测匹配的 ORM 类中配置的列必须有一半已经添加到这个表格来解决。极端情况下,即便误添加一些列,只要这些列不实际写入数据,也不会占用存储空间和影响读写性能。

变化五:更极致的性能优化能力

1、FTS5 优化

iOS微信在 2020 年到 2021年期间,将联系人搜索、聊天记录搜索、收藏搜索这三个主要的本地搜索逻辑全部改用 SQLite 的 FTS5 组件来实现,WCDB 也借此机会完善了 FTS5 支持,优化了 FTS5 的读写性能,重新设计了 FTS5 分词器,并丰富了分词器的能力,还支持了拼音搜索,具体见《iOS微信全文搜索技术优化》:https://mp.weixin.qq.com/s/Ph0jykLr5CMF-xFgoJw5UQ 。

2、可中断事务

在需要对数据库进行大量数据更新的场景,我们的开发习惯一般是将这些更新操作统一到子线程处理,这样可以避免阻塞主线程,影响用户体验。在微信中这种场景有收消息、清理朋友圈数据、清理视频号数据等,收消息可能会一次性收取几百上千条消息,朋友圈和视频号的数据拉下来之后会存储在数据库中,但是不需要永久存储,就需要定期清理过期数据。

对于这类场景,如果只是将数据更新操作放到子线程执行,是不能完整解决问题的。因为 SQLite 的同个DB不支持并行写入,如果子线程的数据更新操作耗时太久,而主线程又有数据写入操作,比如用户在收消息的同时还会发消息,这样也会造成主线程阻塞。以前的做法是,将子线程的数据更新操作拆成一个个耗时很小的独立操作分别执行,比如收消息是逐条写入数据库,这样可以避免主线程阻塞问题,但是又会导致磁盘 IO 量大和增加子线程耗时的问题。因为SQLite读写数据库时以一个数据页为单位的,一个数据页的大小在 WCDB 中是 4kb,单个数据页一般可以存多条消息,逐条消息写入容易导致同一个数据页被读写多次。为了减少磁盘写入量,只能将所有的数据更新操作放到一个事务中执行,这样又会造成主线程阻塞的问题。

收消息写入示例:

 为了解决大事务会阻塞主线程的问题,我们在 WCDB 中开发了一种可中断事务。可中断事务把一个流程很长的事务过程看成一个循环逻辑,每次循环执行一次短时间的DB操作,比如写入一条新消息。操作之后根据外部传入的参数判断当前事务是否可以结束,如果可以结束的话,就直接 Commit Transaction,将事务修改内容写入磁盘。如果事务还不可以结束,再判断主线程是否因为当前事务阻塞,没有的话就回调外部逻辑,继续执行后面的循环,直到外部逻辑处理完毕。如果检测到主线程因为当前事务阻塞,则会立即 Commit Transaction,先将部分修改内容写入磁盘,并唤醒主线程执行DB操作。等到主线程的DB操作执行完成之后,在重新开一个新事务,让外部可以继续执行之前中断的逻辑。可中断事务的整体逻辑如下图所示:

可中断事务让一系列DB操作尽量保持在一个事务中执行,同时能够及时响应主线程的阻塞事件,避免了主线程的卡顿问题。因为事务可能会被分成多次提交,所以事务整体的原子性是不保证的,这个需要使用者注意,必要的时候需要有额外的机制来保证事务的原子性。

3、WAL 文件头更新优化

WAL 文件的文件头保存着 WAL 文件的版本号、页大小、salt 值、校验值等关键信息,每次写入 WAL 文件的第一页数据的时候,都需要一起更新文件头的内容(只有这个时机更新 WAL 文件的头部)。SQLite 的早期版本(WCDB 1.0.8版本之前用的 SQLite 版本)在写入 WAL 文件头时,只是将内容写到磁盘缓存,没有调用 fsync。SQLite 后来发现如果磁盘缓存是随机写入到磁盘,那可能存在 WAL 文件头以外的内容已经写入到磁盘但是文件头还没更新的情况,会导致数据库损坏(具体见SQLite: View Ticket)。所以现在的 SQLite 版本写入 WAL 文件头之后会调用 fsync 将磁盘缓存写到磁盘上,这会导致写入 WAL 文件第一个 frame 的耗时从 5ms 左右提升到 100ms,容易造成卡顿,这个曾经是 iOS 微信的数据库卡顿的头号共性问题。

为了解决这个问题,WCDB 修改 SQLite 源码,对 WAL 文件头的更新做了个优化。在 WCDB 的配置下,写入 WAL 文件的第一页有两个时机,一个是新建数据库后首次写入数据,另一个是将 WAL 文件中的内容完全 Checkpoint 完的时候。对于第一个时机,没法做优化,对于第二个时机,则可以将 WAL 文件头内容的更新操作提前到 Checkpoint 时执行。

具体逻辑是这样,Checkpoint 结束后,如果此时没有其他线程在读写 WAL 文件,则加锁防止其他线程写 WAL 文件,sync 重写 WAL 文件的文件头,再释放锁。在写入 WAL 文件的第一个 frame,如果发现 WAL 文件没创建或者文件头没有重写时,才尝试 sync 重写文件头。因为 Checkpoint 都是子线程执行的,而且读写 WAL 文件的时机不是很多,所以这个优化可以把绝大部分 WAL 文件头的更新操作放到子线程执行,避免造成 UI 卡顿。优化上线之后,iOS 微信的卡顿次数降低了5%~10%。

wal 文件头更新优化:

 

至此,关于新版本WCDB的主要变化及更新介绍完毕。

四、开源地址

新版 WCDB 已在 Github 开源:GitHub - Tencent/wcdb: WCDB is a cross-platform database framework developed by WeChat.,欢迎各位Star!

五、总结

在接口层面,新版 WCDB 全面支持了 C++、Java、Kotlin、Swift 和 ObjC 这五种主要的终端开发语言,覆盖了 Android、iOS、Windows 、macOS 和 Linux 这五大终端平台。同时,我们还对 Winq 进行了重写和强化,使开发者能够在各种语言中使用原生语法编写任意 SQL。

在功能层面,新版 WCDB 推出了全新的数据备份和修复方案,大幅提升了数据修复率,同时将数据备份的性能消耗降至可忽略不计。此外,我们还重点推出了数据迁移和数据压缩这两个新功能,让开发者仅通过简单的配置,就能高效处理复杂业务中的数据过度聚集和数据过度膨胀这两大难题。新版 WCDB 还推出了 FTS5 优化和可中断事务等新特性,使开发者在特定场景下可以实现更极致的性能优化。

原文地址:https://bbs.deepin.org/post/270554

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2202256.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【C++】STL——stack和queue

目录 前言容器配接器(适配器)stack的使用stack的模拟实现queue的使用queue的模拟实现双端队列(deque) 前言 前面我们已经学习了STL容器中的string、vector还有list。 【C】string的模拟实现 【C】STL——vector的模拟实现 【C】S…

gradle降级

文章目录 引子编译错误参考 引子 项目引入了一个高版本项目改造,编译运行失败,提示如下错误,解决方案要么升级gradle版本,升级适配改各种方法配置,现有的项目存在适配问题;要么就是版本降级。 本文以介绍…

docker环境redis启动失败

现象&#xff1a; 查看日志错误为 Bad file format reading the append only file: make a backup of your AOF file, then use ./redis-check-aof --fix <filename> 经查询为aof文件损坏导致&#xff0c;修复aof即可 解决方法&#xff1a; 1.查看执行的docker命令&…

c到c++衔接速成

温馨提示&#xff1a;本篇文章乃博主多次学习后的总结&#xff0c;其中一些专业名词的概念不会做介绍&#xff0c;本篇文章只解决实操问题 c到c中&#xff0c;语法上得到了许多升级&#xff0c;那么哪些地方升级了&#xff0c;我们来快速复习一下 目录 头文件的升级 命名空间…

国产长芯微LDC5791是一款单通道、20位、无缓冲电压输出DAC完全P2P替代AD5791

描述 LDC5791是一款单通道、20位、无缓冲电压输出DAC&#xff0c;采用最高33 V的双极性电源供电。正基准电压输入范围为5 V至VDD – 2.5V&#xff0c;负基准电压输入范围为VSS 2.5 V至0 V。相对精度最大值为1 LSB&#xff0c;保证工作单调性&#xff0c;差分非线性(DNL)最大值…

基于echarts的旅游信息(游客行为分析)大数据可视化平台(echarts+Django+MySQL+Scrapy框架爬虫;程序+论文+开题报告)

摘要 本论文旨在研究Python技术和ECharts可视化技术在游客行为分析系统中的应用&#xff0c;通过对旅游数据的深入分析&#xff0c;希望能够揭示出南京旅游市场的现状和趋势&#xff0c;从而为游客提供更优质的旅游体验&#xff0c;也可以为政府和旅游企业制定更加科学和有效的…

Spring事务的1道面试题

每次聊起Spring事务&#xff0c;好像很熟悉&#xff0c;又好像很陌生。本篇通过一道面试题和一些实践&#xff0c;来拆解几个Spring事务的常见坑点。 原理 Spring事务的原理是&#xff1a;通过AOP切面的方式实现的&#xff0c;也就是通过代理模式去实现事务增强。 具体过程是…

2024年诺贝尔物理学奖

2024年&#xff0c;诺贝尔物理学奖没有颁给物理学家&#xff0c;而是授予了两位计算机科学家&#xff0c;表彰他们在神经网络领域的开创性工作&#xff0c;这项研究成为了我们今天所称的人工智能的基础。这标志着物理学与计算机科学之间的深度融合。 获奖者约翰霍普菲尔德和杰弗…

财政警钟敲响!CBO预测:美国2024财年赤字预计达1.8万亿美元

KlipC报道&#xff1a;美国国会预算办公室&#xff08;CBO&#xff09;近日发布报告显示&#xff0c;2024财年&#xff08;2023年10月1日至2024年9月30日&#xff09;&#xff0c;美国联邦政府预算赤字高达1.8万亿美元&#xff0c;与上一财年相比增加了1390亿美元。据CBO预测&a…

转型AI产品经理需要掌握的硬知识、经理能力模型和常见AI概念梳理

近几年&#xff0c;从亚马逊&#xff0c; Facebook&#xff0c;到谷歌&#xff0c;微软&#xff0c;再到国内的BAT&#xff0c;全球最具影响力的技术公司都将目光转向了人工智能&#xff08; AI &#xff09;。2016年 AlphaGo 战胜李世石&#xff0c;把公众的目光也聚集到了人工…

IROS24新鲜出炉:PRL-Track,最先进的无人机视觉目标跟踪系统!

导读&#xff1a; 近年来&#xff0c;随着无人机技术的飞速发展&#xff0c;视觉目标跟踪在无人机的自主应用中扮演着越来越重要的角色&#xff0c;然而&#xff0c;在复杂多变的环境中&#xff0c;实现高精度的目标跟踪并非易事。无人机在飞行过程中&#xff0c;常常会遇到目标…

介绍几个电池充电管理芯片(TP4056、SGM40561)

TP4056 上一篇我们介绍了个TP4055&#xff0c;那么跟TP4055相比&#xff0c;TP4056肯定是做了升级的。 首先是有最高1000mA的充电电流&#xff0c;而TP4055是500mA。 一般来说我们尽可能的让充电电流接近电池容量的一半&#xff0c;这样对电池比较好。 充电电压都是4.2V。 …

winform实现托盘语音提醒

测试环境: visual studio 2022 window 10 .net framework 4.6 本文实现的功能有&#xff1a; 1 托盘最小化 2 语音定时播放 3 检测到操作系统被客户点静音后&#xff0c;需要程序控制开启音量(在运行过程中&#xff0c;由于语音重复播放&#xff0c;客户很烦&#…

【Protobuf】基本使用总结+项目实践

概述 序列化与反序列化 网络传输中使用&#xff0c;可以实现将对象转换为二进制序列&#xff0c;然后将二进制序列转换为对象&#xff0c;这一个交互的过程就是序列化。生成的数据&#xff0c;持久化存储到磁盘上的过程&#xff0c;也需要经过序列化和反序列化才可以实现。 序…

SpringBoot实现的美发门店客户关系管理(CRM)系统

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…

浅谈2024年诺贝尔物理学奖颁发给了机器学习与神经网络领域的研究者

目录 1.概述 1.1. 跨学科的融合 1.2. 推动科学研究的工具 1.3. 对科学界的激励 1.4. 技术的社会影响 2.机器学习与神经网络的发展前景 2.1.具体应用与作用 2.1.1. 医疗健康 2.1.2. 金融 2.1.3. 制造业 2.1.4. 交通与物流 2.1.5. 零售 2.2.未来展望 2.3.科学研究与…

C# 实现调用函数,打印日志(通过反射代理、非IOC)

&#x1f388;个人主页&#xff1a;靓仔很忙i &#x1f4bb;B 站主页&#xff1a;&#x1f449;B站&#x1f448; &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 &#x1f917;收录专栏&#xff1a;C# &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff…

宝塔面板配置FTP服务结合内网穿透实现其他设备远程连接上传文件

文章目录 前言1. Linux安装Cpolar2. 创建FTP公网地址3. 宝塔FTP服务设置4. FTP服务远程连接小结 5. 固定FTP公网地址6. 固定FTP地址连接 前言 本文主要介绍宝塔FTP文件传输服务如何搭配内网穿透工具&#xff0c;实现随时随地远程连接局域网环境搭建的宝塔FTP文件服务并进行文件…

电商数据淘宝/京东/1688商品SKU数据采集||电商API接口

电商数据采集接口数据分析是一个涉及多个步骤的过程&#xff0c;以下是一个详细的指南&#xff1a; 一、数据采集接口的选择与接入 选择合适的电商数据采集接口&#xff1a; 根据需求选择提供所需数据的电商平台接口。考虑接口的稳定性、数据更新频率及准确性。 接口接入准备&…

《网络数据安全管理条例》正式公布,规范数据处理活动,保障网络数据安全

近日&#xff0c;《网络数据安全管理条例》&#xff08;以下简称《条例》&#xff09;正式公布&#xff0c;自2025年1月1日起施行。 《条例》旨在规范网络数据处理活动&#xff0c;保障网络数据安全&#xff0c;促进网络数据依法合理有效利用&#xff0c;保护个人、组织的合法权…