Redis的搭建和使用

Redis

1 NoSQL

1.1 什么NoSQL

   Not Only SQL 泛指非关系型数据库。

随着web2.0的快速发展,非关系型、分布式数据存储得到了快速的发展,它们不保证关系数据的ACID特性。NoSQL概念在2009年被提了出来。NoSQL最常见的解释是“non-relational”,“Not Only SQL”也被很多人接受。(“NoSQL”一词最早于1998年被用于一个轻量级的关系数据库的名字。)

1.2 为什么要使用NoSQL

1.2.1传统的数据库遇到的瓶颈

 

传统的关系数据库具有不错的性能,高稳定型,久经历史考验,而且使用简单,功能强大,同时也积累了大量的成功案例。在互联网领域,MySQL成为了绝对靠前的王者,毫不夸张的说,MySQL为互联网的发展做出了卓越的贡献。

  在90年代,一个网站的访问量一般都不大,用单个数据库完全可以轻松应付。在那个时候,更多的都是静态网页,动态交互类型的网站不多。

到了最近10年,网站开始快速发展。火爆的论坛、博客、sns、微博逐渐引领web领域的潮流。在初期,论坛的流量其实也不大,如果你接触网络比较早,你可能还记得那个时候还有文本型存储的论坛程序,可以想象一般的论坛的流量有多大。

  • 高并发读写

Web2.0网站,数据库并发负载非常高,往往达到每秒上万次的读写请求

  • 高容量存储和高效存储

Web2.0网站通常需要在后台数据库中存储海量数据,如何存储海量数据并进行高效的查询往往是一个挑战

  • 高扩展性和高可用性

随着系统的用户量和访问量与日俱增,需要数据库能够很方便的进行扩展、维护

 

1.2.2 NoSql数据库的优势

  • 易扩展

  NoSQL数据库种类繁多,但是一个共同的特点都是去掉关系数据库的关系型特性。数据之间无关系,这样就非常容易扩展。也无形之间,在架构的层面上带来了可扩展的能力。

  • 大数据量,高性能

  NoSQL数据库都具有非常高的读写性能,尤其在大数据量下,同样表现优秀。这得益于它的无关系性,数据库的结构简单。一般MySQL使用Query Cache,每次表的更新Cache就失效,是一种大粒度的Cache,在针对web2.0的交互频繁的应用,Cache性能不高。而NoSQLCache是记录级的,是一种细粒度的Cache,所以NoSQL在这个层面上来说就要性能高很多了。

  • 灵活的数据模型

  NoSQL无需事先为要存储的数据建立字段,随时可以存储自定义的数据格式。而在关系数据库里,增删字段是一件非常麻烦的事情。如果是非常大数据量的表,增加字段简直就是一个噩梦。这点在大数据量的web2.0时代尤其明显。

  • 高可用

NoSQL在不太影响性能的情况,就可以方便的实现高可用的架构。比如CassandraHBase模型,通过复制模型也能实现高可用。

1.3 常见NoSQL产品

 

 

 

2 redis

2.1 redis简介

 

全称:REmote DIctionary Server(远程字典服务器)。是完全开源免费的,用C语言编写的, 遵守BCD协议。是一个高性能的(key/value)分布式内存数据库,

基于内存运行并支持持久化的NoSQL数据库,是当前最热门的NoSql数据库之一,也被人们称为数据结构服务器。

    redis的特点:

iRedis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用

iiRedis不仅仅支持简单的key-value类型的数据,同时还提供listsetzsethash等数据结构的存储

2) Redis支持数据的备份,即master-slave模式的数据备份

 

 

2.1 Redis优势

性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。

丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。

原子 – Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行。

丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性

2.2 redis应用场景

      新浪微博、facebook、电商系统的商品查询……

 

 

2.3 redis下载

1Http://redis.io/

 

2Http://www.redis.cn/

2.4 redis版本

3.x(支持集群)  2.x支持集群   (课程中是用3.0版本

    副版本号偶数时,表示稳定版本,建议生产环境中使用

    副版本号为基数时,表示是测试版本,不建议在生产环境中是用

2.5 redis安装

     redis没有基于windows的版本。在Linux系统中使用。课程中是用centOS。

2.5.1 单机版安装

2.5.1.1 安装gcc

安装文件是一个压缩包,但由于redis本身是采用c++ 编写的,所以解压完成后,需要编译和安装。所以先要在系统中安装 c++的编译器  gcc-c++

gcc的安装方式分为两种:

1 在线安装(有网络环境)

  yum install -y gcc-c++

2离线安装(无网络环境)

      可以 http://vault.centos.org/6.5/os/x86_64/Packages/

      下载需要的安装文件

       需要的安装文件如下:

ppl-0.10.2-11.el6.x86_64.rpm

ppl-devel-0.10.2-11.el6.x86_64.rpm

gmp-devel-4.3.1-7.el6_2.2.x86_64.rpm

       cloog-ppl-devel-0.15.7-1.2.el6.x86_64.rpm

cpp-4.4.7-4.el6.x86_64.rpm

glibc-devel-2.12-1.132.el6.x86_64.rpm

glibc-headers-2.12-1.132.el6.x86_64.rpm

libstdc++-4.4.7-4.el6.x86_64.rpm

libstdc++-devel-4.4.7-4.el6.x86_64.rpm

gcc-4.4.7-4.el6.x86_64.rpm

gcc-c++-4.4.7-4.el6.x86_64.rpm

kernel-headers-2.6.32-431.el6.x86_64.rpm

mpfr-2.4.1-6.el6.x86_64.rpm

 

 

逐个安装:

使用命令为:

 rpm ivh ***.rpm

2.5.1.2 安装redis

使用 root 用户登录,把redis安装文件(压缩文件)拷贝linux系统中

(这里拷贝到了root用户的opt目录中)

2 解压这个文件  tar –zxvf redis-3.0.0.tar.gz

3 进入解压后的目录,对里面的文件进行编译和安装

这里采用编译和安装同时进行的方式

命令:

     make install PREFIX=/usr/local/redis

/usr/local/redis是redis的安装路径,目录名不是一定要叫redis可以自己定义

4 进入redis的安装路径中bin路径,查看里面的文件

其中 redis-server文件就是redis的启动文件

5 ./redis-server 运行这个文件,如果看到一个图形界面,界面中显示redis的版本、软件位数、监听的端口(6379)、PID等信息

说明redis的安装和启动成功。

 

注意: 这里的启动叫做前置启动。

       前置启动的特点,当redis 启动后,linux操作界面将不能输入执行其他命令!!!

2.5.2 redis启动

      redis启动分为前置启动和后置启动。

2.5.2.1 前置启动(一般不用)

./redis-server

特点: redis启动后,将不能输入其他命令。

退出前置启动的方式 ctrl+c

 

 

 

2.5.2.2后置启动

redis的解压目录中拷贝 redis.conf redis的安装目录中(和redis-server在同一个目录)

2打开这个文件(vim命令),修改这个文件中 daemonize 的值为yes(默认no)

 

3在启动redis时, 使用 ./redis-server redis.conf(启动时,指定配置文件)

4测试后置启动是否成功:多种方式

(1) ps aux|grep redis   查看这个进程是否存在

(2) 使用redis自带的客户端工具

10.0.1 redis清空 

redis清空 

进入redis目录下 
redis-cli 
flushall 

 

./redis-cli进入

输入ping 如果返回一个pong 则表示redis启动成功

退出redis客户端的方式:  1 ctrl+c  2 quit 3 exit  

 

2.5.3 停止redis

l 强制结束程序。强制终止redis进程可能会导致redis持久化数据丢失。

 

l 正确停止redis的方式应该是向redis发送SHUTDOWN命令,方法为(关闭默认的端口)

[root@zrgk redis]# ./bin/redis-cli shutdown

 

2.6 redis文件介绍 redis.conf)

     1) daemonize yes         修改启动方式

     2) port 6379             redis端口

     3) database 16           redis默认开启16个库,也就是16个存储空间来存储数据

        注意:16个之间是数据隔离的

     4) cluster-enabled yes    配置redis集群 这个配置默认是被注释的,也就是默认不开启集群

     5) save 900 1            这个表示redis的持久化方案 (RDB)

     6)dbfilename dump.rdb    redis持久化时,存放数据的文件

     7)appendonly no          是否开启redis的aof 持久化方案

     8) appendfilename “appendonly.aof”  aof持久化方案存放的文件

2.7 redis持久化方案

     redis的持久化方案:两种 RDB方案  AOF方案

2.7.1 什么redis的持久化方案

     redis 除了可以作为缓存技术,也可以作为非关系型数据库。

     作为缓存技术,数据默认是存放在内存中的,(这样可以提升存取速度)

     但内存不是持久化设备,不能永久保存数据,一旦机器发生问题,

将会造成数据丢失。因此,redis为了解决这个文件,提供了数据的持久化方案(memcahe 没有这个能力)

 

2.7.2 RDB方案

     默认开启的一种持久化方案。它会根据时间轴,以及key的数量改变,来完成持久化动作。

save 900 1 该备份策略表示  900 秒内,如果有一个或多个key的值发生了变化就触发redis的持久化机制。

2.7.3 AOF方案

      该持久化方案,redis默认情况下没有开启。需要手动开启。

      redis.conf   appendonly no 配置成 yes

aof的持久化策略为:redis会记录当前用户执行的,且改变数据的命令。

这种持久化的密度会更细。当然也会对redis的性能产生影响。

注意:即使采用aof的持久化策略,一旦内存崩溃,也会至少丢失一秒的数据。

因此,在很多系统中RDB方案和AOF方案,同时开启

 

2.8 redis常用命令

2.8.1redis的常用类型

     String(字符串)

      Hash(哈希)

      List(链表)

      Set(集合)

      SortSet(有序集合)

2.8.2 常用命令展示

  set 将一个键值对以字符串类型存储

get 通过key获取value 。只能操作字符串类型

ping 测试redis是否链接 如果已链接返回 PONG

echo 测试redis是否链接 如果已链接返回 echo命令后给定的值

keys 返回所有的key 可以加*通配

exists 判断string类型一个key是否存在 如果存在返回1 否则返回0

del 删除一个key或者多个

expire 设置一个key的过期时间

ttl 查看建的失效时间

select 选择数据库(0-15)

move 将当前数据库中的key转移到其他数据库中

persist 移除给定key的过期时间

randomkey 随机返回key空间的一个key

type 返回值的类型

quit 退出连接

dbsize 返回当前数据库中的key的数目

info 获取服务器的信息和统计

flushdb 删除当前选择的数据库中的key

flushall 删除所有数据库中的所有key

 

 

如何使用以上命令?

进入redis客户端

./redis-cli,在客户端中使用以上命令

2.8.3 String(字符串)  

key  value形式存储数据

关于key的说明

1 key 不要太长,不要超过1024个字节。这样会消耗内存,也会降低查询效率

2 key 不要太短。这样会降低可读性。

3一个项目中,key的命名采用同一的规范。

 

   关于value说明:

      String类型存储时,value的最大长度为512M

 

命令:

1、 set (set key value)   键值对到redis中

set name “zhangsan”   “” 可加可不加

set password 123

set aa bb

2、 get (get key)       根据键取值

get name

   

3、 incr 让当前键值以1的数量递增,并返回递增后的值

set  aa  11

incr  aa   返回 12

4、 incrby  可以指定参数一次增加的数值,并返回递增后的值

set aa 11

incrby aa 10 返回 21

5、 decr 让当前键值以1的数量递减 并返回递减后的值

6、 decrby 可以指定参数一次递减的数值,并返回递减后的值

7、 incrbyfloat 可以递增一个双精度浮点数

8、 append 作用是向键值的末尾追加value。如果键不存在则将该键的值设置为value。返回值是追加后字符串的总长度。

set aa hello

append aa world  返回10 表示拼接后的值得长度

get aa 返回helloworld

9、 mget/mset 作用与get/set相似,不过mget/mset可以同时获得/设置多个键的键值

mset xxx 111 yyy 222 zzz 333

mget xxx yyy zzz

返回

“111”

“222”

“333”

2.8.4 Hash

redis的哈希是键值对的集合。 redis的哈希值是字符串字段和字符串值之间的映射,因此它们被用来表示对象

1 hset  存储一个哈希键值对的集合

hset user name zhangsan

hset user password 123

user                        根key

name、password             key

zhangsan  123               value

存储结构为:

 

 

 

 

 

 

 

 

        2hmset 存储一个或多个哈希是键值对的集合

           hmset user name password 123 address beijing

           存储结构同上

 

        3、hgetall 获取一个哈希是键值对的集合

           hgetall user

            返回

             “name”

             “zhangsan”

             “password”

             “123”

             “address”

             “beijing”

 

         注意:在使用hash结构存储数据时,很多时候使用如下方式存储数据

      hmset user:1 name aaa password 111

      hmset user:2 name bbb password 222

      hmset user:3 name ccc password 333

      这样的存储方式的意义在于   三条数据的根key拥有相同的前缀user

使用Redis Desktop Manager(基于windows的redis桌面工具)在使用这个工具连接redis时,需要关闭linux的防火墙

       

       

查看时,显示成文件夹结构

4hexists 判断哈希表中的字段名是否存在 如果存在返回1 否则返回0

    hexists user:1 name

5、hkeys 只返回字段名

    hkeys user:1

6、 hvals 只返回字段值

    hvals user:1

7、hdel 删除一个或多个字段

  hdel user:1 name

8hlen 获得字段长度

  hlen user:1 name

2.8.5 List(链表) 常用

1lpush 向链表左侧添加

           lpush user zhangsan

           lpush user lisi

           lpush user wangwu

2rpush 向链表右侧添加

           rpush user zhangsan

           rpush user lisi

           rpush user wangwu

 

 

3lpop  从左边弹出一个元素

4rpop  从右边弹出一个元素

5llen  返回链表中元素的个数 相当于关系型数据库中 select count(*)

6lrange将返回索引从start到stop之间的所有元素。Redis的列表起始索引为0。

   lrange 0 10

       lrange也支持负索引 lrange nn -2 -1  如 -1表示最右边第一个元素 -2表示最右边第二个元素,依次类推。

lrem  删除列表中前count个值为value的元素,返回值是实际删除的元素个数。根据count值得不同,老人命令的执行方式会略有不同

      1.count > 0 时lrem命令会从列表左边开始删除前count个值为value的元素

  2.count < 0 时lrem命令会从列表右边开始删除前count个值为value的元素

  3.count = 0 时lrem命令会删除所有值为value的元素

lindex 如果要将列表类型当做数组来用,lindex命令是必不可少的。lindex命令用来返回指定索引的元素,索引从0开始

       如果是负数表示从右边开始计算的索引,最右边元素的索引是-1。

lset   是另一个通过索引操作列表的命令,它会将索引为index的元素赋值为value。

2.8.6 Set(集合) 常用

       redis的集合是字符串的无序集合。在Redis您可以添加,删除和测试文件是否存在,在成员O(1)的时间复杂度。

示例:

redis 127.0.0.1:6379> sadd tutoriallist redis

(integer) 1

redis 127.0.0.1:6379> sadd tutoriallist mongodb

(integer) 1

redis 127.0.0.1:6379> sadd tutoriallist rabitmq

(integer) 1

redis 127.0.0.1:6379> sadd tutoriallist rabitmq

(integer) 0

redis 127.0.0.1:6379> smembers tutoriallist

1) "rabitmq"

2) "mongodb"

3) "redis"

2.8.7 SortSet(有序集合) 不常用

        Redis的有序集合类似于Redis的集合,字符串不重复的集合。

示例:

redis 127.0.0.1:6379> zadd tutoriallist 0 redis

(integer) 1

redis 127.0.0.1:6379> zadd tutoriallist 0 mongodb

(integer) 1

redis 127.0.0.1:6379> zadd tutoriallist 0 rabitmq

(integer) 1

redis 127.0.0.1:6379> zadd tutoriallist 0 rabitmq

(integer) 0

redis 127.0.0.1:6379> ZRANGEBYSCORE tutoriallist 0 1000

1) "redis"

2) "mongodb"

3) "rabitmq"

2.8.8 keys通用操作

 

 

 

 

 

 

 

 

 

 

 

 

2.8.9 多个数据库

 

 

 

切换至0数据库下面

 

 newkey移动到1号数据库

 

 

2.8 redis集群

2.9.1 为什么要集群

点单故障:模式(master slave)

可用:集群

2.9.2 集群架构图

 

2.9.3 投票机制

 

由于投票机制的存在,redis的主机个数必须是单数。最少需要三台主机。每个主机安排一个备机的话,至少需要六台机器才能完成集群

2.9.4 集群环境准备

redis-trib.rb:在redis解压后的src目录下有个叫redis-trib.rb文件。该文件可以帮助我们创建redis集群。

2.9.4.1 安装ruby解释器

由于redis-trib.rb 是一个ruby语言所写的一个脚本程序,那么我们的linux中需要安装ruby的解释器

在线安装命令:yum install -y ruby

在没有网络的环境下,也可以手动安装。但版本匹配问题,相当复杂

1、下载ruby解释器的压缩包(ruby-2.4.0.tar.gz

2、解压  (tar -zxvf ruby-2.4.0.tar.gz)  

3、进入解压目录 (cd ruby-2.4.0)

4、执行如下命令:

a.    ./configure

b.    make

c.    make install

d.    ruby –version测试安装是否成功

 

2.9.4.2安装ruby的包管理器

在线安装:

yum install -y rubygems

 

没有网络环境时,也可以手动安装步骤很复杂

1、下载ruby的包管理器 (rubygems-2.6.10.tar

2、解压(tar -zxvf rubygems-2.6.10.tar.gz

3、进入解压目录(cd rubygems-2.6.10.tar.gz

4、执行 ruby setup.rb

5、测试是否安装成功 gem –version

 

2.9.4.3 执行redis3.0.0.gem

创建redis集群时,需要运行的redis-trib.rb文件需要用到一些包。这些包在redis3.0.0.gem在这个文件中。该文件并没有存放在解压的redis的目录下。需要我们额外下载。

下载完成后,执行 gem install redis3.0.0.gem

2.9.5 集群搭建

实际工作搭建集群需要多台物理设备,在每台物理设备上安装一个redis进行集群的搭建

这里搭建的方式叫做伪集群(因为无法提供多台设备在一个虚拟机中安装多个redis实例)

集群的搭建方式和物理集群的搭建方式几乎相同学会了伪集群的搭建就可以完成真正集群的搭建。

1、 创建6个redis实例

由于redis在创建集群时,需要考虑到单点故障以及高可用。所以需要采用主备模式。同时redis在管理集群判断节点是否健康采用的是投票策略,所以主节点的数量一定是单数的。

如:创建redis的最小的集群,应该是三个实例,在加上主备模式,那么一共应该是6个redis实例。

        2、我们可以将已经安装好的redis拷贝6份。注意:在拷贝创建集群节点时。在该实例中一定更要将备份文件删除掉(dump.rdb)。否则创建集群时会出问题。

3、 开启六个实例中的集群 redis.conf中配置cluster-enabled yes

4、 当前我们是在一台虚拟机中同时运行六个redis实例。所以我们需要将端口做一个改变。可以改变成任何端口。只要没有被占用就可以。

5、 redis在创建集群时,要求只能对已启动的redis创建集群。所以我们需要将六个redis启动

6、 每次分别启动6个实例比较麻烦,我们可以创建一个startall.sh(类似windows中的.bat文件)

vim startall.sh

 cd redis01

 ./redis-server redis.conf

 cd ../redis02

 ./redis-server redis.conf

cd ../redis03

 ./redis-server redis.conf

 

        7、.sh文件在创建完成后,并不是可执行的。需要做如下配置

           文件的执行必须要分配一个可执行权限

命令为:chmod +x startall.sh

+x:表示为分配一个可执行的权限。

        8、把redis-trib.rb拷贝到集群目录下,使用这个文件来创建集群

 

           ./redis-trib.rb create  --replicas 1 192.168.10.155:8001 192.168.10.155:8002 192.168.10.155:8003 192.168.10.155:8004 192.168.10.155:8005  192.168.10.155:8006

 

           上面的红色的1表示创建集群时,主备模式的策略,1表示11

 

   Can I set the above configuration? (type 'yes' to accept): 到这一步输入yes,开始创建集群

 

 

        9redis集群在做数据存储时采用的hash算法来进行值的存储

redis集群会准备16384个槽。他会将这些槽平分到主节点上。

当有数据要进行存储时,redis会使用CRC16算法,先将key做16进制的转换,然后使用该值与16384取模,该值就决定了当前要将这个key与value存储到哪个节点中。

10、如何连接到集群中的节点?

redis允许我们使用任何一个节点下的客户端工具连接到集群中的任何一个节点下。

./redis-cli -h 192.168.10.155 -p 8001 -c  

注意:在连接集群时一定要给定一个参数 -c

该参数表示告知客户端当前我连接的是集群中的一个节点。

11、查看集群中的节点信息

cluster nodes

          12、如何关闭集群

redis运行使用任何一个实例下的客户端工具,来关闭集群中的任何一个节点。

我们可以编写一个一次性关闭集群中所有节点中的脚本

shutdownall.sh

            redis01/redis-cli -h IP -p Port shutdown

java语言操作redis

Java语言操作redis 要使用 jedis.jar这个jar(这里使用jedis2.7.2.jar版本)

由于后面涉及到池的操作,所以还需要导入 commons-pool2.jar

3.1连接redis单机版(直连方式)

@Test

public void test1(){   // 连单机版(直连)

Jedis jedis = new Jedis("192.168.50.131",6379);

jedis.set("bbb", "222");

String value = jedis.get("bbb");

        System.out.println(value);

        jedis.close();

}

3.2连接redis单机版(池连方式)

@Test

public void test2(){  // 单机(池连)

 

JedisPool pool = new JedisPool("192.168.50.131", 6379);  

Jedis j = pool.getResource();

 

String str = j.get("ccc");

 

System.out.println(str);

 

j.close();

 

}

 

 

3.3连接redis集群版

  @Test

public void test3(){  //连集群

 

Set<HostAndPort> set = new HashSet<HostAndPort>();

 

set.add(new HostAndPort("192.168.50.131",8001));

set.add(new HostAndPort("192.168.50.131",8002));

set.add(new HostAndPort("192.168.50.131",8003));

set.add(new HostAndPort("192.168.50.131",8004));

set.add(new HostAndPort("192.168.50.131",8005));

set.add(new HostAndPort("192.168.50.131",8006));

JedisCluster cluster = new JedisCluster(set);

cluster.set("name","zhangsan");

String value = cluster.get("name");

System.out.println(value+"<<<");

 

cluster.hset("user","name","aaa");

 

String value2 = cluster.hget("user","name");

 

System.out.println(value2+"...");

// 一定不要关集群

}

 

  

 

 

 

3.4spring整合 单机版池连方式

  <bean id="jedisPool" class="redis.clients.jedis.JedisPool">

         <constructor-arg name="host">

             <value>192.168.50.131</value>

         </constructor-arg>

         <constructor-arg name="port">

             <value>6379</value>

         </constructor-arg>

     </bean>

          

     @Test

public void test4(){  // spring整合 JedisPool

 

ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

JedisPool pool = (JedisPool)ac.getBean("jedisPool");

Jedis j = pool.getResource();

 

String value = j.get("aaa");

System.out.println(value+"###");

j.close();

}

 

3.5spring整合 单机版池连方式(使用自定义的池策略)

  <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">

<!-- 最大连接数 -->

<property name="maxTotal" value="30" />

<!-- 最大空闲连接数 -->

<property name="maxIdle" value="10" />

<!-- 每次释放连接的最大数目 -->

<property name="numTestsPerEvictionRun" value="1024" />

<!-- 释放连接的扫描间隔(毫秒) -->

<property name="timeBetweenEvictionRunsMillis" value="30000" />

<!-- 连接最小空闲时间 -->

<property name="minEvictableIdleTimeMillis" value="1800000" />

<!-- 连接空闲多久后释放, 当空闲时间>该值 且 空闲连接>最大空闲连接数 时直接释放 -->

<property name="softMinEvictableIdleTimeMillis" value="10000" />

<!-- 获取连接时的最大等待毫秒数,小于零:阻塞不确定的时间,默认-1 -->

<property name="maxWaitMillis" value="1500" />

<!-- 在获取连接的时候检查有效性, 默认false -->

<property name="testOnBorrow" value="true" />

<!-- 在空闲时检查有效性, 默认false -->

<property name="testWhileIdle" value="true" />

<!-- 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true -->

<property name="blockWhenExhausted" value="false" />

</bean>

<!-- jedis客户端单机版 -->

<bean id="redisClient" class="redis.clients.jedis.JedisPool">

<constructor-arg name="host" value="192.168.50.131"></constructor-arg>

<constructor-arg name="port" value="6379"></constructor-arg>

<constructor-arg name="poolConfig" ref="jedisPoolConfig"></constructor-arg>

</bean>

 

 

3.6spring整合 集群版

<bean id="jedisCluster" class="redis.clients.jedis.JedisCluster">

          <constructor-arg name="nodes">

              <set>

                  <bean class="redis.clients.jedis.HostAndPort">

                      <constructor-arg name="host">

                           <value>192.168.50.131</value>

                      </constructor-arg>

                      <constructor-arg name="port">

                           <value>8001</value>

                      </constructor-arg>

                  </bean>

                  <bean class="redis.clients.jedis.HostAndPort">

                      <constructor-arg name="host">

                           <value>192.168.50.131</value>

                      </constructor-arg>

                      <constructor-arg name="port">

                           <value>8002</value>

                      </constructor-arg>

                  </bean>

                  <bean class="redis.clients.jedis.HostAndPort">

                      <constructor-arg name="host">

                           <value>192.168.50.131</value>

                      </constructor-arg>

                      <constructor-arg name="port">

                           <value>8003</value>

                      </constructor-arg>

                  </bean>

                  <bean class="redis.clients.jedis.HostAndPort">

                      <constructor-arg name="host">

                           <value>192.168.50.131</value>

                      </constructor-arg>

                      <constructor-arg name="port">

                           <value>8004</value>

                      </constructor-arg>

                  </bean>

                  <bean class="redis.clients.jedis.HostAndPort">

                      <constructor-arg name="host">

                           <value>192.168.50.131</value>

                      </constructor-arg>

                      <constructor-arg name="port">

                           <value>8005</value>

                      </constructor-arg>

                  </bean>

                  <bean class="redis.clients.jedis.HostAndPort">

                      <constructor-arg name="host">

                           <value>192.168.50.131</value>

                      </constructor-arg>

                      <constructor-arg name="port">

                           <value>8006</value>

                      </constructor-arg>

                  </bean>

              </set>

          </constructor-arg>

     </bean>

      @Test

public void test6(){  // Spring整合 JedisCluster

ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

 

    JedisCluster cluster = (JedisCluster)ac.getBean("jedisCluster");

 

    String name=cluster.hget("user","name");

    

    System.out.println(name+"%%%");

}

 

 

 

4 redisweb项目中的应用

   redis的操作一般放在业务层,由于在各个业务层操作redis的代码都相似,所以,一般会提取出一个类。这里叫做 JedisDao(接口)  JedisSingeDaoImpl(对单机操作的实现类)

JedisClusterDaoImpl(对集群操作的实现类)

  在这个类中封装对Jedis操作的各种方法

public String set(String key,String value);

public String get(String key);

public long hset(String hkey,String key,String value);

public String hget(String hkey,String key);

public long incr(String key);

public long expire(String key,int second);

public long ttl(String key);

public long del(String key);

public long hdel(String hkey,String key);

 

 

service的方法中这样使用:

   以查询全部为例:

@Override

public String getAllUser() {

try{   //从redis中取值

String json = this.jedisDao.hget("user","all");

if(json!=null && !"".equals(json)){

return json;

}

}catch(Exception e){

e.printStackTrace();

}

List<User> list = dao.findAllUser();

String str = JsonUtils.objectToJson(list);

try{  // 存入redis

this.jedisDao.hset("user","all",str);

 

}catch(Exception e){

e.printStackTrace();

}

return str;

}

UserDao的正常查询方法之前和之后,添加操作redis的代码

redis中查询数据,如果查到,则不会走UserDao中查询数据的方法

如果没有查到,执行UserDao中的查询方法,查到后,把数据存入redis

 

注意:这里操作redis的代码都放在了try{} catch(){} 中。因为当redis出问题时,不能中断正常的流程!!!

 

当对数据做了增删改操作时,要同步redis中的数据,这里不需要去修改redis中的数据,只需要将数据从redis中删除即可!!!

@Override

public void modifyUser(User user) {

 

jedisDao.hdel("user","all");  // 同步数 据,这里没有必要去修改redis中的数据,直接将redis中数据删除

dao.updateUser(user);

 

}

 

这里用到了一个json转换的工具类,使用的事jackson(很多框架中对json的处理都使用这个)。项目中也做了相应的代码!!!

package util;

 

import java.util.List;

 

import com.fasterxml.jackson.core.JsonProcessingException;

import com.fasterxml.jackson.databind.JavaType;

import com.fasterxml.jackson.databind.ObjectMapper;

 

 

public class JsonUtils {

 

    // 定义jackson对象

    private static final ObjectMapper MAPPER = new ObjectMapper();

 

    /**

     * 将对象转换成json字符串。

     * <p>Title: pojoToJson</p>

     * <p>Description: </p>

     * @param data

     * @return

     */

    public static String objectToJson(Object data) {

     try {

String string = MAPPER.writeValueAsString(data);

return string;

} catch (JsonProcessingException e) {

e.printStackTrace();

}

     return null;

    }

    

    /**

     * json结果集转化为对象

     *

     * @param jsonData json数据

     * @param clazz 对象中的object类型

     * @return{key:value,key:value}

     */

    public static <T> T jsonToPojo(String jsonData, Class<T> beanType) {

        try {

            T t = MAPPER.readValue(jsonData, beanType);

            return t;

        } catch (Exception e) {

         e.printStackTrace();

        }

        return null;

    }

    

    /**

     * json数据转换成pojo对象list

     * <p>Title: jsonToList</p>

     * <p>Description: </p>

     * @param jsonData

     * @param beanType

     * @return[{},{}]

     */

    public static <T>List<T> jsonToList(String jsonData, Class<T> beanType) {

     JavaType javaType = MAPPER.getTypeFactory().constructParametricType(List.class, beanType);

     try {

     List<T> list = MAPPER.readValue(jsonData, javaType);

     return list;

} catch (Exception e) {

e.printStackTrace();

}

    

     return null;

    }

    

}

 

测试工具类

package util;

 

import java.util.ArrayList;

import java.util.Date;

import java.util.List;

 

import domain.User;

 

public class TestJackson {

 

public static void main(String[] args) {

 

/* User user = new User(1,"zhangsan",20,new Date());

 

String str = JsonUtils.objectToJson(user);

 

System.out.println(str);*/

 

/* String jsonStr = "{\"id\":1,\"name\":\"zhangsan\",\"age\":20,\"birthday\":1489930990995}";

 

User user = (User)JsonUtils.jsonToPojo(jsonStr, User.class);

 

System.out.println(user.getId()+"\t"+user.getName()+"\t"+user.getAge()+"\t"+user.getBirthday());

 

    */

 

/*User user2 = new User(2,"lisi",22,new Date());

User user1 = new User(1,"zhangsan",21,new Date());

User user3 = new User(3,"wangwu",23,new Date());

 

List<User> list = new ArrayList<User>();

list.add(user1);

list.add(user2);

list.add(user3);

 

String str = JsonUtils.objectToJson(list);

 

System.out.println(str);*/

 

String jsonStr = "[{\"id\":1,\"name\":\"zhangsan\",\"age\":21,\"birthday\":1489931282127},{\"id\":2,\"name\":\"lisi\",\"age\":22,\"birthday\":1489931282127},{\"id\":3,\"name\":\"wangwu\",\"age\":23,\"birthday\":1489931282127}]";

 

List<User> list = JsonUtils.jsonToList(jsonStr, User.class);

 

for(User user:list){

System.out.println(user.getId()+"\t"+user.getName()+"\t"+user.getAge()+"\t"+user.getBirthday());

}

 

}

 

}

posted on 2018-09-09 17:55  云作  阅读(6891)  评论(0编辑  收藏  举报

导航