Redis 简介
Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库。
Redis 与其他 key - value 缓存产品有以下三个特点:
- Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
- Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
- Redis支持数据的备份,即master-slave模式的数据备份。
Redis 优势
- 性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
- 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
- 原子 – Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
- 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。
Redis 配置
Redis 的配置文件位于 Redis 安装目录下,文件名为 redis.conf(Windows 名为 redis.windows.conf)。
你可以通过 CONFIG 命令查看或设置配置项
Redis 数据类型
Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。
Redis 发布订阅
Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。
Redis 客户端可以订阅任意数量的频道。
下图展示了频道 channel1 , 以及订阅这个频道的三个客户端 —— client2 、 client5 和 client1 之间的关系:
当有新消息通过 PUBLISH 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端:
Redis 事务
Redis 事务可以一次执行多个命令, 并且带有以下三个重要的保证:
- 批量操作在发送 EXEC 命令前被放入队列缓存。
- 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
- 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。
一个事务从开始到执行会经历以下三个阶段:
- 开始事务。
- 命令入队。
- 执行事务。
在 Java 中使用 Redis
第一步:添加 Jedis 依赖
想要在 Java 中使用 Redis 缓存,需要添加相关的Jar包依赖,打开Maven仓库的网站:https://mvnrepository.com/ ,搜索Jedis:
把它导入工程中去就可以啦,下面我们来对Redis的写入性能做一下测试:
@Test
public void redisTester() {
Jedis jedis = new Jedis("localhost", 6379, 100000);
int i = 0;
try {
long start = System.currentTimeMillis();// 开始毫秒数
while (true) {
long end = System.currentTimeMillis();
if (end - start >= 1000) {// 当大于等于1000毫秒(相当于1秒)时,结束操作
break;
}
i++;
jedis.set("test" + i, i + "");
}
} finally {// 关闭连接
jedis.close();
}
// 打印1秒内对Redis的操作次数
System.out.println("redis每秒操作:" + i + "次");
}
-----------测试结果-----------
redis每秒操作:10734次
据说 Redis 的性能能达到十万级别,我不敢相信我的台式机电脑只有十分之一不到的性能,虽然说这里不是流水线的操作,会造成一定的影响,但我还是不信邪,我查到了官方的性能测试方法:
首先在Redis根目录下召唤Cmd:具体方法是按住【Shift】点击右键
然后输入命令:【redis-benchmark -n 100000 -q】:来同时执行10万个请求测试性能
好吧,我同时在我的笔记本上测试了一下,结果更加惨淡...low啊low...
第二步:使用 Redis 连接池
跟数据库连接池相同,Java Redis也同样提供了类redis.clients.jedis.JedisPool
来管理我们的Reids连接池对象,并且我们可以使用redis.clients.jedis.JedisPoolConfig
来对连接池进行配置,代码如下:
JedisPoolConfig poolConfig = new JedisPoolConfig();
// 最大空闲数
poolConfig.setMaxIdle(50);
// 最大连接数
poolConfig.setMaxTotal(100);
// 最大等待毫秒数
poolConfig.setMaxWaitMillis(20000);
// 使用配置创建连接池
JedisPool pool = new JedisPool(poolConfig, "localhost");
// 从连接池中获取单个连接
Jedis jedis = pool.getResource();
// 如果需要密码
//jedis.auth("password");
Redis 只能支持六种数据类型(string/hash/list/set/zset/hyperloglog)的操作,但在 Java 中我们却通常以类对象为主,所以在需要 Redis 存储的五中数据类型与 Java 对象之间进行转换,如果自己编写一些工具类,比如一个角色对象的转换,还是比较容易的,但是涉及到许多对象的时候,这其中无论工作量还是工作难度都是很大的,所以总体来说,就操作对象而言,使用 Redis 还是挺难的,好在 Spring 对这些进行了封装和支持。
第三步:在 Spring 中使用 Redis
上面说到了 Redis 无法操作对象的问题,无法在那些基础类型和 Java 对象之间方便的转换,但是在 Spring 中,这些问题都可以通过使用RedisTemplate得到解决!
想要达到这样的效果,除了 Jedis 包以外还需要在 Spring 引入 spring-data-redis 包:https://mvnrepository.com/artifact/org.springframework.data/spring-data-redis
这里把2.0.7最新版本标红的意思是:别老想着使用最新的Jar包,特别是涉及到框架的一些东西,笔者用实际的操作体验告诉你们,引入该版本的包是会导致Jar包冲突的(也就是莫名其妙的错误),我乖乖换回了1.7.2的版本,代码就通了...我们来看看怎么做吧:
(1)第一步:使用Spring配置JedisPoolConfig对象
大部分的情况下,我们还是会用到连接池的,于是先用 Spring 配置一个 JedisPoolConfig 对象:
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<!--最大空闲数-->
<property name="maxIdle" value="50"/>
<!--最大连接数-->
<property name="maxTotal" value="100"/>
<!--最大等待时间-->
<property name="maxWaitMillis" value="20000"/>
</bean>
(2)第二步:为连接池配置工厂模型
好了,我们现在配置好了连接池的相关属性,那么具体使用哪种工厂实现呢?在Spring Data Redis中有四种可供我们选择的工厂模型,它们分别是:
- JredisConnectionFactory
- JedisConnectionFactory
- LettuceConnectionFactory
- SrpConnectionFactory
我们这里就简单配置成JedisConnectionFactory:
<bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<!--Redis服务地址-->
<property name="hostName" value="localhost"/>
<!--端口号-->
<property name="port" value="6379"/>
<!--如果有密码则需要配置密码-->
<!--<property name="password" value="password"/>-->
<!--连接池配置-->
<property name="poolConfig" ref="poolConfig"/>
</bean>
(3)第三步:配置RedisTemplate
普通的连接根本没有办法直接将对象直接存入 Redis 内存中,我们需要替代的方案:将对象序列化(可以简单的理解为继承Serializable接口)。我们可以把对象序列化之后存入Redis缓存中,然后在取出的时候又通过转换器,将序列化之后的对象反序列化回对象,这样就完成了我们的要求:
RedisTemplate可以帮助我们完成这份工作,它会找到对应的序列化器去转换Redis的键值:
<bean id="redisTemplate"
class="org.springframework.data.redis.core.RedisTemplate"
p:connection-factory-ref="connectionFactory"/>
笔者从《JavaEE互联网轻量级框架整合开发》中了解到,这一步需要配置单独的序列化器去支撑这一步的工作,但是自己在测试当中,发现只要我们的POJO类实现了Serializable接口,就不会出现问题...所以我直接省略掉了配置序列化器这一步...
(4)第四步:编写测试
首先编写好支持我们测试的POJO类:
/**
* @author: @我没有三颗心脏
* @create: 2018-05-30-下午 22:31
*/
public class Student implements Serializable{
private String name;
private int age;
/**
* 给该类一个服务类用于测试
*/
public void service() {
System.out.println("学生名字为:" + name);
System.out.println("学生年龄为:" + age);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
然后编写测试类:
@Test
public void test() {
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
RedisTemplate redisTemplate = context.getBean(RedisTemplate.class);
Student student = new Student();
student.setName("我没有三颗心脏");
student.setAge(21);
redisTemplate.opsForValue().set("student_1", student);
Student student1 = (Student) redisTemplate.opsForValue().get("student_1");
student1.service();
}
运行可以成功看到结果:
第四步:在 SpringBoot 中使用 Redis
(1)在SpringBoot中添加Redis依赖:
<!-- Radis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
(2)添加配置文件:
在SpringBoot中使用.properties
或者.yml
都可以,这里给出.properties
的例子,因为自己的.yml
文件看上去感觉乱糟糟的:
# REDIS (RedisProperties)
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=localhost
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=0
(3)测试访问:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest()
public class ApplicationTests {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Test
public void test() throws Exception {
// 保存字符串
stringRedisTemplate.opsForValue().set("aaa", "111");
Assert.assertEquals("111", stringRedisTemplate.opsForValue().get("aaa"));
}
}
通过上面这段极为简单的测试案例演示了如何通过自动配置的StringRedisTemplate对象进行Redis的读写操作,该对象从命名中就可注意到支持的是String类型。原本是RedisTemplate<K, V>接口,StringRedisTemplate就相当于RedisTemplate<String, String>的实现。
运行测试,如果一切成功则不会报错,如果我们没有拿到或者拿到的数不是我们想要的 “111” ,那么则会报错,这是使用Assert的好处(下面是我改成112之后运行报错的结果):
(4)存储对象:
这一步跟上面使用Spring一样,只需要将POJO类实现Serializable接口就可以了,我这里就贴一下测试代码:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest()
public class ApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
public void test() throws Exception {
User user = new User();
user.setName("我没有三颗心脏");
user.setAge(21);
redisTemplate.opsForValue().set("user_1", user);
User user1 = (User) redisTemplate.opsForValue().get("user_1");
System.out.println(user1.getName());
}
}
仍然没有任何问题:
Redis与Memcached的区别与比较
1 、Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,zset,hash等数据结构的存储。memcache支持简单的数据类型,String。
2 、Redis支持数据的备份,即master-slave模式的数据备份。
3 、Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用,而Memecache把数据全部存在内存之中
4、 redis的速度比memcached快很多
5、Memcached是多线程,非阻塞IO复用的网络模型;Redis使用单线程的IO复用模型。
使用redis有哪些好处?
(1) 速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)
(2)支持丰富数据类型,支持string,list,set,sorted set,hash
(3) 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
(4) 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除
Redis常见数据结构使用场景
1. String
常用命令: set,get,decr,incr,mget 等。
String数据结构是简单的key-value类型,value其实不仅可以是String,也可以是数字。
常规key-value缓存应用;
常规计数:微博数,粉丝数等。
2.Hash
常用命令: hget,hset,hgetall 等。
Hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。 比如我们可以Hash数据结构来存储用户信息,商品信息等等。
举个例子: 最近做的个人博客网站项目的首页就使用了redis的hash数据结构进行缓存,因为一个网站的首页访问量是最大的,所以通常网站的首页可以通过redis缓存来提高性能和并发量。我用jedis客户端来连接和操作我搭建的redis集群或者单机redis,利用jedis可以很容易的对redis进行相关操作,总的来说从搭一个简单的集群到实现redis作为缓存的整个步骤不难。感兴趣的可以看我昨天写的这篇文章:
《一文轻松搞懂redis集群原理及搭建与使用》: https://juejin.im/post/5ad54d76f265da23970759d3
3.List
常用命令: lpush,rpush,lpop,rpop,lrange等
list就是链表,Redis list的应用场景非常多,也是Redis最重要的数据结构之一,比如微博的关注列表,粉丝列表,最新消息排行等功能都可以用Redis的list结构来实现。我自己的项目是最新文章、最新评论。
Redis list的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销。
4.Set
常用命令:
sadd,spop,smembers,sunion 等
set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的。
当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。
在微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。Redis可以非常方便的实现如共同关注、共同喜好、二度好友等功能。
5.Sorted Set
常用命令: zadd,zrange,zrem,zcard等
和set相比,sorted set增加了一个权重参数score,使得集合中的元素能够按score进行有序排列。
举例: 在直播系统中,实时排行信息包含直播间在线用户列表,各种礼物排行榜,弹幕消息(可以理解为按消息维度的消息排行榜)等信息,适合使用Redis中的SortedSet结构进行存储。
=============================================================================
redis的应用场景有哪些
1,会话缓存(最常用)
2,消息队列,
比如支付3,活动排行榜或计数
4,发布,订阅消息(消息通知)
5,商品列表,评论列表等
Redis 事务相关的命令有哪几个?
MULTI、EXEC、DISCARD、WATCH