覆盖Django mysql model中save方法时碰到的一个数据库更新延迟问题
最近有一个需求,通过django的admin后台,可以人工配置5张表的数据,这些数据进行一些业务规则处理后会统一成一份数据缓存在一个cache之中供服务端业务访问,因而任何一张表的数据更新(增、删、改),都要需要重新根据规则计算数据结果,并更新cache。
首先想到的方法就是覆盖每个表model子类中的save方法,在其中先调用父类的save方法走原有保存逻辑更新数据到数据库后,之后再单独调用一次cache的更新逻辑,这样每张表的任意数据被用户更新后,都将先触发model的数据库更新、而后执行cache的数据更新,其中Application表model的代码如下所示:
class Application(models.Model):
name = models.CharField(max_length=128, blank=False, verbose_name=u'应用名')
description = models.TextField(blank=False, verbose_name=u'应用描述')
status = models.IntegerField(verbose_name=u'状态', choices=APPLICATION_STATUS)
mtime = models.DateTimeField(blank=False, verbose_name=u'修改日期', auto_now=True)
ctime = models.DateTimeField(blank=False, verbose_name=u'创建日期', auto_now_add=True)
class Meta:
db_table = 'application'
verbose_name_plural = u'应用' def save(self, *args, **kargs):
super(Application, self).save(*args, **kargs)
# 更新memcached逻辑实现函数,该函数为通用函数一部分,会单独建立mysql连接,查询数据库数据,并更新到memcached
update_memcached_from_mysql()
这样每次在web上新增或者修改数据表记录时,都会先执行父类save操作,save完成后,又会执行update_memcached_from_mysql函数,从mysql查询到最新数据,而后更新到cache之中了。
然而实际测试的时候,发现每次修改数据时,更新到cache的并不是最新数据,而是未修改前的旧数据,比如当前name="test0",修改为name="test1"点击保存后,更新到cache之中的确还是test0,再次修改为name="test2",更新到cache之中的确实test1。
百思不得其解~怀疑是model执行save时,本地有cache会延迟更新,于是在super.save和update_memcached_from_mysql之间增加了time.sleep(10),并多次调用update_memcached_from_mysql函数,可是依然是每次修改保存时,更新到cache的数据都是修改前的取值:
def save(self, *args, **kargs):
super(Application, self).save(*args, **kargs)
# 更新memcached逻辑实现函数,该函数为通用函数一部分,会单独建立mysql连接,查询数据库数据,并更新到memcached
update_memcached_from_mysql()
time.sleep(10)
update_memcached_from_mysql()
time.sleep(10)
update_memcached_from_mysql()
想不出好的解决方案,猜测model真正将数据更新到数据的时机是在save整个函数执行结束后,臆测了如下更新逻辑:
1 子类save执行前
2 父类save执行
3 更新memcached
4 子类save执行结束
5 真正更新到数据库
于是必须想办法将第3步的cache更新逻辑挪到save执行结束后,然后要保证每次执行save操作时更新cache,这个位置又不能动~~
于是考虑通过开启独立线程异步执行的方式实现,改写update_memcached_from_mysql,在其中开启独立线程执行一个delay版本的更新函数,线程start后会先休眠n秒钟(n为可控参数,下例中为2),而后才执行从数据库读取数据并更新到cache的逻辑,改完后手动更新数据多次,验证已经能拉取到最新数据。
def update_memcached_from_mysql():
"""
猜测由于model的缓存机制,save函数执行完成前,新的数据可能未及时更新到数据库,
此处开启独立线程执行memcache更新操作,线程中会休眠数秒再从数据库拉取最新数据更新
"""
td = threading.Thread(target=update_memcached_from_mysql_delay, args=(2, ))
td.start()
然而之前的更新流程还仅仅是猜测而已,虽然采用线程异步延迟更新cache的方法后,多次修改验证避开了取不到新数据的问题,并不就说明猜测一定是正确的,而且即便猜测是正确的,如果save函数执行完后,model的数据更新没有在线程延迟时间结束前完成,理论上还是会有问题,考虑可以通过设置一个定时任务,比如每隔10分钟定时执行cache更新逻辑,来保证新数据最多延迟10分钟也能生效。
本来想深入探究model save更新机制~然而最近太忙了~~blog都两周没更新了,初步尝试了一下也还没有研究清楚这一块save逻辑的源码,这个数据修改平台也仅供内部使用~~暂时先这么修补一下~~以后有时间再深究这一块的问题~~加入TODO list。
覆盖Django mysql model中save方法时碰到的一个数据库更新延迟问题的更多相关文章
- django定义Model中的方法和属性
#定义一个Model class UserProfile(models.Model): user=models.OneToOneField(User,unique=True) phone=models ...
- backbone Model调用save方法的时候提交方式
horizon使用的是backbone框架,但是我们的后台api都是只接收post请求,请求的路径为/api/,根据backbone的官档解释: backbone的model.save方法会判断当前的 ...
- 误删Django的model中的表解决办法
误删Django的model中的表解决办法 1.model里面的表格实际的操作都在migrations文件夹中,里面记录了操作过程,当在database和model中删除表格时要注意初始化数据库时会报 ...
- Django 资源 与 知识 Django中自建脚本并使用Django环境 model中的save()方法说明 filter()用法
Django 资源 与 知识 Django中自建脚本并使用Django环境 model中的save()方法说明 filter()用法 2018/11/06 Chenxin 资料说明 Django基础入 ...
- Django model重写save方法及update踩坑记录
一个非常实用的小方法 试想一下,Django中如果我们想对保存进数据库的数据做校验,有哪些实现的方法? 我们可以在view中去处理,每当view接收请求,就对提交的数据做校验,校验不通过直接返回错误, ...
- CI中的控制器中要用model中的方法,是统一写在构造器方法中,还是在每一个方法中分别写
Q: CI中的控制器中要用model中的方法,是统一写在构造器方法中,还是在每一个方法中分别写 A: 建议统一写,CI框架会自动识别已经加载过的类,所以不用担心重复加载的问题 class C_User ...
- CakePHP采用model的save方法更新数据所需查询
采用model的save方法更新数据所需查询 1. 验证时候要确认是update 或者 create,以便使用对应规则 public $validate = array( 'field_name' = ...
- 使用List中remove方法时需要注意的问题
String str1 = new String("1"); String str2 = new String("2"); String str3 = new ...
- SQL把表中的数据复制到另一个数据库中
1 删除整张表的数据,并还原自增长值TRUNCATE TABLE TbWeixinActivity 2 3张表左连接select a.ID,c.Name,b.nickname,a.CreateDate ...
随机推荐
- poi导出excel出现本工作薄不能再使用其他新字体的解决方法
最近使用POI处理EXCEL,当处理的单元格太多时,就会出现,本工作薄使用字体过多,不能再使用其他新的字体的是提示. 网上很多方法告诉我,要怎么修改excel文件,但是这个解决不了问题啊,难道让客户去 ...
- centos7安装docker-ce新版
先卸载系统的旧版本yum remove docker \ docker-common \ docker-selinux \ ...
- python异常处理及内置模块
异常处理 有时候我们在写程序的时候会出现错误或者异常,导致程序终止,如下这个例子: #!/usr/bin/env python a = 2/0 print(a) 结果提示如下错误: Traceback ...
- 记录智能指针使用shared_ptr使用错误
shared_ptr为智能指针,今天一次在使用shared_ptr时,错误的将其初始化方式写为shared_ptr<T> test = shared_ptr<T>(),随后导致 ...
- android--eclipse闪退,无法启动
解决方法: 删除文件:[workspace]/.metadata/.plugins/org.eclipse.e4.workbench/workbench.xmi
- Jenkins获取编译状态
背景:在通过python的API调用Jenkins,启动Jenkins的job任务时,是需要知道Jenkins的编译状态,获取编译状态为 status=server.get_build_info(jo ...
- FZU-1608 Huge Mission 线段树(更新懒惰标记)
题目链接: https://cn.vjudge.net/problem/FZU-1608 题目大意: 长度n,m次操作:每次操作都有三个数:a,b,c:意味着(a,b]区间单位长度的价值为c,若某段长 ...
- java复制文件夹及所有子目录和文件
package text; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; im ...
- gluoncv rpn 正负样本
https://github.com/dmlc/gluon-cv/blob/master/gluoncv/model_zoo/rpn/rpn_target.py def forward(self, i ...
- Python KafkaProducer and KafkaConsumer的开发模块
1.在python中往kakfa写数据和读取数据,使用的是python-kafka库 2.消费者需持续写入数据,因groupid存在偏移量,才能看看到数据. 3.安装库的命令为pip install ...