一、 Redis简介
Redis是用C语言编写的开源软件,可以运行在Linux上,目前不支持Windows。Redis通常会被用于缓存、数据持久化、消息队列,Redis避免了服务器挂掉后,内存数据丢失的问题。Redis支持5种数据结构: strings, hashes, lists, sets, sorted sets,而且对于这些数据结构上的操作都是原子性的,这意味着操作这些集合是线程安全的;Redis首先是把数据集放在内存上的,可以设置每隔一段儿时间持久化数据到硬盘,如果工程中仅仅需要Redis做缓存,持久化也是可以被禁止的;Redis支持像Mysql一样的主从同步,主服务器上的数据可以同步到N个从服务器上,可以读写分离。
大多数编程语言都能使用Redis,C、C++、Java、C#、Python都能通过相应的API绑定Redis服务端的IP和端口使用Redis服务。在下面的章节中主要以Java为例介绍Redis数据结构上的简单操作。
二、Redis支持的数据结构以及简单操作
1. String
在String类数据结构上,Redis和Memcached一致,都支持Key-Value的数据存储(一个Key对应一个Value,相当于Java中的HashMap),支持给一个Key设定一个value,支持在特定Key下的Value后添加字符串、删除特定Key等操作、对相应Key的数字value进行原子的加减操作。
public class RedisClient { private static final String HOST="192.168.146.129"; private static final int PORT=6379; private final Jedis jedis=new Jedis(HOST,PORT); /** * 测试 Redis做Key-Value存取 */ @Test public void testKeyValue(){ jedis.set("name", "boruoyihao"); System.out.println("Get name from redis:"+jedis.get("name")); System.out.println("Is exist in redis:"+jedis.exists("name")); jedis.append("name", ",Hello"); //相同Key下添加 System.out.println("Get name from redis:"+jedis.get("name")); jedis.del("name"); //delete the Key System.out.println("Get name from redis:"+jedis.get("name")); //相当于jedis.set("name","boreyihao"); //jedis.set("age",26); jedis.mset("name","boreyihao","age","26"); System.out.println(jedis.get("name")+"=="+jedis.get("age")); //设置name有效时间为2S jedis.setex("name", 2, "boruo"); System.out.println("Get name from redis:"+jedis.get("name")); try { Thread.sleep(3000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //name时间超时,取出数据为null System.out.println("Get name from redis:"+jedis.get("name")); //原子递增操作 jedis.set("age", "26"); jedis.incr("age"); System.out.println("Get age from redis:"+jedis.get("age")); jedis.decrBy("age", 3); System.out.println("Get age from redis:"+jedis.get("age")); } }
2. hashes
Redis的Hash可以以一个键值存入一个Map对象,可以获取整个Map对象,也可以获取Map对象的key集合或者Value集合,也可以获取指定Field的Value,也可以对指定Field的整数值执行加减操作。Redis可以存对象的属性以及属性值,但是memcached存对象 只能转化为字符串,如果要修改对象的值,只能把字符串取出来转化为对象,然后修改对象,再转化为字符串存回memcached,开销还是很大的,这是Redis的优势所在。
@Test public void testHash(){ //test hashmap Map<String,String>m=new HashMap<String,String>(); m.put("name", "Tom"); m.put("major", "Software"); m.put("age", "43"); jedis.hmset("m", m); List<String> name=jedis.hmget("m", "name"); // m为key,name为m的key,返回的是List System.out.println(name); System.out.println("Get Hash Values="+jedis.hvals("m")); Iterator<String> it=jedis.hkeys("m").iterator(); while(it.hasNext()){ String key=it.next(); System.out.println(key+":"+jedis.hmget("m", key)); } System.out.println("Get keys from redis:"+jedis.hkeys("m")); System.out.println("Get Values from redis:"+jedis.hvals("m")); Map<String,String>map=jedis.hgetAll("m"); System.out.println("Get map from Redis:"+map); //Test hashset System.out.println("---test hash set"); jedis.hset("s", "name", "Jack"); jedis.hset("s", "age", "25"); System.out.println(jedis.hexists("s", "name")); System.out.println(jedis.hget("s", "name")); System.out.println(jedis.hgetAll("s")); Map<String,String>smap=jedis.hgetAll("s"); System.out.println(smap); System.out.println(jedis.hdel("s", "name")); //清除name属性 System.out.println(jedis.hincrBy("s", "age", 3)); System.out.println("after incr:"+jedis.hgetAll("s")); }
3. lists
List是一个双向链表,原因是Redis支持在这个链表的两端插入和弹出操作,所以Redis的List除了是双向链表外,也可以用于队列,也可以用于栈。
@Test public void testList(){ System.out.println("test List"); jedis.del("task"); jedis.rpush("task", "do homework");//在链表尾部添加 jedis.rpush("task", "clean housr"); jedis.lpush("task", "rest");//在链表头部添加 jedis.lpush("task", "watch tv"); System.out.println("length:"+jedis.llen("task")); System.out.println("Get all List:"+jedis.lrange("task", 0, -1)); //-1表示到最后,表示取出从头到尾的数据 System.out.println("target Index:"+jedis.lindex("task", 2)); System.out.println(jedis.lpop("task"));//取出头部数据 System.out.println("Get all List:"+jedis.lrange("task", 0, -1)); System.out.println(jedis.rpop("task"));//取出尾部数据 System.out.println("Get all List:"+jedis.lrange("task", 0, -1)); }
4. sets
Redis的Set相当于Java的hashset,元素排列有序,更强的是它可以很方便的求两个集合的并、交、差集。
@Test public void testSet(){ System.out.println("test Set"); jedis.del("s1"); jedis.sadd("s1", "4","1","3","20"); //有序 System.out.println("Get all s1:"+jedis.smembers("s1")); //有序的 System.out.println("Get no s1:"+jedis.scard("s1")); jedis.sadd("s2", "4","11","13","34","3","20"); System.out.println("Get all s2:"+jedis.smembers("s2")); //有序的 System.out.println("Get no s2:"+jedis.scard("s2")); System.out.println(jedis.sinter("s1","s2"));//交集 System.out.println(jedis.sunion("s1","s2")); //并集 System.out.println(jedis.sdiff("s1","s2"));//差集 }
5. sorted sets
Redis的Sorted Sets 是对Set补充,可以根据权值进行排序,同样不允许重复。
@Test public void testSortedSet(){ System.out.println("testSortedset"); jedis.zadd("ss", 12, "Tom"); //中间为权值,根据权值排序 jedis.zadd("ss", 1, "Jack"); jedis.zadd("ss", 20, "David"); jedis.zadd("ss", 3, "Jim"); System.out.println("sortedset length:"+jedis.zcard("ss")); //获取元素个数 System.out.println("sorted set:"+jedis.zrange("ss", 0, -1)); //输出按权值排列 jedis.zadd("ss1", 1, "Tom"); //中间为权值 jedis.zadd("ss1", 3, "Jimmy"); jedis.zadd("ss1", 2, "Alex"); jedis.zadd("ss1", 5, "Jim"); System.out.println("sortedset length:"+jedis.zcard("ss1")); //获取元素个数 System.out.println("sorted set:"+jedis.zrange("ss1", 0, -1)); //输出按权值排列 System.out.println(jedis.zscore("ss1", "Tom")); //获取Tom的排序权值 }
三、Redis应用
结合以上对Redis数据结构的介绍,Redis在服务端开发技术上,简单来说是可以做缓存,并可以做数据持久化,以下几方面是菜鸟的理解。
1. 消息队列
在本文第二部分介绍了Redis有双向链表List,并且支持双进双出,所以可以用Redis做消息队列,比如通知的消息、任务、邮件等都可以存储在消息队列,然后另一端可以通过Redis能支持的大部分语言消费队列里的数据,这样便于系统模块之间的解耦,降低模块之间的依赖性;并且把同步消息处理方式转化为异步处理,不会造成队列阻塞。微博的用户微博列表应该是用Redis做的缓存。
2. 列出某项列表最新几条数据
这个同样可以用Redis中的List去处理,比如微博某大V的评论数上百万,在页面上显示肯定只能显示最新的几十条评论,如果存在数据库中,用Mysql的SELECT limit 查询数据,会对所有数据进行排序,这样的代价机会非常大,但是如果使用Redis的List就可以使用lrange(“key”,0,100)方法,这样可以取出最近100条评论。
3. 计数器
因为微博有最大的Redis集群,我们还是拿微博举例,某明星发了一个主题“我们”的微博,点赞数量两天内几百万,对于这种情况,就可以使用Redis中的原子加减操作,在java中,我们知道i++或者i--并不是原子性的,也就意味着它并不是线程安全的,但是使用Redis就不会有这个问题,可以使用jedis.hincrBy("weiboid", "likes", 1);这样表示某微博点赞数量加1,在这里是线程安全的。
4. 排行榜,取TOP N数据
结合本文第二部分介绍的Sorted Set,我们可以取出TOP N的数据,比如有100W用户参与比赛,或者100W用户微博,我们想取出比赛成绩最高的100个,或者最热的100条微博,在这里的比赛成绩或者微博热度都可以是权值(score),在缓存中操作,不需要进行排序,就能快速取出。
5. 排重与求并、差、交集
Redis使用Set,在第二部分Java程序中已经介绍,需要排重时,只需要往set集合中插入数据,Redis会自动做好去重操作,并且可以求任意两个缓存中的set集合的并、交、差集合。
6. Pub/Sub
Redis支持消息发布-订阅模式,非常适合聊天消息推送。
学习资料与参考文献:
http://try.redis.io/ 有Redis初学者需要学习的基本命令,提供命令行界面。
http://redis.io/topics/introduction Redis基本介绍
http://www.csdn.net/article/2013-10-07/2817107-three-giant-share-redis-experience/1 Redis在国内外应用
http://www.cnblogs.com/whoamme/p/3532129.html Redis基本操作
http://www.360doc.com/content/15/0510/20/23016082_469494498.shtml Redis应用