标签:
Redis中的事务是一组命令的集合。一个事务中的命令要么都执行,要么都不执行。
事务的原理是先将一个事务的命令发送给Redis,然后再让Redis依次执行这些命令。下面看一个示例:
首先,使用multi命令告诉Redis:下面我给你的命令属于同一个事务,你先不要执行,而是暂时存起来。
然后,我们发送两个set命令来实现赋值,可以看到redis没有执行这些命令,而是返回queued表示这两条命令已经进入等待执行的事务队列中。
当所有要在同一事务中执行的命令都发给Redis后,用exec命令告诉Redis将等待执行的事务队列中所有的命令按照发送顺序依次执行。
当一个事务中某个命令执行出错时,Redis会怎样处理呢?这里有三种情况:
情况一:语法错误,指错命令不存在或者命令参数的个数不对
可以看到,跟在multi命令后执行了三个命令,第二个和第三个命令有语法错误,执行exec后redis就会执行返回错误。
情况二:运行错误,指在命令执行时出现的错误。
这种错误在实际执行前redis是无法发现的,所以在事务里这样的命令是会被redis接受和执行的,如果事务里一条命令出现了运行错误,事务里其他的命令依然会继续执行(包括出错命令之后的命令)。
注意:Redis事务没有关系型数据库提供的回滚功能。
watch命令可以监控一个或多个键,一旦其中一个键被修改或删除,之后的事务就不会执行,监控一直持续到exec命令(但是不能保证其他客户不修改这一键值)。
上例中执行watch命令后,事务执行前修改了key值,所以事务中命令set username xujian没有执行,exec命令返回空结果。
注意:执行exec命令后立即取消对所有键的监控,如果不想执行事务中的命令也可以使用unwatch命令来保证下一个事务的执行不会受到影响。
在Redis中,可以使用expire命令设置一个键的生存时间,到时间后Redis会自动删除它。
expire命令的使用方法为expire key seconds,其中seconds参数表示键的生存时间,单位是秒。如果想知道一个键还有多久的时间会被删除,可以使用TTL命令,返回值是键的剩余时间(单位是秒)。如果想取消键的生存时间设置,即将恢复永久的,可以使用persist命令。
有序集合常见的使用场景是大数据排序,如游戏的玩家排行榜。
sort命令可以对列表类型、集合类型和有序集合类型键进行排序,并且可以完成与关系数据库中的连接查询相类似的任务。
对List进行排序:
对有序集合类型排序时会忽略元素的分数,只针对元素自身的值进行排序:
by参数的语法为“by 参考键”,其中参考键可以是字符串类型键或者是散列类型键的某个字段。如果提供了by参数,sort命令将不再依据元素自身的值进行排序,而是对每个元素使用元素的值替换参考键中的第一个“*”并获取其值,然后依据该值对元素排序:
默认情况下,sort会直接返回排序结果,如果希望保存排序结果,可以使用store参数。
注意:sort命令的时间复杂度是O(n+mlogm),其中n表示要排序的列表(集合或有序集合)中的元素个数,m表示要返回的元素的个数。当n较大的时候sort命令的性能相对较低,并且redis在排序前会建立一个长度为n的容器来存储待排序的元素。在开发中使用sort要注意以下几点:
1、尽可能减少待排序键中元素的数量(使n尽可能小)
2、使用limit参数只获取需要的数据(使m尽可能小)
3、如果要排序的数据量较大,尽可能使用store参数将结果缓存
一般来说,消息队列有两种场景,一种是生产者消费者模式,一种是发布者订阅者模式。利用redis这两种场景的消息队列都能实现。
生产者生产消息放到队列中,多个消费者同时监听队列,谁先抢到消息谁就会从队列中取走消息,即对于每个消息最多只能被一个消费者拥有。
具体的方法就是创建一个任务队列,生产者主动lpush消息,而消费者去rpop数据。但是这样存在一个问题,就是消费者需要主动去请求数据,周期性的请求会造成资源的浪费。如果可以实现一旦有新消息加入队列就通知消费者就好了,这时借助brpop命令就可以实现这样的需求。brpop和rpop命令相似,唯一区别就是当列表中没有元素时,brpop命令会一直阻塞住连接,直到有新元素加入。
BRPOP key timeout
brpop命令接收两个参数,第一个参数key为键值,第二个参数timeout为超时时间。BRPOP命令取数据时候,如果暂时不存在数据,该命令会一直阻塞直到达到超时时间。如果timeout设置为0,那么就会无限等待下去。
发布者生产消息放到队列里,多个监听队列的订阅者都会受到同一份消息。
生产者使用下面命令来发布消息:
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(); } }
先执行订阅操作,然后发布者发布消息,执行结果为:
标签:
原文地址:http://www.cnblogs.com/xujian2014/p/5578221.html