1,什么是redis
Redis(全称:Remote Dictionary Server 远程字典服务)是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
2,使用redis的优点
性能极高 – Redis能支持超过 100K+ 每秒的读写频率。
丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
原子 – Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行。
丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。
3,使用redis的缺点
是数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。
总结: Redis受限于特定的场景,专注于特定的领域之下,速度相当之快,目前还未找到能替代使用产品。
4,redis的安装
Redis是c语言开发的。
安装redis需要c语言的编译环境。如果没有gcc需要在线安装。yum install gcc-c++
安装步骤:
第一步:redis的源码包上传到linux系统。
第二步:解压缩redis。
第三步:编译。进入redis源码目录。make
第四步:安装。make install PREFIX=/usr/local/redis
PREFIX参数指定redis的安装目录。一般软件安装到/usr目录下
5,redis的连接
5.1,redis的启动
前端启动:在redis的安装目录下直接启动redis-server.
[root@localhost bin]# ./redis-server
后台启动:
把/root/redis-3.0.0/redis.conf复制到/usr/local/redis/bin目录下
[root@localhost redis-3.0.0]# cp redis.conf /usr/local/redis/bin/
后台启动的话,需要修改配置文件,打开安装的redis的文件目录,打开里面的redis.conf的配置文件,把配置文件的daemonize no改成daemonize yes,使用vim命令进行修改,使用“/内容”进行查找。
启动
[root@localhost bin]# ./redis-server redis.conf
判断是否启动可查看系统进程
[root@localhost bin]# ps aux|grep redis
5.2,Redis-cli
命令行使用
[root@localhost bin]# ./redis-cli
默认连接localhost运行在6379端口的redis服务
[root@localhost bin]# ./redis-cli -h 192.168.25.153 -p 6379
-h:连接的服务器的地址
-p:服务的端口号
关闭redis
[root@localhost bin]# ./redis-cli shutdown
5.3,Redis的五种数据类型
主要介绍常用的String和hash,有时间会出命令大全
keys * 查询所有键
5.3.1,String类型
String:key-value(做缓存)
Redis中所有的数据都是字符串。命令不区分大小写,key是区分大小写的。Redis是单线程的。Redis中不适合保存内容大的数据。
存取数据,set和get方法
127.0.0.1:6379> set name zhangsan
OK
127.0.0.1:6379> get name
"zhangsan"
自增自减,incr和decr,没有则自动创建
127.0.0.1:6379> incr ago
(integer) 1
127.0.0.1:6379> get ago
"1"
5.3.2,Hash类型(做缓存)
相当于一个key对于一个map,map中还有key-value
使用hash对key进行归类。
Hset:向hash中添加内容
Hget:从hash中取内容
127.0.0.1:6379> hset key name zhangsan
(integer) 1
127.0.0.1:6379> hget key name
"zhangsan"
5.3.3,List类型
有顺序可重复
5.3.4,Set类型
无序不重复
5.3.5,SortedSet类型
有序不重复
5.4,Key命令
expire key second:设置key的过期时间
ttl key:查看key的有效期(-1代表持久化,-2代表已过期)
persist key:清除key的过期时间。Key持久化
127.0.0.1:6379> expire a 50
(integer) 1
127.0.0.1:6379> ttl a
(integer) 34
127.0.0.1:6379> persist a
(integer) 1
127.0.0.1:6379> ttl a
(integer) -1
6,Redis的持久化方案
Redis的所有数据都是保存到内存中的。
:Rdb
Rdb:(默认方案)快照形式,定期把内存中当前时刻的数据保存到磁盘。Redis默认支持的持久化方案,存在丢失数据的风险。
—在redis.conf配置文件中配置
save 900 1
save 300 10
save 60 a0000
900秒内执行1次的话会在900秒的时候持久化数据…
:AOF
aof形式:append only file。把所有对redis数据库操作的命令,增删改操作的命令。保存到文件中。数据库恢复时把所有的命令执行一遍即可,会降低redis的性能。
—在redis.conf配置文件中配置
appendonly yes
7,Redis集群的搭建
7.1,redis-cluster架构图
redis-cluster投票:容错,有超过一半的认为其中一个出问题了,那真个redis就集体罢工了
架构细节:
(1)所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽.
(2)节点的fail是通过集群中超过半数的节点检测失效时才生效.
(3)客户端与redis节点直连,不需要中间proxy层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可
(4)redis-cluster把所有的物理节点映射到[0-16383]slot上,cluster 负责维护node<->slot<->value
如何让n太服务器共同承担文件存储任务,而不是由一个服务器工作?
Redis 集群中内置了 16384 个哈希槽,当需要在 Redis 集群中放置一个 key-value 时,redis 先对 key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点
7.,2,Redis集群的搭建
Redis集群中至少应该有三个节点。要保证集群的高可用,需要每个节点有一个备份机。
Redis集群至少需要6台服务器。
搭建伪分布式。可以使用一台虚拟机运行6个redis实例。需要修改redis的端口号7001-7006
7.2.1,集群搭建环境
1、使用ruby脚本搭建集群。需要ruby的运行环境。
安装ruby
yum install ruby
yum install rubygems
2、安装ruby脚本运行使用的包。
[root@localhost ~]# gem install redis-3.0.0.gem
Successfully installed redis-3.0.0
1 gem installed
Installing ri documentation for redis-3.0.0...
Installing RDoc documentation for redis-3.0.0...
3.编译rb脚本文件
查询后缀名为rb的文件
[root@localhost ~]# cd redis-3.0.0/src
[root@localhost src]# ll *.rb
-rwxrwxr-x. 1 root root 48141 4月 1 2015 redis-trib.rb
把脚本拷贝到6个redis的文件夹中
[root@Jeck1 src]# cp redis-trib.rb /usr/local/redis-cluster
rb脚本运行还需要第三方库,redis3.0.0.gem
[root@Jeck1 local]# gem install redis-3.0.0
7.2.2,搭建步骤(关闭防火墙)
需要6台redis服务器。搭建伪分布式。
需要6个redis实例。
需要运行在不同的端口7001-7006
第一步:创建6个redis实例,每个实例运行在不同的端口。需要修改redis.conf配置文件。配置文件中还需要把cluster-enabled yes前的注释去掉。
第二步:启动每个redis实例。
设置启动脚本文件start-all.sh
第三步:使用ruby脚本搭建集群
[root@Jeck1 redis-cluster]# ./redis-trib.rb create --replicas 1
192.168.240.129:7001
192.168.240.129:7002
192.168.240.129:7003
192.168.240.129:7004
192.168.240.129:7005
192.168.240.129:7006
系统说把7001-7003作为主数据库
把7004-7006作为备用数据库,其中7001和7003相对应其他类似
每个数据库分配的哈希槽也告诉了
7.2.3,集群的使用方法
Redis-cli连接集群
[root@localhost redis-cluster]# redis01/redis-cli -p 7002 -c
-c:代表连接的是redis集群
8,Jedis
需要把jedis依赖的jar包添加到工程中。Maven工程中需要把jedis的坐标添加到依赖。推荐添加到服务层。
8.1,连接单机版
第一步:创建一个Jedis对象。需要指定服务端的ip及端口。
第二步:使用Jedis对象操作数据库,每个redis命令对应一个方法。
第三步:打印结果。
第四步:关闭Jedis
@Test
public void jedisTest() {
// 创建jedis对象
Jedis jedis = new Jedis("192.168.240.129", 6379);
//使用jedis对象执行操作
jedis.set("name", "张三");
jedis.set("name", "lisi");
String string = jedis.get("name");
System.out.println(string);
//关闭连接
jedis.close();
}
8.2,连接单机版使用连接池
第一步:创建一个JedisPool对象。需要指定服务端的ip及端口。
第二步:从JedisPool中获得Jedis对象。
第三步:使用Jedis操作redis服务器。
第四步:操作完毕后关闭jedis对象,连接池回收资源。
第五步:关闭JedisPool对象。
@Test
public void jedisPoolTest() {
//创建连接池对象
JedisPool jedisPool=new JedisPool("192.168.240.129",6379);
//获取jedis
Jedis jedis = jedisPool.getResource();
//执行操作
String string = jedis.get("name");
System.out.println(string);
//关闭连接,连接池回收资源
jedis.close();
//关闭连接池
jedisPool.close();
}
8.3,连接集群版
第一步:使用JedisCluster对象。需要一个Set参数。Redis节点的列表。
第二步:直接使用JedisCluster对象操作redis。在系统中单例存在。
第三步:打印结果
第四步:系统关闭前,关闭JedisCluster对象。
@Test
public void jedisClusterTest() {//集群使用
//创建一个JedisCluster对象,有一个参数nodes是一个set类型,set中包含着若干个HostAndPort对象
Set<HostAndPort> nodes=new HashSet<>();
nodes.add(new HostAndPort("192.168.240.129",7001));
nodes.add(new HostAndPort("192.168.240.129",7002));
nodes.add(new HostAndPort("192.168.240.129",7003));
nodes.add(new HostAndPort("192.168.240.129",7004));
nodes.add(new HostAndPort("192.168.240.129",7005));
nodes.add(new HostAndPort("192.168.240.129",7006));
JedisCluster jedisCluster=new JedisCluster(nodes);
//直接使用JedisCluster对象操作redis
jedisCluster.set("name", "zhangsan");
jedisCluster.set("ago", "12");
String string = jedisCluster.get("name");
System.err.println(string);
String string2 = jedisCluster.get("ago");
System.err.println(string2);
//关闭JidesCluster对象
jedisCluster.close();
}
9,向业务逻辑中添加缓存
9.1,接口封装
常用的操作redis的方法提取出一个接口,分别对应单机版和集群版创建两个实现类
9.2 配置applicationContext-redis.xml
9.2.1,配置单机版
<bean id="jedisClientPool" class="cn.e3mall.common.jedis.JedisClientPool">
<property name="jedisPool" ref="jedisPool"></property>
</bean>
<bean id="jedisPool" class="redis.clients.jedis.JedisPool">
<!-- 构造方法参数使用constructor-arg -->
<constructor-arg name="host" value="192.168.240.129"></constructor-arg>
<constructor-arg name="port" value="6379"></constructor-arg>
</bean>
9.2.2,配置集群版
<bean id="jedisClientCluster" class="cn.e3mall.common.jedis.JedisClientCluster">
<property name="jedisCluster" ref="jedisCluster"></property>
</bean>
<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.240.129"></constructor-arg>
<constructor-arg name="port" value="7001"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.240.129"></constructor-arg>
<constructor-arg name="port" value="7002"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.240.129"></constructor-arg>
<constructor-arg name="port" value="7003"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.240.129"></constructor-arg>
<constructor-arg name="port" value="7004"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.240.129"></constructor-arg>
<constructor-arg name="port" value="7005"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.240.129"></constructor-arg>
<constructor-arg name="port" value="7006"></constructor-arg>
</bean>
</set>
</constructor-arg>
</bean>
9.3,书写Test进行操作实现
单机版和集群版2选1,实现接口的好处,在开发阶段可实现单机版进行开发,在正式上线的时候可修改配置文件,实现集群版
@Test
public void demo1() {
//初始化spring容器
ApplicationContext ac=new ClassPathXmlApplicationContext("classpath:spring/applicationContext-redis.xml");
//从spring容器中拿出jedisClient对象
JedisClient jedis = (JedisClient) ac.getBean(JedisClient.class);//单机版和集群版都是JedisClient的实现类
//惊醒操作
jedis.set("name","zhangsan");
String string = jedis.get("name");
System.out.println(string);
}
9.4,给项目中正式添加redis
9.4.1,给service添加变量
@Autowired
private JedisClient jedisClient;
9.4.2,具体实现
//查询缓存
try {
String hget = jedisClient.hget(content_list, categoryId+"");
if(!StringUtils.isEmpty(hget)) {
List<TbContent> selectByExample = JsonUtils.jsonToList(hget, TbContent.class);
EasyUIDataResult result=new EasyUIDataResult();
result.setRows(selectByExample);
//取分页结果
PageInfo<TbContent> pageInfo=new PageInfo<TbContent>(selectByExample);
long total = pageInfo.getTotal();
result.setTotal(total);
return result;
}
} catch (Exception e) {
e.printStackTrace();
}
//redis查询不到就查询数据库,并把结果添加到缓存
try {
jedisClient.hset("content_list", categoryId+"", JsonUtils.objectToJson(selectByExample));
} catch (Exception e) {
e.printStackTrace();
}
9.4.3,缓存同步
给查询列表添加redis后,如果进行修改删除更新操作,而redis中数据没有改变,这时候就需要缓存同步到redis,已便修改删除更新操作之后再redis中保存的是最新的数据。
思路是在修改更新删除操作后删除redis存储的数据,重新从数据库中查到最新数据保存到redis中。
//缓存同步, 删除缓存中对应的数据
jedisClient.hdel(content_list, content.getCategoryId().toString());