您的位置:首页 > 数据库 > Redis

Redis简介、安装、数据类型、简单案例

2018-03-14 17:06 423 查看

01/ nosql介绍

 
NoSQL:一类新出现的数据库(not only sql),它的特点:
1、  不支持SQL语法
2、  存储结构跟传统关系型数据库中的那种关系表完全不同,nosql中存储的数据都是KV形式
3、  NoSQL的世界中没有一种通用的语言,每种nosql数据库都有自己的api和语法,以及擅长的业务场景
4、  NoSQL中的产品种类相当多:
a)        Mongodb  文档型nosql数据库,擅长做CMS系统(内容管理系统)
b)        Redis        内存数据库,数据结构服务器,号称瑞士军刀(精巧),只要你有足够的想象力,它可以还给你无限惊喜
c)        Hbase  hadoop生态系统中原生的一种nosql数据库,重量级的分布式nosql数据库,用于海量数据的场景
d)        Cassandra  hadoop生态系统中原生的一种分布式nosql数据库,后起之秀
 
NoSQL和SQL数据库的比较:
1、适用场景不同:sql数据库适合用于关系特别复杂的数据查询场景,nosql反之
2、“事务”特性的支持:sql对事务的支持非常完善,而nosql基本不支持事务
3、两者在不断地取长补短,呈现融合趋势
 

02/ redis介绍

2.1 简述

Redis是一个高性能的kv对缓存和内存数据库(存的不像mysql那样的表)
Redis的存储结构就是key-value,形式如下:



注: redis中的value内部可以支持各种数据结构类型,比如可以存入一个普通的string,还可以存list,set,hashmap,sortedSet(有序的set)
 

2.2 redis应用场景

A、用来做缓存(ehcache/memcached)——redis的所有数据是放在内存中的(内存数据库)
B、可以在某些特定应用场景下替代传统数据库——比如社交类的应用
C、在一些大型系统中,巧妙地实现一些特定的功能:session共享、购物车
只要你有丰富的想象力,redis可以用在可以给你无限的惊喜…….
 

2.3 redis的特性

1、redis数据访问速度快(数据在内存中)
2、redis有数据持久化机制(持久化机制有两种:1、定期将内存数据dump到磁盘;2、aof(append only file)持久化机制——用记日志的方式记录每一条数据更新操作,一旦出现灾难事件,可以通过日志重放来恢复整个数据库)
3、redis支持集群模式(容量可以线性扩展)
4、redis相比其他缓存工具(ehcach/memcached),有一个鲜明的优势:支持丰富的数据结构
 

03/ 安装redis

3.1 获取源码包

1、先去官网(http://redis.io/download )下载一个源码工程(redis官网版本只支持linux/微软开源事业部维护了一个windows版本)
2、把安装包上传到服务器,解压缩
 

3.2 编译源码

1、切换到解压出来的源码工程目录中  
cd redis-2.6.16
2、用make命令来对redis的c语言源码工程进行编译
3、编译完成之后,用make  install命令进行安装
[root@notrue-centos redis-2.6.16]# make  PREFIX=/usr/local/redis  install
安装成功的显示:



 

3.3 启动redis服务

1、进入redis的bin目录



 

2、准备配置文件

Redis服务在启动的时候可以指定配置文件,那,我们可以从redis的源码目录中拷贝一份配置文件模板到redis的安装目录,修改后使用
[root@notrue-centos redis-2.6.16]# cp /root/redis-2.6.16/redis.conf /usr/local/redis/
 

3、启动redis服务

并指定使用的配置文件



 

4、启动成功的显示



 

5、启动为后台服务

上述启动方法,会让redis服务进程运行在console前台,最好应该放到后台运行,可将启动命令改为如下方式:
1/方式一
[root@notrue-centos redis]# nohup bin/redis-server ./redis.conf 1>/dev/null 2>&1 &
Nohup:控制台关闭或闲置超时,也不退出
1>/dev/null : 把程序的“1”——标准输出,重定向到文件/dev/null
2>&1      : 把程序的“2”——错误输出,重定向到“1”所去的文件
&        :   把程序放到后台运行            
 
 
2/方式二
修改配置文件,
vi redis.conf
修改其中一个配置



保存文件后再用普通命令启动,也可以启动为后台模式
[root@notrue-centos redis]#bin/redis-server  ./redis.conf
 

04/ 客户端连接

1、  用redis自带的命令行客户端
[root@notrue-centos redis]# bin/redis-cli  -h  notrue-centos  -p  6379
redis notrue-centos:6379> ping
PONG
redis notrue-centos:6379>
bin/redis-cli  -a 密码  -h  notrue-centos  -p  6379
 
2、  或者用redis的api客户端连接
新建一个maven工程,导入jedis的maven依赖坐标
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.7.2</version>
    <type>jar</type>
    <scope>compile</scope>
</dependency>
 
然后写一个类用来测试服务器跟客户端的连通性:
public class RedisClientConnectionTest {
         public static void main(String[] args) {
                   // 构造一个redis的客户端对象
                   Jedis jedis = new Jedis("pinshutang.zicp.net", 6379);
                   String ping = jedis.ping();
                   System.out.println(ping);
         }
}
 
 

05/ Redis的数据功能

5.1 String类型的数据

(常作为缓存使用)
 
1/插入和读取一条string类型的数据

redis notrue-centos:6379> set sessionid-0001 "zhangsan"
OK
redis notrue-centos:6379> get sessionid-0001
"zhangsan"
 
2/对string类型数据进行增减(前提是这条数据的value可以看成数字)
DECR key
INCR key 
 
DECRBY key decrement
INCRBY key increment
 
3/一次性插入或者获取多条数据
MGET  key1  key2
MSET  key1  value1   key2  value2  …..
 
 
4/在插入一条string类型数据的同时为它指定一个存活期限
setex  bancao  10  weige
### bancao这条数据就只会存活10秒钟,过期会被redis自动清除
 
 
应用:将一个自定义的对象比如product存入redis
实现方式一:将对象序列化成byt
f985
e数组
         /**
          * 将对象缓存到redis的string结构数据中
          * @throws Exception
          *
          */
         @Test
         public void testObjectCache() throws Exception{
                  
                            ProductInfo p = new ProductInfo();
                           
                            p.setName("苏菲");
                            p.setDescription("angelababy专用");
                            p.setCatelog("unknow");
                            p.setPrice(10.8);
 
                            //将对象序列化成字节数组
                           
                            ByteArrayOutputStream ba = new ByteArrayOutputStream();
                            ObjectOutputStream oos = new ObjectOutputStream(ba);
                           
                            //用对象序列化流来将p对象序列化,然后把序列化之后的二进制数据写到ba流中
                            oos.writeObject(p);
                           
                            //将ba流转成byte数组
                            byte[] pBytes = ba.toByteArray();
                           
                           
                           
                            //将对象序列化之后的byte数组存到redis的string结构数据中
                            jedis.set("product:01".getBytes(), pBytes);
                           
                  
                            //根据key从redis中取出对象的byte数据
                            byte[] pBytesResp = jedis.get("product:01".getBytes());
                           
                            //将byte数据反序列出对象
                            ByteArrayInputStream bi = new ByteArrayInputStream(pBytesResp);
                           
                            ObjectInputStream oi = new ObjectInputStream(bi);
                           
                            //从对象读取流中读取出p对象
                            ProductInfo pResp = (ProductInfo) oi.readObject();
                           
                            System.out.println(pResp);
                           
                 
         }
 
实现方式二:
将对象转成json字符串来存取
         /**
          * 将对象转成json字符串缓存到redis的string结构数据中
          */
         @Test
         public void testObjectToJsonCache(){
                  
                  
                   ProductInfo p = new ProductInfo();
                  
                   p.setName("ABC");
                   p.setDescription("刘亦菲专用");
                   p.setCatelog("夜用型");
                   p.setPrice(10.8);
 
                   //利用gson将对象转成json串
                   Gson gson = new Gson();
                   String pJson = gson.toJson(p);
                  
                   //将json串存入redis
                   jedis.set("prodcut:02", pJson);
                  
                  
                   //从redis中取出对象的json串
                   String pJsonResp = jedis.get("prodcut:02");
                  
                   //将返回的json解析成对象
                   ProductInfo pResponse = gson.fromJson(pJsonResp, ProductInfo.class);
                  
                   //显示对象的属性
                   System.out.println(pResponse);
                  
                  
         }
 
 

5.2 List数据结构

5.2.1 List图示



 
 

5.2.2 List功能演示

#从头部(左边)插入数据
redis>LPUSH  key  value1  value2  value3   
#从尾部(右边)插入数据
redis>RPUSH  key  value1  value2  value3  
#读取list中指定范围的values
redis>LRANGE  key  start   end
redis> lrange  task-queue  0  -1      读取整个list
#从头部弹出一个元素
LPOP  key
#从尾部弹出一个元素
RPOP  key
#从一个list的尾部弹出一个元素插入到另一个list
RPOPLPUSH   key1    key2     ## 这是一个原子性操作
 
 

5.2.3 List的应用案例demo

1 需求描述任务调度系统:
生产者不断产生任务,放入task-queue排队
消费者不断拿出任务来处理,同时放入一个tmp-queue暂存,如果任务处理成功,则清除tmp-queue,否则,将任务弹回task-queue



 
2 代码实现1/生产者
——模拟产生任务
public class TaskProducer {
 
         // 获取一个redis的客户端连接对象
         public static Jedis getRedisConnection(String host, int port) {
 
                   Jedis jedis = new Jedis(host, port);
 
                   return jedis;
 
         }
 
         public static void main(String[] args) {
 
                   Jedis jedis = getRedisConnection("angelababy", 6379);
 
                   Random random = new Random();
                   // 生成任务
                   while (true) {
 
                            try {
                                     // 生成任务的速度有一定的随机性,在1-2秒之间
                                     Thread.sleep(random.nextInt(1000) + 1000);
                                     // 生成一个任务
                                     String taskid = UUID.randomUUID().toString();
 
                                     // 往任务队列"task-queue"中插入,第一次插入时,"task-queue"还不存在
                                     //但是lpush方法会在redis库中创建一条新的list数据
                                     jedis.lpush("task-queue", taskid);
                                     System.out.println("向任务队列中插入了一个新的任务: " + taskid);
 
                            } catch (InterruptedException e) {
                                     // TODO Auto-generated catch block
                                     e.printStackTrace();
                            }
 
                   }
 
         }
 
}
 
 
 
2/消费者
——模拟处理任务,并且管理暂存队列
public class TaskConsumer {
 
         public static void main(String[] args) {
 
                   Jedis jedis = new Jedis("angelababy", 6379);
                   Random random = new Random();
 
                   while (true) {
 
                            try {
                                     // 从task-queue中取一个任务,同时放入"tmp-queue"
                                     String taskid = jedis.rpoplpush("task-queue", "tmp-queue");
 
                                     // 模拟处理任务
                                     Thread.sleep(1000);
 
                                     // 模拟有成功又有失败的情况
                                     int nextInt = random.nextInt(13);
                                     if (nextInt % 7 == 0) { // 模拟失败的情况
 
                                               // 失败的情况下,需要将任务从"tmp-queue"弹回"task-queue"
                                               jedis.rpoplpush("tmp-queue", "task-queue");
                                               System.out.println("-------任务处理失败: " + taskid);
 
                                     } else { // 模拟成功的情况
 
                                               // 成功的情况下,将任务从"tmp-queue"清除
                                               jedis.rpop("tmp-queue");
                                               System.out.println("任务处理成功: " + taskid);
 
                                     }
 
                            } catch (InterruptedException e) {
                                     // TODO Auto-generated catch block
                                     e.printStackTrace();
                            }
 
                   }
 
         }
 
}
 
上述机制是一个简化版,真实版的任务调度系统会更加复杂,如下所示:
(增加了一个专门用来管理暂存队列的角色,以便就算消费者程序失败退出,那些处理失败的任务依然可以被弹回task-queue)



5.3 Hash数据结构

5.3.1 Hash图示

Redis中的Hashes类型可以看成具有StringKey和String Value的map容器



 

5.3.2 Hash功能演示

1、往redis库中插入一条hash类型的数据
redis> HSET  key  field  value
举例:
redis 127.0.0.1:6379> hset user001:zhangsan  iphone 6
(integer) 1
redis 127.0.0.1:6379> hset user001:zhangsan   xiaomi 7
(integer) 1
redis 127.0.0.1:6379> hset user001:zhangsan   meizu 8
(integer) 1
在redis库中就形成了这样一条数据:



 
2、从redis库中获取一条hash类型数据的value
取出一条hash类型数据中所有field-value对
redis 127.0.0.1:6379> hgetall user001:zhangsan
1) "iphone"
2) "6"
3) "xiaomi"
4) "7"
5) "meizu"
6) "8"
 
取出hash数据中所有fields
redis 127.0.0.1:6379> HKEYS user001:zhangsan
1) "iphone"
2) "xiaomi"
3) "meizu"
 
取出hash数据中所有的value
redis 127.0.0.1:6379> hvals user001:zhangsan
1) "6"
2) "7"
3) "8"
 
取出hash数据中一个指定field的值
redis 127.0.0.1:6379> hget user001:zhangsan xiaomi
"8"
 
为hash数据中指定的一个field的值进行增减
redis 127.0.0.1:6379> HINCRBY user001:zhangsan xiaomi 1
(integer) 8
 
从hash数据中删除一个字段field及其值
redis 127.0.0.1:6379> hgetall user001:zhangsan
1) "iphone"
2) "6"
3) "xiaomi"
4) "7"
5) "meizu"
6) "8"
redis 127.0.0.1:6379> HDEL user001:zhangsan iphone
(integer) 1
redis 127.0.0.1:6379> hgetall user001:zhangsan
1) "xiaomi"
2) "7"
3) "meizu"
4) "8"
 
 

5.4 Set数据结构功能

集合的特点:无序、无重复元素
1、  插入一条set数据
redis 127.0.0.1:6379> sadd frieds:zhangsan  bingbing baby fengjie furong ruhua tingting
(integer) 6
redis 127.0.0.1:6379> scard frieds:zhangsan
(integer) 6
redis 127.0.0.1:6379>
 
 
2、获取一条set数据的所有members
redis 127.0.0.1:6379> smembers frieds:zhangsan
1) "fengjie"
2) "baby"
3) "furong"
4) "bingbing"
5) "tingting"
6) "ruhua"
 
3、判断一个成员是否属于某条指定的set数据
redis 127.0.0.1:6379> sismember frieds:zhangsan liuyifei     #如果不是,则返回0
(integer) 0
redis 127.0.0.1:6379> sismember frieds:zhangsan baby       #如果是,则返回1
(integer) 1
 
4、求两个set数据的差集
#求差集
redis 127.0.0.1:6379> sdiff  frieds:zhangsan  friends:xiaotao
1) "furong"
2) "fengjie"
3) "ruhua"
4) "feifei"
#求差集,并将结果存入到另一个set
redis 127.0.0.1:6379> sdiffstore zhangsan-xiaotao frieds:zhangsan friends:xiaotao
(integer) 4
#查看差集结果
redis 127.0.0.1:6379> smembers zhangsan-xiaotao
1) "furong"
2) "fengjie"
3) "ruhua"
4) "feifei"
 
5、  求交集,求并集
#求交集
redis 127.0.0.1:6379> sinterstore zhangsan:xiaotao frieds:zhangsan friends:xiaotao
(integer) 2
redis 127.0.0.1:6379> smembers zhangsan:xiaotao
1) "bingbing"
2) "baby"
 
#求并集
redis 127.0.0.1:6379> sunion  frieds:zhangsan friends:xiaotao
 1) "fengjie"
 2) "tangwei"
 3) "liuyifei"
 4) "bingbing"
 5) "ruhua"
 6) "feifei"
 7) "baby"
 8) "songhuiqiao"
 9) "furong"
10) "yangmi"
 
 
 
 
 

5.5 sortedSet(有序集合)数据结构

5.5.1 sortedSet图示

sortedset中存储的成员都有一个附带的分数值
而redis就可以根据分数来对成员进行各种排序(正序、倒序)
 
1、  sortedSet存储内容示意图:



 

5.5.2 SortedSet功能演示

1、往redis库中插入一条sortedset数据
redis 127.0.0.1:6379> zadd nansheng:yanzhi:bang  70 liudehua  90 huangbo  100 weixiaobao  250 yangwei  59 xiaotao
(integer) 5
 
 
2、  从sortedset中查询有序结果
#正序结果
redis 127.0.0.1:6379> zrange nanshen:yanzhi:bang  0  4
1) "xiaotao"
2) "liudehua"
3) "huangbo"
4) "weixiaobao"
5) "yangwei"
#倒序结果
redis 127.0.0.1:6379> zrevrange nanshen:yanzhi:bang 0 4
1) "yangwei"
2) "weixiaobao"
3) "huangbo"
4) "liudehua"
5) "xiaotao"
 
3、  查询某个成员的名次
#在正序榜中的名次
redis 127.0.0.1:6379> zrank nanshen:yanzhi:bang  xiaotao
(integer) 0
 
#在倒序榜中的名次
redis 127.0.0.1:6379> zrevrank nanshen:yanzhi:bang xiaotao
(integer) 4
 
4、修改成员的分数
redis 127.0.0.1:6379> zincrby nanshen:yanzhi:bang  300  xiaotao
"359"
redis 127.0.0.1:6379> zrevrank nanshen:yanzhi:bang xiaotao
(integer) 0
 
 
 

06/ Redis应用案例(1)

案例:
Lol盒子英雄数据排行榜:
1、  在redis中需要一个榜单所对应的sortedset数据
2、  玩家每选择一个英雄打一场游戏,就对sortedset数据的相应的英雄分数+1
3、  Lol盒子上查看榜单时,就调用zrange来看榜单中的排序结果
 
 

07/ Redis应用案例(2)

实现步骤:
1、  每来一个用户创建购物车,就对购物车中的每一件商品在redis中插入一条以商品名称为key的sortedset,成员为同时出现在购物车中的其他商品,分数为1
2、  每新产生一个购物车,就对车中的商品更新redis中的sortedset,将同时出现的商品的分数增1
3、  推荐时,用户放入一件商品到购物车,则从这件商品对应的sortedset中查询分数最高的同现商品,推荐给该用户
 
 
设计思想如下,详见《代码》

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: