NewLife.XCode是一个有10多年历史的开源数据中间件,支持nfx/netstandard,由新生命团队(2002~2019)开发完成并维护至今,以下简称XCode。
整个系列教程会大量结合示例代码和运行日志来进行深入分析,蕴含多年开发经验于其中,代表作有百亿级大数据实时计算项目。
开源地址:https://github.com/NewLifeX/X (求star, 729 )
累加的需求背景
一个网站,部署了两台应用服务器,共用数据库,其中文章表有个访问次数的字段。
现在需要记录访问次数,需要怎么做?
代码语言:javascript复制var entity = Article.FindByID(9527);
entity.Views ;
entity.Update();
如果两台服务器都有用户访问了9527这篇文章,访问前Views是1000,访问后是多少?1002?大部分情况下是1002,少数情况下可能是1001。
如果每台服务器都有100个用户同时访问这篇文章呢?那可就精彩了,最后访问数可能是1001到1200之间的某个数。
按照教科书做法,我们似乎应该开个事务加个锁,确保同时只能有一个用户(线程)修改这一行数据。
且不说加锁和事务成本有多高,光是为了一个字段就锁住这一行导致用户无法更新这一行其它字段,就让人觉得挺不地道的。再者,访问次数对于其它字段来说,也许并没有那么重要。
聪明如你,可以想到这么一个办法:
代码语言:javascript复制update article set views=views 1 where id=9527
哈,这就是XCode增量累加的出发点,每个用户(线程)执行自己的那一次,不管排队先后,最终结果都将会是1200。
设置增量累加
在实体类静态构造函数中,可以设置需要增量累加的字段
向 Meta.Factory.AdditionalFields 添加需要采用增量累加的字段,执行update时才生成 x=x 123 样子的语句。
测试代码:
代码语言:javascript复制Update VisitStat Set Times=Times 123,Users=Users 1,IPs=IPs 1,UpdateTime='2019-03-26 22:36:14' Where ID=1
从输出效果看到,产生了累加效果。并且,这段代码不管执行多少次,都是这样的累加效果,而不管实际值是多少。
累加原理
从数据库查出来一个对象时,如果发现有设置累加字段,XCode会把此时的数据“备份” 下来。
在执行update保存的时候,拿累加字段的最后值减去原始备份值,得到差值(可能是负数),生成 x=x 123 或 x=x-456 的语句。
不光整数,小数也可以设置累加字段。
需要注意的是,如果字段x允许空,并且要更新行的x字段刚好为NULL,x=x 123 将会得不到预期效果。
高级用法
再看开头的例子,即使使用了累加,不需要加锁以及开事务,仍然需要update数据库200次。
借助累加以及异步保存功能,可以把这个次数大大降低。
代码语言:javascript复制var entity = Article.FindByID(9527);
entity.Views ;
entity.SaveAsync(5_000);
先把Views设为累加字段。
Article.FindByID内部可以用对象缓存,然后每台应用服务器在10秒(默认缓存时间)内多线程查到的都是同一个entity对象。
SaveAsync将把对象entity放入实体队列,5秒后延迟保存。如果200用户访问集中在5秒内,最后每台服务器只会执行一次update操作。
代码语言:javascript复制Update Article Set Viewss=Views 100 Where ID=9527
数据库写入次数由200次下降到2次,提升100倍。
由此,你能想到什么更有意思的用法了吗?
系列教程
NewLife.XCode教程系列2019版
- 增删改查入门。快速展现用法,代码配置连接字符串
- 数据模型文件。建立表格字段和索引,名字以及数据类型规范,推荐字段(时间,用户,IP)
- 实体类详解。数据类业务类,泛型基类,接口
- 功能设置。连接字符串,调试开关,SQL日志,慢日志,参数化,执行超时。代码与配置文件设置,连接字符串局部设置
- 反向工程。自动建立数据库数据表
- 数据初始化。InitData写入初始化数据
- 高级增删改。重载拦截,自增字段,Valid验证,实体模型(时间,用户,IP)
- 脏数据。如何产生,怎么利用
- 增量累加。高并发统计
- 事务处理。单表和多表,不同连接,多种写法
- 扩展属性。多表关联,Map映射
- 高级查询。复杂条件,分页,自定义扩展FieldItem,查总记录数,查汇总统计
- 数据层缓存。Sql缓存,更新机制
- 实体缓存。全表整理缓存,更新机制
- 对象缓存。字典缓存,适用用户等数据较多场景。
- 百亿级性能。字段精炼,索引完备,合理查询,充分利用缓存
- 实体工厂。元数据,通用处理程序
- 角色权限。Membership
- 导入导出。Xml,Json,二进制,网络或文件
- 分表分库。常见拆分逻辑
- 高级统计。聚合统计,分组统计
- 批量写入。批量插入,批量Upsert,异步保存
- 实体队列。写入级缓存,提升性能。
- 备份同步。备份数据,恢复数据,同步数据
- 数据服务。提供RPC接口服务,远程执行查询,例如SQLite网络版
- 大数据分析。ETL抽取,调度计算处理,结果持久化