Skip to content

Latest commit

 

History

History
 
 

Cache

缓存架构设计

核心原则:读多写少的数据,缓存起来,减少数据库访问,提升性能

数据表行数缓存

数据表行数缓存Meta.Count代表了相对准确的表行数,在1000行以内精确,1000行以上从索引表查询,精确度由数据库保证。
多年使用经验来看,SQLite/MSSQL/MySql/Oracle索引表里面的表行数相当准确
查询数据表行数一般用Student.FindCount(where),还可以指定条件。
实际使用中,用得最多的是查整表行数,供分页等业务逻辑使用。
FindCount()执行的是Select Count(*) From Table,在很多没有预热的大数据表(百万千万级)里面,该语句执行起来非常慢,往往需要超过1秒的时间,用户体验很不好。
快速查询QueryCountFast从索引表读取表行数。
本框架内,大量使用表行数来决定不同的优化策略。

实体缓存

实体缓存就是整表缓存那些读取很多修改极少的数据,用于系统参数表、栏目分类表等。
实体类内使用'Meta.Cache.Entities'即可触发使用实体缓存,内部将执行一次查询('Select * From Table')加载整表数据为实体列表。
Meta.Cache.Entities就是这个实体列表,使用缓存实际上就是在这个列表上执行Find/FindAll操作。
基于性能考虑,建议单表数据小于1000行时使用,大于10000时坚决不要使用。

工具生成的实体业务类代码经常可以看到如下代码:

public Student FindByID(Int32 id)
{
    if (id <= 0) return null;

    if (Meta.Count >= 1000)
        return Find(__.ID, id);
    else // 实体缓存
        return Meta.Cache.Entities.Find(__.ID, id);
}

因此,FindByID在该表数据小于1000时,其实是使用实体缓存。

缓存默认过期时间60秒,过期后使用仍然是马上返回旧数据,同时开启异步查询更新缓存。
任何添删改等改动数据库的操作,都将会让缓存马上过期,并启动异步更新。
任何添删改操作,都将实时修改缓存,即使在异步更新完成之前,从缓存拿到的也是最新数据。除非有其它进程更新了数据表,此时需要等缓存的异步更新操作完成才能得到最新数据。

单对象缓存

单对象缓存就是以主键字典逐行缓存数据,常用于需要单行数据查询的场合,比如用户表等。
可以看做是一个字典,以主键为Key,实体对象为Value。每次查询先在字典里面搜索,找到则返回,找不到则去数据库查,缓存起来后再返回。
实体类内使用Meta.SingleCache[key]即可启用单对象缓存。

public Student FindByID(Int32 id)
{
    if (id <= 0) return null;

    return Meta.SingleCache[id];
}

缓存默认过期时间60秒,过期后更新。
任何添删改等改动数据库的操作,都将会清空缓存。

业务代码->对象缓存: 主键
note over 对象缓存: 缓存不存在
对象缓存-->>数据库: 查询数据库
note over 对象缓存: 已存在
对象缓存->业务代码: 返回对象
note over 对象缓存: 已过期
对象缓存-->>数据库: 异步更新

定时器-->>对象缓存: 清理过期
业务代码->对象缓存: 主键
note over 对象缓存: 缓存不存在
对象缓存-->>数据库: FindKey
数据库-->>对象缓存: 更新主键
数据库-->>对象缓存: 更新从键
业务代码->对象缓存: 从键
note over 对象缓存: 缓存不存在
对象缓存-->>数据库: FindSlaveKey
数据库-->>对象缓存: 更新主键
数据库-->>对象缓存: 更新从键

缓存更新策略

在没有使用事务时,对数据表的任何添删改,将会让该表的实体缓存马上过期,以及清空单对象缓存。
使用事务时,每一个添删改操作仅修改缓存,直到事务提交或回滚才清空缓存。

特别优化

SQLite没有索引表供快速查询表行数,而直接Select Count又慢,因此框架针对SQLite进行特别优化。
获取表行数时,如果有自增字段,首先获取其最大值临时充当表行数,然后启动异步查询Select Count以获取精确的表行数。
因此,SQLite使用Meta.Count时,第一次得到的数据可能有偏差,一会后即可得到精确数据。时间的长度主要由数据表大小决定,一百万数据大概需要几百毫秒。该偏差完全可以通过系统启动时进行系统预热来对冲掉。