码迷,mamicode.com
首页 > 其他好文 > 详细

Redis学习笔记二

时间:2016-06-13 22:02:13      阅读:162      评论:0      收藏:0      [点我收藏+]

标签:

一、事务

  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(); } }

  先执行订阅操作,然后发布者发布消息,执行结果为:

  技术分享

五、参考资料

  1、Redis入门指南

Redis学习笔记二

标签:

原文地址:http://www.cnblogs.com/xujian2014/p/5578221.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!