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性能不高。而NoSQL的Cache是记录级的,是一种细粒度的Cache,所以NoSQL在这个层面上来说就要性能高很多了。
- 灵活的数据模型
NoSQL无需事先为要存储的数据建立字段,随时可以存储自定义的数据格式。而在关系数据库里,增删字段是一件非常麻烦的事情。如果是非常大数据量的表,增加字段简直就是一个噩梦。这点在大数据量的web2.0时代尤其明显。
- 高可用
NoSQL在不太影响性能的情况,就可以方便的实现高可用的架构。比如Cassandra,HBase模型,通过复制模型也能实现高可用。
1.3 常见的NoSQL产品
2 redis
2.1 redis简介
全称:REmote DIctionary Server(远程字典服务器)。是完全开源免费的,用C语言编写的, 遵守BCD协议。是一个高性能的(key/value)分布式内存数据库,
基于内存运行并支持持久化的NoSQL数据库,是当前最热门的NoSql数据库之一,也被人们称为数据结构服务器。
redis的特点:
i)Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用
ii)Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储
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下载
(1)Http://redis.io/
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
1 使用 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后置启动
1 到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
存储结构为:
2、hmset 存储一个或多个哈希是键值对的集合
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的防火墙
查看时,显示成文件夹结构
4、hexists 判断哈希表中的字段名是否存在 如果存在返回1 否则返回0
hexists user:1 name
5、hkeys 只返回字段名
hkeys user:1
6、 hvals 只返回字段值
hvals user:1
7、hdel 删除一个或多个字段
hdel user:1 name
8、hlen 获得字段长度
hlen user:1 name
2.8.5 List(链表) 不常用
1、lpush 向链表左侧添加
lpush user zhangsan
lpush user lisi
lpush user wangwu
2、rpush 向链表右侧添加
rpush user zhangsan
rpush user lisi
rpush user wangwu
3、lpop 从左边弹出一个元素
4、rpop 从右边弹出一个元素
5、llen 返回链表中元素的个数 相当于关系型数据库中 select count(*)
6、lrange将返回索引从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表示1主1备
Can I set the above configuration? (type 'yes' to accept): 到这一步输入yes,开始创建集群
9、redis集群在做数据存储时采用的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
3 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.4和spring整合 单机版池连方式
<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.5和spring整合 单机版池连方式(使用自定义的池策略)
<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.6和spring整合 集群版
<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 redis在web项目中的应用
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()); }
}
} |