Redis
Redis介绍
redis是一款开源的Key-Value数据库,运行在内存中,由ANSI C编写。企业开发通常采用Redis来实现缓存。同类的产品还有Memcache 、Memcached 、MongoDB等。
什么是Redis
Redis本质上是一个Key-Value类型的内存数据库,很像memcached,整个数据库统统加载在内存当中进行操作,定期通过异步操作把数据库数据flush到硬盘上进行保存。因为是纯内存操作,Redis的性能非常出色,每秒可以处理超过 10万次读写操作,是已知性能最快的Key-Value DB。 Redis的出色之处不仅仅是性能,Redis最大的魅力是支持保存多种数据结构,此外单个value的最大限制是1GB,不像 memcached只能保存1MB的数据,因此Redis可以用来实现很多有用的功能,比方说用他的List来做FIFO双向链表,实现一个轻量级的高性 能消息队列服务,用他的Set可以做高性能的tag系统等等。另外Redis也可以对存入的Key-Value设置expire时间,因此也可以被当作一 个功能加强版的memcached来用。 Redis的主要缺点是数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。
Redis出现的原因
-
由于关系型数据库检索效率低(因为有大量的io操作),为了提高效率,使用redis 远程字典
-
像淘宝京东这样的首页每天有大量的人访问,对数据库造成很大的访问压力,甚至是瘫痪。那如何解决呢?我们通常的做法有两种:一种是数据缓存、一种是网页静态化。我们今天讨论第一种解决方案。
Redis的作用:
把数据存放到内存中,作为数据库使用缓存技术
Spring Data Redis
Spring-data-redis是spring大家族的一部分,提供了在srping应用中通过简单的配置访问redis服务,对reids底层开发包(Jedis, JRedis, and RJC)进行了高度封装,RedisTemplate提供了redis各种操作、异常处理及序列化,支持发布订阅,并对spring 3.1 cache进行了实现。
spring-data-redis针对jedis提供了如下功能:
- 连接池自动管理,提供了一个高度封装的“RedisTemplate”类
- 针对jedis客户端中大量api进行了归类封装,将同一类型操作封装为operation接口
- ValueOperations:简单K-V操作
- SetOperations:set类型数据操作
- ZSetOperations:zset类型数据操作
- HashOperations:针对map类型的数据操作
- ListOperations:针对list类型的数据操作
小知识
3.x(支持集群) 2.x不支持集群 (3.0)
副版本号为偶数时,表示是稳定版本,建议在生产环境中使用
副版本号为奇数时,表示是测试版本,不建议在生产环境中是用
没有Windows版本哦
因为目前Linux版本已经相当稳定,而且用户量很大,无需开发windows版本,反而会带来兼容性等问题。
Linux版本Redis的安装
这里介绍的是单机版安装
第一步 安装gcc:
由于redis本身是采用c++ 编写的,所以解压完成后,需要编译和安装。
所以先要在系统中安装 c++的编译器 gcc-c++
切换管理员用户 su root
输入命令 yum install -y gcc-c++
第二步 安装redis
-
使用 root 用户登录,把redis安装文件(xxx.gz结尾的是linux系统的压缩包) 拷贝到linux系统中
-
解压这个文件
解压命令 tar –zxvf redis-3.0.0.tar.gz
- 进入解压后的目录,对里面的文件进行编译和安装
cd redis-3.0.0
这里采用编译和安装同时进行的方式。
make install PREFIX=/usr/local/redis
/usr/local/redis是redis的安装路径,目录名不是一定要叫redis可以自己定义
- 进入redis的安装路径,查看里面的文件
cd usr/local/redis/bin
其中 redis-server文件就是redis的启动文件
- ./redis-server 运行这个文件,如果看到一个图形界面,界面中显示redis的版本、软件位数、监听的端口(6379)、PID等信息
说明redis的安装和启动成功
注意: 前面的启动叫做前置启动。
前置启动的特点,当redis 启动后,linux操作界面将不能输入执行其他命令!!!
第三步 启动Redis
redis的启动,分为前置启动和后置启动两种:
- 前置启动
启动命令 ./redis-server
退出前置启动的方式 ctrl+c
- 后置启动
- 到redis的解压目录中拷贝 redis.conf 到 redis的安装目录中(和redis-server在同一个目录)
cp opt/redis-3.0.0/redis.conf usr/local/redis/bin
-
打开这个文件(vim命令),修改这个文件中 daemonize 的值为yes(默认为no)
-
在启动redis时, 使用 ./redis-server redis.conf(启动时,指定配置文件)
-
测试后置启动是否成功:
ps aux|grep redis
Redis文件介绍
-
daemonize yes 修改启动方式
-
port 6379 redis端口
-
database 16 redis默认开启16个库,也就是16个存储空间来存储数据
-
cluster-enabled yes 配置redis集群 ,这个配置默认是被注释的,也就是默认不开启集群
-
save 900 1 这个表示redis的持久化方案 (RDB)
6)dbfilename dump.rdb redis持久化时,存放数据的文件
7)appendonly no 是否开启redis的aof 持久化方案
8) appendfilename “appendonly.aof” aof持久化方案存放的文件
redis的持久化方案: RDB方案 和 AOF方案
什么是redis的持久化方案?
redis 除了可以作为缓存技术,也可以作为非关系型数据库。
作为缓存技术,数据默认是存放在内存中的,(这样可以提升存取速度)
但内存不是持久化设备,不能永久保存数据,一旦机器发生问题,
将会造成数据丢失。因此,redis为了解决这个文件,提供了数据的持久化方 案(memcahe 没有这个能力)
RDB方案:
redis默认开启的一种持久化方案。它会根据时间轴,以及key的数量改变来完成持久化动作。
save 900 1 该备份策略表示 在 900 秒内,如果有一个或多个key的值发生了变化,就触发redis的持久化机制。
AOF方案:
该持久化方案,redis默认情况下没有开启。需要手动开启。
redis.conf 中的 appendonly no 配置成 yes
aof的持久化策略为:redis会记录当前用户执行的,且改变数据的命令。
这种持久化的密度会更细。当然也会对redis的性能产生影响。
注意:即使采用aof的持久化策略,一旦内存崩溃,也会至少丢失一秒的数据
Spring Data Redis入门小Demo
- 引入Spring相关依赖、引入JUnit依赖
<properties>
<spring.version>4.2.4.RELEASE</spring.version>
<junit.version>4.12</junit.version>
</properties>
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
</dependencies>
- 引入Jedis和SpringDataRedis依赖
<!-- 缓存 -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.8.1</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.7.2.RELEASE</version>
</dependency>
- 在src/main/resources下创建properties文件夹,建立redis-config.properties
redis.host=127.0.0.1
redis.port=6379
redis.pass=
redis.database=0
redis.maxIdle=300
redis.maxWait=3000
redis.testOnBorrow=true
- 在src/main/resources下创建spring文件夹 ,创建applicationContext-redis.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath*:properties/*.properties" />
<!-- redis 相关配置 -->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${redis.maxIdle}" />
<property name="maxWaitMillis" value="${redis.maxWait}" />
<property name="testOnBorrow" value="${redis.testOnBorrow}" />
</bean>
<bean id="JedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
p:host-name="${redis.host}" p:port="${redis.port}" p:password="${redis.pass}" p:pool-config-ref="poolConfig"/>
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="JedisConnectionFactory" />
</bean>
</beans>
以上是相关的依赖,具体的后端代码分为几种类型操作
值类型操作
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:spring/applicationContext-redis.xml")
public class TestValue {
@Autowired
private RedisTemplate redisTemplate;
@Test
public void setValue(){
redisTemplate.boundValueOps("name").set("youjiuye");
}
@Test
public void getValue(){
String str = (String) redisTemplate.boundValueOps("name").get();
System.out.println(str);
}
@Test
public void deleteValue(){
redisTemplate.delete("name");;
}
}
Set类型操作
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:spring/applicationContext-redis.xml")
public class TestSet {
@Autowired
private RedisTemplate redisTemplate;
/**
* 存入值
*/
@Test
public void setValue(){
redisTemplate.boundSetOps("nameset").add("曹操");
redisTemplate.boundSetOps("nameset").add("刘备");
redisTemplate.boundSetOps("nameset").add("孙权");
}
/**
* 提取值
*/
@Test
public void getValue(){
Set members = redisTemplate.boundSetOps("nameset").members();
System.out.println(members);
}
/**
* 删除集合中的某一个值
*/
@Test
public void deleteValue(){
redisTemplate.boundSetOps("nameset").remove("孙权");
}
/**
* 删除整个集合
*/
@Test
public void deleteAllValue(){
redisTemplate.delete("nameset");
}
}
List类型操作
- 左压栈
/**
* 左压栈:后添加的对象排在前边
*/
@Test
public void testSetValue2(){
redisTemplate.boundListOps("namelist2").leftPush("刘备");
redisTemplate.boundListOps("namelist2").leftPush("关羽");
redisTemplate.boundListOps("namelist2").leftPush("张飞");
}
/**
* 显示左压栈集合
*/
@Test
public void testGetValue2(){
List list = redisTemplate.boundListOps("namelist2").range(0, 10);
System.out.println(list);
}
运行结果:
[张飞, 关羽, 刘备]
- 右压栈
/**
* 右压栈:后添加的对象排在后边
*/
@Test
public void testSetValue1(){
redisTemplate.boundListOps("namelist1").rightPush("刘备");
redisTemplate.boundListOps("namelist1").rightPush("关羽");
redisTemplate.boundListOps("namelist1").rightPush("张飞");
}
/**
* 显示右压栈集合
*/
@Test
public void testGetValue1(){
List list = redisTemplate.boundListOps("namelist1").range(0, 10);
System.out.println(list);
}
运行结果:
[刘备, 关羽, 张飞]
Hash类型操作 (常使用)
@Test
public void testSetValue(){
redisTemplate.boundHashOps("namehash").put("a", "唐僧");
redisTemplate.boundHashOps("namehash").put("b", "悟空");
redisTemplate.boundHashOps("namehash").put("c", "八戒");
redisTemplate.boundHashOps("namehash").put("d", "沙僧");
}
(2)提取所有的KEY
@Test
public void testGetKeys(){
Set s = redisTemplate.boundHashOps("namehash").keys();
System.out.println(s);
}
运行结果:
[a, b, c, d]
提取所有的值
@Test
public void testGetValues(){
List values = redisTemplate.boundHashOps("namehash").values();
System.out.println(values);
}
运行结果 [唐僧, 悟空, 八戒, 沙僧]
根据KEY提取值
@Test
public void testGetValueByKey(){
Object object = redisTemplate.boundHashOps("namehash").get("b");
System.out.println(object);
}
运行结果 悟空
根据KEY移除值
@Test
public void testRemoveValueByKey(){
redisTemplate.boundHashOps("namehash").delete("c");
}
现在的集合 [唐僧, 悟空, 沙僧]
(5)高效率存储大量数据
@Test
public void execValue(){
final List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
redisTemplate.executePipelined(new SessionCallback<Object>() {
public <K, V> Object execute(RedisOperations<K, V> operations) throws DataAccessException {
for(int i=0;i<list.size();i++){
redisTemplate.opsForHash().put("list",i,list.get(i));
}
return null;
}
});
}