一个基于mysql构建的队列表
通常大家都会使用redis作为应用的任务队列表,redis的List结构,在一段进行任务的插入,在另一端进行任务的提取。
任务的插入
$redis->lPush("key:task:list",$task);
任务的提取
$tasks = $redis->RPop("key:task:list",0,-1);
可是大家想,如何使用mysql来实现一个队列表呢?
映入大家脑海的一个典型的模式是一个表包含多种类型的记录:未处理记录,已处理记录,正在处理记录等。一个或者多个消费者线程在表中查询未处理的记录,然后声称正在处理这个任务,处理完成之后,再讲记录更新为已处理状态。
这个典型的模式,存在两个问题;1:随着队列表越来越大,查找未处理记录的速度会越来越慢。2:频繁的加锁会让多个消费者线程增加竞争。
首先我们来创建一个表
create table unsent_emails{
id int not null primary key auto_increment,
status enum("unsent","claimed","sent"),
owner int unsigned not null default 0,
ts timestamp,
key (owner,status,ts)
};
该表的列owner用来存储当前正在处理这个记录的连接id,由函数 CONNECTION_ID()返回的连接id或者线程id。如果这个记录当前被没有被处理,则该值为0
我们在 owner status ts上面做了索引的处理,所以查找未处理的记录会很快。
通过我们会采用 select for update的方式来标记待处理的记录,方法如下
begin;
select id from unsent_emails
where owner = 0 and status = 'unsent'
limit 10 for update;
-- result 10,20,33
update unsent_emails
set status = 'claimed',owner = CONNECTION_ID()
where id in (10,20,33);
commit;
select的时候,使用了两个索引,应该会很快。问题出在select 和 update两个查询之间的间隙,这里的加锁会让其他相同的查询全部阻塞。
如果我们采用update then select的方式,那么效果就会更加高效,代码如下
set autocommit=1;
commit;
update unsent_emails
set statue = 'claimed',owner = CONNECTION_ID()
where owner = 0 and status = 'unsent'
limit 10;
set autocommit=0;
select id from unsent_emails
where owner = CONNECTION_ID() and status = 'claimed';
根本无需使用select去查找哪些记录还没有处理。客户端协议会告诉你更新了几条记录,就可以知道这次需要处理多少条记录。
这样是不是解决了上面的第二个问题,select for update的模式的加锁会增加多个消费队列的竞争问题。
其实所有的select for update 都可以替换为 update then select模式。
问题还没有结束,还有一种情况需要处理,就是比如正在处理任务的进程异常退出了,那么对应的进程正在处理的任务也就变为僵尸任务了。如何避免这种情况的发生呢?
所以我们还是需要一个新的定时器或者线程来定时检测并且update,将那些僵尸任务的记录更新到原始状态,就可以了。
僵尸任务的定义必须符合两点,1:任务被搁置了很久,比如十分钟,而通常一个任务只需要10秒就可以处理完;2:任务的owner(线程id或者连接id)已经不存在,只需要执行show processlist就可以获取当前正在工作的线程id了。代码如下
update unsent_emails
set owner = 0,status = 'unsent'
where owner not in (10,20,33,44) and status = 'claimed'
and ts < current_timestamp - interval 10 minute;
一个基于mysql构建的队列表就完成了。
当然,最好的办法就是将任务队列从数据库中迁移出来。redis真是一个很好的队列容器,当然也可以使用ssdb(基于leveldb,内存占用更少)。
读 高性能mysql 笔记
一个基于mysql构建的队列表的更多相关文章
- 一个基于Asterisk构建的VOIP应用软件:Elastix介绍
Elastix 是一种应用软件,它整合了适用于那些基于 Asterisk 的 PBX 的最好工具,并将它们集成为单一的.易用的接口.同时,它增加了自己的工具集,以及允许创建第三方模块来使 Elasti ...
- 如何设计一个基于mysql的消息系统
https://segmentfault.com/a/1190000012255186
- 基于MySQL协议的数据库中间层项目Atlas - 360团队
一.简介 Atlas是由 Qihoo 360公司Web平台部基础架构团队开发维护的一个基于MySQL协议的数据中间层项目.它在MySQL官方推出的MySQL-Proxy 0.8.2版本的基础上,修改了 ...
- 利用Dockerfile构建一个基于CentOS 7镜像
利用Dockerfile构建一个基于CentOS 7,包括java 8, tomcat 7,php ,mysql+mycat的镜像. Dockerfile内容如下: FROM centosMAINTA ...
- 开源低代码平台开发实践二:从 0 构建一个基于 ER 图的低代码后端
前后端分离了! 第一次知道这个事情的时候,内心是困惑的. 前端都出去搞 SPA,SEO 们同意吗? 后来,SSR 来了. 他说:"SEO 们同意了!" 任何人的反对,都没用了,时代 ...
- 使用 XMPP 构建一个基于 web 的通知工具——转
Inserting of file(使用 XMPP 构建一个基于 web 的通知工具.docx) failed. Please try again. http://www.ibm.com/develo ...
- 构建一个基于 Spring 的 RESTful Web Service
本文详细介绍了基于Spring创建一个“hello world” RESTful web service工程的步骤. 目标 构建一个service,接收如下HTTP GET请求: http://loc ...
- 一个基于php+mysql的外卖订餐网站(带源码)
订饭组 一个基于php+mysql的外卖订餐网站,包括前端和后台.源码地址 源码演示地址:http://dingfanzu.com 商家后台系统:http://dingfanzu.com/admin ...
- Spring MVC第一课:用IDEA构建一个基于Spring MVC, Hibernate, My SQL的Maven项目
作为一个Spring MVC新手最基本的功夫就是学会如何使用开发工具创建一个完整的Spring MVC项目,本文站在一个新手的角度讲述如何一步一步创建一个基于Spring MVC, Hibernate ...
随机推荐
- 《Linux内核设计与实现》读书笔记 第三章 进程管理
第三章进程管理 进程是Unix操作系统抽象概念中最基本的一种.我们拥有操作系统就是为了运行用户程序,因此,进程管理就是所有操作系统的心脏所在. 3.1进程 概念: 进程:处于执行期的程序.但不仅局限于 ...
- 开源消息队列:NetMQ
NetMQ 是 ZeroMQ的C#移植版本. ZeroMQ是一个轻量级的消息内核,它是对标准socket接口的扩展.它提供了一种异步消息队列,多消息模式,消息过滤(订阅),对多种传输协议的无缝访问. ...
- 在Visual Studio中将现有.NET Framework项目迁移至.NET Core 1.1 Preview 1
1)下载安装包含 .NET Core 1.1 Preview 1 的 SDK:Windows x64 安装包(下载地址列表) 2)下载最新 VS 2015 NuGet 插件:https://dist. ...
- windows下 安装Kali Linux到 U盘的方法
作者:玄魂工作室 \ 2016年10月20日 把Kali Linux安装到U盘好处很多,可以从U盘启动使用整个电脑的硬件资源, 可以随身携带,减少对自己电脑的影响. 今天要给大家讲的是如何在windo ...
- [.net 面向对象程序设计进阶] (15) 缓存(Cache)(二) 利用缓存提升程序性能
[.net 面向对象程序设计进阶] (15) 缓存(Cache)(二) 利用缓存提升程序性能 本节导读: 上节说了缓存是以空间来换取时间的技术,介绍了客户端缓存和两种常用服务器缓布,本节主要介绍一种. ...
- Javascript本质第一篇:核心概念
很多人在使用Javascript之前都至少使用过C++.C#或Java,面向对象的编程思想已经根深蒂固,恰好Javascript在语法上借鉴了Java,虽然方便了Javascript的入门,但要深入理 ...
- Eclipse 调试技巧
条件断点 顾名思义,是指当发生某种情况或者触发某种条件的情况下命中断点.常用的情形就是for循环中某个变量为xx值的时候命中断点类似的. 做法1:在debug视图中,BreakPoint View将所 ...
- Js replace() 学习笔记
最近捣鼓着学习Js,发现replace()真的很有用,替换功能杠杠的棒. 接下来看看我遇到的问题: 有两个随机给出的字符串,字符串1'xxxxxx',字符串2'====T'(这两个用作示例,其他为随机 ...
- [汇编与C语言关系]1.函数调用
对于以下程序: int bar(int c, int d) { int e = c + d; return e; } int foo(int a, int b) { return bar(a, b); ...
- RequireJs调研
背景 Problem(问题) Web sites are turning into Web apps(网站正转变为网络应用程序) Code complexity grows as the site g ...