typedef struct sqlite3_pcache_methods2 sqlite3_pcache_methods2; struct sqlite3_pcache_methods2 { int iVersion; void *pArg; int (*xInit)(void*); void (*xShutdown)(void*); sqlite3_pcache *(*xCreate)(int szPage, int szExtra, int bPurgeable); void (*xCachesize)(sqlite3_pcache*, int nCachesize); int (*xPagecount)(sqlite3_pcache*); sqlite3_pcache_page *(*xFetch)(sqlite3_pcache*, unsigned key, int createFlag); void (*xUnpin)(sqlite3_pcache*, sqlite3_pcache_page*, int discard); void (*xRekey)(sqlite3_pcache*, sqlite3_pcache_page*, unsigned oldKey, unsigned newKey); void (*xTruncate)(sqlite3_pcache*, unsigned iLimit); void (*xDestroy)(sqlite3_pcache*); void (*xShrink)(sqlite3_pcache*); };
通过传递一个 sqlite3_pcache_methods2 结构的实例到 sqlite3_config(SQLITE_CONFIG_PCACHE2, ...) 接口,可以注册一个替代的页面缓存实现。在许多应用程序中,SQLite 分配的大部分堆内存用于页面缓存。通过使用此 API 实现自定义页面缓存,应用程序可以更好地控制 SQLite 消耗的内存量、内存分配和释放的方式,以及用于确定数据库文件哪些部分被缓存以及缓存多长时间的策略。
替代页面缓存机制是一种极端措施,只有在最苛刻的应用程序中才需要使用。对于大多数用途,建议使用内置的页面缓存。
在调用 sqlite3_config 时,SQLite 会将 sqlite3_pcache_methods2 结构的内容复制到内部缓冲区。因此,应用程序可以在 sqlite3_config() 返回后丢弃参数。
对于每次对 sqlite3_initialize() 的有效调用(通常在进程的生命周期中只调用一次),都会调用 xInit() 方法一次。xInit() 方法会传递 sqlite3_pcache_methods2.pArg 值的副本。xInit() 方法的目的是设置自定义页面缓存实现所需的全局数据结构。如果 xInit() 方法为 NULL,则使用内置的默认页面缓存,而不是应用程序定义的页面缓存。
xShutdown() 方法由 sqlite3_shutdown() 调用。如果需要,它可以用来在进程关闭之前清理所有未完成的资源。xShutdown() 方法可以为 NULL。
SQLite 会自动序列化对 xInit 方法的调用,因此 xInit 方法不需要是线程安全的。xShutdown 方法只从 sqlite3_shutdown() 调用,因此它也不需要是线程安全的。在多线程应用程序中,所有其他方法都必须是线程安全的。
SQLite 永远不会在调用 xShutdown() 之前调用 xInit() 多次。
SQLite 调用 xCreate() 方法来构建一个新的缓存实例。SQLite 通常会为每个打开的数据库文件创建一个缓存实例,但这并不保证。第一个参数 szPage 是缓存必须分配的页面的大小(以字节为单位)。szPage 将始终是 2 的幂。第二个参数 szExtra 是与每个页面缓存条目关联的额外存储字节数。szExtra 参数将是一个小于 250 的数字。SQLite 将使用每个页面上的额外 szExtra 字节来存储有关磁盘上基础数据库页面的元数据。传递到 szExtra 的值取决于 SQLite 版本、目标平台以及 SQLite 的编译方式。xCreate() 的第三个参数 bPurgeable 为 true,如果创建的缓存用于缓存存储在磁盘上的文件的数据库页面,或者为 false,如果它用于内存数据库。缓存实现不需要根据 bPurgeable 的值做任何特殊的事情;它纯粹是建议性的。在 bPurgeable 为 false 的缓存上,SQLite 永远不会调用 xUnpin(),除非故意删除页面。换句话说,对 bPurgeable 设置为 false 的缓存的 xUnpin() 的调用将始终将“discard”标志设置为 true。因此,用 bPurgeable 设置为 false 创建的缓存将永远不会包含任何未固定页面。
xCachesize() 方法可以随时由 SQLite 调用,以设置传递给第一个参数的缓存实例的建议最大缓存大小(存储的页面数)。这是使用 SQLite "PRAGMA cache_size" 命令配置的值。与 bPurgeable 参数一样,实现不需要对该值做任何事情;它只是建议性的。
xPagecount() 方法必须返回当前存储在缓存中的页面数量,包括固定页面和未固定页面。
xFetch() 方法在缓存中定位一个页面,并返回一个指向与该页面关联的 sqlite3_pcache_page 对象的指针,或者返回一个 NULL 指针。返回的 sqlite3_pcache_page 对象的 pBuf 元素将是一个指向用于存储单个数据库页面内容的 szPage 字节缓冲区的指针。sqlite3_pcache_page 的 pExtra 元素将是一个指向 SQLite 为页面缓存中的每个条目请求的 szExtra 字节额外存储的指针。
要获取的页面由键决定。最小键值为 1。在使用 xFetch 获取页面之后,页面被视为“固定”。
如果请求的页面已经在页面缓存中,则页面缓存实现必须返回一个指向包含其内容的页面缓冲区的指针。如果请求的页面不在缓存中,则缓存实现应该使用 createFlag 参数的值来帮助它确定要采取的操作。
createFlag | 页面不在缓存中的行为 |
---|---|
0 | 不要分配新页面。返回 NULL。 |
1 | 如果这样做容易且方便,则分配新页面。否则返回 NULL。 |
2 | 尽一切努力分配新页面。只有在分配新页面实际上不可能时才返回 NULL。 |
SQLite 通常会使用 0 或 1 的 createFlag 调用 xFetch()。SQLite 只有在之前使用 1 的 createFlag 调用失败后才会使用 2 的 createFlag。在 xFetch() 调用之间,SQLite 可能会尝试通过将固定页面的内容溢出到磁盘并同步操作系统磁盘缓存来取消固定一个或多个缓存页面。
xUnpin() 由 SQLite 调用,并以指向当前固定页面的指针作为其第二个参数。如果第三个参数 discard 为非零,则必须将该页面从缓存中逐出。如果 discard 参数为零,则页面可以根据页面缓存实现的意愿被逐出或保留。页面缓存实现可以选择随时逐出未固定页面。
缓存不能执行任何引用计数。对 xUnpin() 的单次调用会取消固定页面,而不管之前调用 xFetch() 的次数如何。
xRekey() 方法用于更改与作为第二个参数传递的页面关联的键值。如果缓存之前包含与 newKey 关联的条目,则必须将其丢弃。任何以前与 newKey 关联的缓存条目都保证不会被固定。
当 SQLite 调用 xTruncate() 方法时,缓存必须丢弃所有现有缓存条目,这些条目的页面号(键)大于或等于传递给 xTruncate() 的 iLimit 参数的值。如果这些页面中的任何一个被固定,它们将被隐式取消固定,这意味着它们可以安全地被丢弃。
xDestroy() 方法用于删除由 xCreate() 分配的缓存。应释放与指定缓存关联的所有资源。在调用 xDestroy() 方法后,SQLite 将认为 sqlite3_pcache* 句柄无效,并且不会将其与任何其他 sqlite3_pcache_methods2 函数一起使用。
当 SQLite 希望页面缓存释放尽可能多的堆内存时,它会调用 xShrink() 方法。页面缓存实现没有义务释放任何内存,但行为良好的实现应该尽力而为。