Redis系列二之事务及消息通知
一、事务
Redis中的事务是一组命令的集合。一个事务中的命令要么都执行,要么都不执行。
1、事务简介
事务的原理是先将一个事务的命令发送给Redis,然后再让Redis依次执行这些命令。下面看一个示例:

首先,使用multi命令告诉Redis:下面我给你的命令属于同一个事务,你先不要执行,而是暂时存起来。
然后,我们发送两个set命令来实现赋值,可以看到redis没有执行这些命令,而是返回queued表示这两条命令已经进入等待执行的事务队列中。
当所有要在同一事务中执行的命令都发给Redis后,用exec命令告诉Redis将等待执行的事务队列中所有的命令按照发送顺序依次执行。
2、错误处理
当一个事务中某个命令执行出错时,Redis会怎样处理呢?这里有二种情况:
情况一:语法错误,指错命令不存在或者命令参数的个数不对

可以看到,跟在multi命令后执行了三个命令,第二个和第三个命令有语法错误,执行exec后redis就会执行返回错误。
情况二:运行错误,指在命令执行时出现的错误。

这种错误在实际执行前redis是无法发现的,所以在事务里这样的命令是会被redis接受和执行的,如果事务里一条命令出现了运行错误,事务里其他的命令依然会继续执行(包括出错命令之后的命令)。
注意:Redis事务没有关系型数据库提供的回滚功能。
3、Watch命令
watch命令可以监控一个或多个键,一旦其中一个键被修改或删除,之后的事务就不会执行,监控一直持续到exec命令(但是不能保证其他客户不修改这一键值)。

上例中执行watch命令后,事务执行前修改了key值,所以事务中命令set username xujian没有执行,exec命令返回空结果。
注意:执行exec命令后立即取消对所有键的监控,如果不想执行事务中的命令也可以使用unwatch命令来保证下一个事务的执行不会受到影响。
二、生存时间
在Redis中,可以使用expire命令设置一个键的生存时间,到时间后Redis会自动删除它。
expire命令的使用方法为expire key seconds,其中seconds参数表示键的生存时间,单位是秒。如果想知道一个键还有多久的时间会被删除,可以使用TTL命令,返回值是键的剩余时间(单位是秒)。如果想取消键的生存时间设置,即将恢复永久的,可以使用persist命令。

三、排序
1、有序集合
有序集合常见的使用场景是大数据排序,如游戏的玩家排行榜。
2、sort命令
sort命令可以对列表类型、集合类型和有序集合类型键进行排序,并且可以完成与关系数据库中的连接查询相类似的任务。
对List进行排序:

对有序集合类型排序时会忽略元素的分数,只针对元素自身的值进行排序:

3、by参数
by参数的语法为“by 参考键”,其中参考键可以是字符串类型键或者是散列类型键的某个字段。如果提供了by参数,sort命令将不再依据元素自身的值进行排序,而是对每个元素使用元素的值替换参考键中的第一个“*”并获取其值,然后依据该值对元素排序:

4、store参数
默认情况下,sort会直接返回排序结果,如果希望保存排序结果,可以使用store参数。
注意:sort命令的时间复杂度是O(n+mlogm),其中n表示要排序的列表(集合或有序集合)中的元素个数,m表示要返回的元素的个数。当n较大的时候sort命令的性能相对较低,并且redis在排序前会建立一个长度为n的容器来存储待排序的元素。在开发中使用sort要注意以下几点:
1、尽可能减少待排序键中元素的数量(使n尽可能小)
2、使用limit参数只获取需要的数据(使m尽可能小)
3、如果要排序的数据量较大,尽可能使用store参数将结果缓存
四、消息通知
一般来说,消息队列有两种场景,一种是生产者消费者模式,一种是发布者订阅者模式。利用redis这两种场景的消息队列都能实现。
1、生产者消费者模式
生产者生产消息放到队列中,多个消费者同时监听队列,谁先抢到消息谁就会从队列中取走消息,即对于每个消息最多只能被一个消费者拥有。
具体的方法就是创建一个任务队列,生产者主动lpush消息,而消费者去rpop数据。但是这样存在一个问题,就是消费者需要主动去请求数据,周期性的请求会造成资源的浪费。如果可以实现一旦有新消息加入队列就通知消费者就好了,这时借助brpop命令就可以实现这样的需求。brpop和rpop命令相似,唯一区别就是当列表中没有元素时,brpop命令会一直阻塞住连接,直到有新元素加入。
BRPOP key timeout
brpop命令接收两个参数,第一个参数key为键值,第二个参数timeout为超时时间。BRPOP命令取数据时候,如果暂时不存在数据,该命令会一直阻塞直到达到超时时间。如果timeout设置为0,那么就会无限等待下去。

2、发布者订阅者模式
发布者生产消息放到队列里,多个监听队列的订阅者都会受到同一份消息。
生产者使用下面命令来发布消息:
PUBLISH CHANNEL MESSAGE
订阅者通过下面的命令来订阅消息,执行subscribe命令后,客户端进入订阅状态,处于此状态的客户端不能使用4个属于“发布/订阅”模型的命令之外的命令。另外,可以使用subscribe channel1.1 channel1.2 ... 同时订阅多个频道。
SUBSCRIBE CHANNEL

3、Java实现的redis的消息队列
在jedis中,有对应的方法进行订阅和发布,为了传输对象,需要将对象进行序列化,并封装成字符串进行处理。
下面我们要实现三个类,一个对应publish,一个对应subscribe,一个对应要传递的对象实体类:
实体类:
import java.io.Serializable;
/**
* 实体类
* 封装消息
* @author Administrator
*
*/
public class Message implements Serializable
{
private static final long serialVersionUID = 1L;
private String title;
private String content;
public String getTitle()
{
return title;
}
public void setTitle(String title)
{
this.title = title;
}
public String getContent()
{
return content;
}
public void setContent(String content)
{
this.content = content;
}
}
Publish类:
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import redis.clients.jedis.Jedis;
/**
* 发布者,用于发布消息
* @author Administrator
*
*/
public class TestPub
{
public static void main(String[] args)
{
Jedis jedis = new Jedis("127.0.0.1");
try
{
Message message = new Message();
message.setTitle("体育新闻");
message.setContent("著名NBA球星科比退役了!");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(message);
String msg1 = baos.toString("ISO-8859-1"); jedis.publish("foo", msg1);
} catch (Exception e)
{
e.printStackTrace();
}
jedis.close();
}
}
Subscribe类:
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPubSub;
/**
* 订阅者,用于接收消息
* @author Administrator
*
*/
public class TestSub
{
public static void main(String[] args)
{
Jedis jedis = new Jedis("127.0.0.1");
JedisPubSub jedisPubSub = new JedisPubSub()
{
@Override
public void onUnsubscribe(String channel, int subscribedChannels)
{
} @Override
public void onSubscribe(String channel, int subscribedChannels)
{
} @Override
public void onPUnsubscribe(String pattern, int subscribedChannels)
{
} @Override
public void onPSubscribe(String pattern, int subscribedChannels)
{
} @Override
public void onPMessage(String pattern, String channel, String message)
{
} @Override
public void onMessage(String channel, String message)
{
try
{
ByteArrayInputStream bis = new ByteArrayInputStream(message.getBytes("ISO-8859-1"));
// 此处指定字符集将字符串编码成字节数组,此处的字符集需要与发布时的字符集保持一致
ObjectInputStream ois = new ObjectInputStream(bis);
Message message2= (Message) ois.readObject();
System.out.println(message2.getTitle()+"\n"+message2.getContent());
} catch (Exception e)
{
e.printStackTrace();
} finally
{ }
}
};
jedis.subscribe(jedisPubSub, "foo");
jedis.close();
}
}
先执行订阅操作,然后发布者发布消息,执行结果为:

五、管道
客户端和Redis使用TCP协议连接,不管是客户端向Redis发送命令还是Redis向客户端返回命令的执行结果,都需要经过网络传输。在执行过个命令时,每条命令都需要等待上一条命令执行完才能执行,即使命令不需要上一条命令的执行结果。
Redis的底层通信协议对管道提供了支持,通过管道可以一次性发送多条命令并在执行完后一次性将结果返回,当一组命令中每条命令都不依赖之前命令的执行结果时就可以将这组命令一起通过管道发出。管道通过减少客户端与Redis的通信次数来实现降低往返时延累计值的目的。
六、存储优化
1、精简键名和键值
2、内部编码优化
七、参考资料
Redis系列二之事务及消息通知的更多相关文章
- Redis系列(二):Redis的数据类型及命令操作
原文链接(转载请注明出处):Redis系列(二):Redis的数据类型及命令操作 Redis 中常用命令 Redis 官方的文档是英文版的,当然网上也有大量的中文翻译版,例如:Redis 命令参考.这 ...
- Redis自学笔记:4.4进阶-消息通知
4.4消息通知 4.4.1任务队列 传递任务的队列.与任务队列进行交互的实体有两类,一类是生产者,一类是消费者. 生产者将需要处理的任务放入任务队列中,二消费者不断从任务队列中读入任务 信息并执行. ...
- RabbitMQ学习系列二-C#代码发送消息
RabbitMQ学习系列二:.net 环境下 C#代码使用 RabbitMQ 消息队列 http://www.80iter.com/blog/1437455520862503 上一篇已经讲了Rabbi ...
- Redis系列(二):Redis的5种数据结构及其常用命令
上一篇博客,我们讲解了什么是Redis以及在Windows和Linux环境下安装Redis的方法, 没看过的同学可以点击以下链接查看: Redis系列(一):Redis简介及环境安装. 本篇博客我们来 ...
- Redis系列二:reids介绍
一.什么是redis.redis有哪些特性.redis有哪些应用场景.redis的版本 1. 什么是redis redis是一种基于键值对(key-value)数据库,其中value可以为string ...
- Redis系列(二)-Hredis客户端设计及开源
接上篇c#实现redis客户端(一),重新整理些了下. 阅读目录: 项目说明 Hredis设计图 单元测试场景 总结 项目说明 背景:因为有地方要用,而又没找到对sentinel良好支持的Net客户端 ...
- redis系列二: linux下安装redis
下面介绍在Linux环境下,Redis的安装与配置 一. 安装 1.首先上官网下载Redis 压缩包,地址:http://redis.io/download 下载稳定版3.0即可. 2.通过远程管理工 ...
- Redis系列二 - 数据结构
前言 redis作为我们开发的一大神器,我们接触肯定不会少,但是很多同学也许只会存储String类型的值,这是非常不合理的.在这里,将带大家认识Redis的5中数据结构. 1.问:Redis有那些数据 ...
- Redis系列(五):消息队列
消息队列已经成为现在互联网服务端的标配组件,现在比较常用的消息中间件有RabbitMQ.Kafka.RocketMQ.ActiveMQ.说出来你可能不信,Redis作为一个缓存中间件,居然也提供了消息 ...
随机推荐
- js 倒计时实现
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- ComponentOne 2016 年产品规划
作为老牌的 Visual Studio 控件集,ComponentOne 今后的发展方向是什么?新的一年会在哪些方面有所增强?且听 ComponentOne 全球产品经理的 2016年规划. 2016 ...
- import com.sun.image.codec.jpeg.JPEGCodec不通过 找不到包(转载)
http://www.xuebuyuan.com/2008608.html 在Eclipse中处理图片,需要引入两个包:import com.sun.image.codec.jpeg.JPEGCode ...
- PHP面向对象04_串行化
oop04复习 2014-9-3 10:48:45 要点: --1.克隆对象 --2.__toString( ) --3. __call( ) --4.自动加载类 --5.对象串行化 1.克隆对象以及 ...
- H5常用代码:适配方案4
前面有分享了4种适配方案,但始终是通过手动缩放或者视口缩放来实现,用来做一些专题页,或者功能相对简单的项目来说也是完全能应付的,但整体来说感觉还是一种缩放,说不上是真正的适配,言外之意就是即将分享真正 ...
- JS 脚本最后加载
有些脚本执行,为了不影响页面其他脚本执行,需要放在最后 <script type="text/javascript"> function addLoadEvent(fu ...
- 【管理心得之四十】中文“其他”、英文“other”、日文“その他”..........................................
场景再现====================={某研讨会}本学期为:调查研究.整理总结阶段.本阶段的主要任务是: 一.学习理论,收集.汇编学习资料,提高自己的素质..... 二.通过对部分班级学生 ...
- iOS-数据持久化-对象归档
一.简单说明 对象归档是将对象归档以文件的形式保存到磁盘中(也称为序列化,持久化),使用的时候读取该文件的保存路径读取文件的内容(也称为接档,反序列化), (对象归档的文件是保密的,在磁盘上无法查看文 ...
- VMware Workstation cannot connect to the virtual machine 解决方案
今天 打开虚拟机 忽然遇到这个问题: VMware Workstation cannot connect to the virtual machine. Make sure you have righ ...
- JavaScript开发的技巧
1. 使用===取代== ==和!=操作符会在需要的情况下自动转换数据类型.但===和!==不会,它们会同时比较值和数据类型,这也使得它们要比==和!=快. "){ //速度慢 } & ...