覆盖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 ...
随机推荐
- python中的装饰
在面向对象(OOP)的设计模式中,decorator被称为装饰模式.OOP的装饰模式需要通过继承和组合来实现,而Python除了能支持OOP的decorator外,直接从语法层次支持decorator ...
- 「bzoj 4025: 二分图」
题目 显然二分图没有奇环 于是考虑使用并查集维护一下看看是否存在奇环 我们可以考虑加权并查集,维护出\(x\)到\(fa_x\)的实际距离 由于我们只需要考虑奇偶性,于是我们处理出到根的路径异或一下就 ...
- ThinkPhp表单令牌和字段映射
开启表单令牌: 先在配置文件目录下定义tags.php 在里面添加 return array( 'view_filter' =>array('Behavior\TokenBuildBehavio ...
- yum 安装 php5.6
yum 安装 php5.6 安装 PHP rpm -Uvh http://ftp.iij.ad.jp/pub/linux/fedora/epel/6/i386/epel-release-6-8.noa ...
- 高斯消元求主元——模意义下的消元cf1155E
#include <bits/stdc++.h> , MO = ; ; inline int qpow(int a, int b) { ; while(b) { ) { ans = 1ll ...
- java 常用类2
1.1 日期时间类 时间戳(timestamp):距离特定时间的时间间隔. 计算机时间戳是指距离历元(1970-01-01 00:00:00:000)的时间间隔(ms). 计算机中时间2019-04- ...
- JNI由浅入深_5_基本类型应用
1.基本类型应用 对于JNI处理基本类型还是比较简单的,下面是Java代码: <span style="font-size:14px;"> public native ...
- Java中的集合和常用类
Java中的常用类: ▪ Object类 ▪ Math类 ▪ String类和StringBuffer类(字符串) ▪ 8种基本类型所对应的包装类 ▪ java.util包中的类——Date类 Obj ...
- 百度地图中找不到BMap的解决
一般情况下是引用的问题,产生的原因大概有两种 1.不同架构的引用方式不同,引用js的方式不同导致 2.自身调用顺序有误 官方的引用方式是使用标签引入,示例 <script type=" ...
- Java类和对象详解,以及相关知识点
了解类和对象前,简单提及面向对象程序设计.面向对象程序设计就是通过对象来进行程序设计,对象表示一个可以明确标识的实体.例如:一个人.一本书.一个学校或一台电脑等等.每个对象都有自己独特的标识.状态和行 ...