本文主要知识点:

  1.使用Django的signals来获取Model的新建/删除操作更新

  2.使用数据库的select for update来正确处理并发的数据库操作

  3.使用redis的sorted set来缓存计数器的修改操作

一,起始

  我们要实现消息未读记录,常规做法:

定义一个类,当用户有一条消息未读,就生成一条记录。你可以通过以下方式获取指定用户未读消息数目:

当你的Notification表比较小的时候,这样的方式没有任何问题。但是随着业务的增大,表中的数据可能有上亿条记录。那么随之用户的未读消息数,也会有所提高。

  这时候,你就需要实现一个计数器,让这个计数器来统计每个用户的未读消息数,这样 比起之前的 count() ,我们只需要执行一条简单的主键查询(或者更优)就可以拿到实时的未读消息数了。

二,优化

  首先 ,我们得建立一个新表来存储每个用户的未读消息数。

我们为每个注册的用户提供一条对应的记录,来保存用户的未读消息数。

那么重点就是,我们如何知道什么时候应该更新unread_count呢?

这就要说到Django给我提供的强大的信号机制,从而方便我们实时检测计数器。

为了实时更新我们的计数器,我们必须做到以下几点:

1.当有新的未读消息过来的时候,计数器+1

2.当消息被异常删除时,如果关联的消息未读,为计数器-1

3.当阅读完一个新消息的时候,计数器-1

django.db.models.signals.pre_save & django.db.models.signals.post_save 表示的是 某个Model调用save方法之前和之后会触发的事件

现在,我们利用Django提供的两个信号机制来实现我们的实时更新计数器。

1.当有新的未读消息过来的时候,计数器+1

这样,每当你使用Notification.create或者.save()之类的方法创建新通知时,我们的 NotificationController 便会得到通知,为计数器 +1。

2.当消息被异常删除时,如果关联的消息为未读,为计数器 -1

3. 当阅读一个新消息的时候,为计数器 -1

  接下来,当用户阅读某条未读消息的时候,我们也需要更新我们的未读消息计数器。 你可能会说,这有什么难的?我只要在我的阅读消息的方法里面,手动更新我的计数器不就好了?

  但是,这样的实现方式有一个 非常致命的问题, 这个方式没有办法正常处理并发的请求。

  例如,你拥有一个id为100的未读消息对象,这个时候同时有了两个请求过来,都要标记这个通知为已读

显而易见的,这两次方法都会成功的标记这条通知为已读,因为在并发的情况下, if notification.has_readed 这样的检查无法正常工作,所以我们的计数器将会被错误的 -1 两次 ,但其实我们只读了一条请求。

那么,这样的问题应该怎么解决呢?

基本上,解决并发请求产生的数据冲突只有一个办法: 加锁 ,介绍两种比较简单的解决方案:

使用 select for update 数据库查询

select ... for update 是数据库层面上专门用来解决并发取数据后再修改的场景的,主流的关系数据库 比如mysql、postgresql都支持这个功能, 新版的Django ORM甚至直接提供了这个功能的shortcut 。 关于它的更多介绍,你可以搜索你使用的数据库的介绍文档。

使用 select for update 后,我们的代码可能会变成这样:

除了使用``select for update``这样的功能,还有一个比较简单的办法来解决这个问题。

使用update来实现原子性修改

其实,更简单的办法,只要把我们的数据库改成单条的update就可以解决并发情况下的问题了:

这样,并发的标记已读操作也可以正确的影响到我们的计数器了。

三,高性能

我们可能会直接使用UPDATE 语句来修改我们的计数器,就像这样:

但是在生产环境中,这样的处理方式很有可能造成严重的性能问题,因为如果我们的计数器在频繁 更新的话,海量的Update会给数据库造成不小的压力。所以为了实现一个高性能的计数器,我们 需要把改动暂存起来,然后批量写入到数据库。

使用 redis 的 sorted set ,我们可以非常轻松的做到这一点。

使用sorted set来缓存计数器改动

redis是一个非常好用的内存数据库,其中的sorted set是它提供的一种数据类型:有序集合, 使用它,我们可以非常简单的缓存所有的计数器改动,然后批量回写到数据库。

通过以上的代码,我们把计数器的更新缓冲在了redis里面,我们还需要一个脚本来把这个缓冲区 里面的数据定时回写到数据库中。

通过自定义django的command,我们可以非常轻松的做到这一点:

之后,通过 python manage.py notification_update_counter 这样的命令就可以把缓冲区 里面的改动批量回写到数据库了。我们还可以把这个命令配置到crontab中来定义执行。

利用django信号实现计数功能相关推荐

  1. django restful 请求_利用 Django REST framework 构建 RESTful Web API

    利用 Django REST framework 构建 RESTful Web API 终于到了动手操作的环节啦,这一节,我们以师生管理系统为例,带领大家搭建一套 framework Web API. ...

  2. 创建一个delete触发器_基于 Django 信号机制实现类似触发器的效果

    我们都知道,在关系数据库中,为了保证数据完整性,我们都会使用一个叫做触发器的玩意.今天我就基于Django信号机制实现类似触发器的效果,在此之前我先简单介绍一下触发器. 触发器 触发器(trigger ...

  3. 利用Django生成PDF文档

    利用Django生成PDF文档 本小节说明如何利用Django视图动态生成PDF文档.此功能是由优秀的开源Python PDF库ReportLab实现的. 动态生成PDF文档的好处在于你可以定制PDF ...

  4. 利用STM32的定时器中断功能编写线阵CCD(ILX554B)的驱动时序

    利用STM32的定时器中断功能编写线阵CCD(ILX554B)的驱动时序 1.利用定时器完成线阵CCDILX554B的驱动时序,采用两个管脚1个用于产生ROG信号,一个用于产生CLK信号 2.使用一个 ...

  5. 通过python利用Django框架搭建一个属于自己的免费网站(已更新)

    大家好,我是天空之城,今天给大家带来,通过python利用Django框架搭建一个属于自己的免费网站. 以下是网上找到的免费教程,https://djangogirlstaipei.gitbooks. ...

  6. Qt实战案例(42)——利用Qt实现自定义标题栏功能(自定义最大化、最小化、关闭等功能)

    目录 一.项目介绍(为什么要自自定义标题栏功能) 二.项目基本配置 三.UI界面设计 四.主程序实现 4.1 隐藏边框 4.2 设置最大化(还原),最小化,关闭按钮的样式风格 4.3 关闭按钮的实现 ...

  7. python middleware模块_详解利用django中间件django.middleware.csrf.CsrfViewMiddleware防止csrf攻击...

    一.在django后台处理 1.将django的setting中的加入django.contrib.messages.middleware.MessageMiddleware,一般新建的django项 ...

  8. php可以打印一个页面,利用html实现分页打印功能的实例详解

    本篇介绍利用html实现分页打印功能的实例详解,有些不想打印出来的分页打印的都可以应用这类样式进行控制 在非打印时是无效的. 页面打印 /* 应用这个样式的在打印时隐藏 */ .noPrint { d ...

  9. redisson的锁的类型_绝对干货:利用redisson完成分布式锁功能

    在单体架构中,我们使用synchronize或者Lock就能完成上锁同步的操作,但是这些在分布式,微服务的今天,失去了作用. 分布式锁的实现一般有三种解决方案:基于数据库表实现 基于缓存实现,比如re ...

最新文章

  1. java-类(class)继承,重写,重构,抽象,接口等
  2. rs232读取智能电表_老王说表之--快速查看家里电表读数的方法
  3. mongoDB中的数据类型
  4. 弹出框设置在页面居中
  5. Java必考题目之JVM面试题目和答案
  6. python自定义异常捕获_python怎么自定义捕获错误
  7. 我对 SRE 的理解
  8. 也谈POV旋转LED制作,经验及技术点。
  9. hd Aruba wifi / honor
  10. 华为VLAN隔离配置
  11. 因子分析法(Factor Analysis)是什么分析
  12. 百度没有柳传志,联想没有李彦宏
  13. 当物流行业遇见MongoDB
  14. 关于Android自启动管理的相关知识点
  15. Python Tkinter 下载器 多任务下载+多线程下载+多任务断点续存
  16. 微信小程序单页开发之 min-cli
  17. eclipse 显示中文或者英文
  18. 华为MateBook16投屏设置
  19. 动画项目中问题及解决方案(V客学院知识分享)
  20. M3u8或者ts协议的直播流视频,如何通过加密防止盗链和盗播?

热门文章

  1. 2556. 【NOIP2011模拟9.7】伊吹萃香 (Standard IO)
  2. nginx 手机版页面判断_Nginx if 条件判断
  3. 用python表白代码_如何用Python代码向心爱的姑娘花式表白?
  4. 沈孝钧计算机算法基础答案,计算机算法基础教学课件ppt作者沈孝钧第14章-PPT-N2课件.pptx...
  5. Leetcode 宝石与石头 C++
  6. 实现必填项后添加红色星号*
  7. 提高总账和日记账导入的性能
  8. 函授大专和成人大专哪个含金量更高
  9. arduino 串口监视器数据传到本地txt文件
  10. javaScript 实现元素匀速运动 元素缓速运动(先快后慢)