简介
什么是Redis?
- redis是c语言开发的高性能的k-v形式的NoSQL型数据库,数据存储在内存中,诞生于意大利
- 优点:存取速度快,官方称 读取速度会达到30万次每秒,写速度在10万次每秒,具体限制于硬件.
- 缺点:对持久化支持不够良好,所以redis一般配合传统的关系型数据库使用。
Redis的使用场景
- 缓存(数据查询)。(最多使用)
- 分布式集群架构中的session分离。
- 聊天室的在线好友列表。
- 任务队列。(秒杀、抢购、12306等等)
- 网站访问统计。
什么是NoSQL?
-
NoSQL(Not-Only SQL),泛指非关系型的数据库(关系型数据库:以二维表形式存储数据)
-
传统的关系数据库在应付超大规模和高并发的网站已经显得力不从心,暴露了很多难以克服的问题,
-
NoSQL数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,尤其是大数据应用难题。例如:
-
High performance -对数据库高并发读写的需求
对于每秒要达到上万次读写请求。关系数据库应付上万次SQL查询还勉强顶得住,但是应付上万次SQL写数据请求,硬盘IO就已经无法承受了。
-
Huge Storage -对海量数据的高效率存储和访问的需求
腾讯,动辄数以亿计的帐号,每天用户产生海量的用户动态
类似Facebook,twitter,Friendfeed这样的SNS网站,一个月就达到了2.5亿条用户动态
-
-
主流的NOSQL产品:Redis、mongoDB
NoSQL的四大分类
- 键值(Key-Value)存储数据库
- 相关产品: Tokyo Cabinet/Tyrant、Redis、Voldemort、Berkeley DB
- 典型应用:内容缓存,主要用于处理大量数据的高访问负载。
- 数据模型:一系列键值对
- 优势:快速查询
- 劣势:存储的数据缺少结构化
- 列存储数据库
- 相关产品:Cassandra, HBase, Riak
- 典型应用:分布式的文件系统
- 数据模型:以列簇式存储,将同一列数据存在一起
- 优势:查找速度快,可扩展性强,更容易进行分布式扩展
- 劣势:功能相对局限
- 文档型数据库
- 相关产品:CouchDB、MongoDB
- 典型应用:Web应用(与Key-Value类似,Value是结构化的)
- 数据模型:一系列键值对
- 优势:数据结构要求不严格
- 劣势:查询性能不高,而且缺乏统一的查询语法
- 图形(Graph)数据库
- 相关数据库:Neo4J、InfoGrid、Infinite Graph
- 典型应用:社交网络
- 数据模型:图结构
- 优势:利用图结构相关算法。
- 劣势:需要对整个图做计算才能得出结果,不容易做分布式的集群方案。
Redis安装和启动
安装
1)安装c语言环境
yum install gcc-c++
2)上传并解压
cd /usr/upload
tar -zxvf redis-3.0.7.tar.gz
3)编译并安装
cd /usr/local/redis-3.0.7
make
make install PREFIX=/usr/local/redis
启动和关闭
-
前端启动(不推荐)
ssh命令窗口关闭则redis-server程序结束,所以不推荐
cd /usr/local/redis/bin ./redis-server ctrl+c #关闭
-
后端启动
#进入源码目录,里面有一份配置文件 redis.conf,然后将其拷贝到安装路径下 cp /usr/upload/redis-3.0.0/redis.conf /usr/local/redis/bin #修改/usr/java/redis/bin下的redis.conf,将daemonize由no改为**yes** vim /usr/local/redis/bin/redis.conf --->daemonize yes #启动 ./redis-server redis.conf #关闭 ./redis-cli -h 127.0.0.1 -p 6379 shutdown ./redis-cli shutdown #测试 ./redis-cli -h 127.0.0.1 -p 6379 ./redis-cli
基本命令
set和get:使用set和get可以向redis设置数据、获取数据
keys *:查看所有key
expire和ttl:设置和查看key的失效时间
incr和decr:自增和自减
exists:判断key值是否存在
多数据库实例
1、redis实例中提供了下标是0-15的16个数据库,不能修改下标,可通过select切换
2、清空数据库的命令:
flushdb:清空当前数据库的数据
flushall:清空所有数据库的数据
持久化(RDB与AOF)
-
RDB:默认(存数据)
save 900 1 #900秒内容如果超过1个key被修改,则发起快照保存 save 300 10 #300秒内容如超过10个key被修改,则发起快照保存 save 60 10000 #表示60秒内如果超过10000个key被修改,则发起快照保存
-
优点:不影响性能
缺点: 不能保证数据的完整性 -
实验:
1.删除dump.rdb
2.set str abc
3…/redis-cli shutdown—>dump.rdb -
AOF:(存命令)
#appendfsync always #每次有数据修改发生时都会写入AOF文件。 appendfsync everysec #每秒钟同步一次,该策略为AOF的缺省策略。 #appendfsync no #从不同步。高效但是数据不会被持久化。
-
缺点:影响性能
优点:保证数据的完整性- 实验:
1.设置appendonly为yes
2.set str1 abc
3.等待1秒观察appendonly.aop
- 实验:
主从复制
-
过程
a、从往主发送sync命令
b、主往从发送rdb文件
c、主往从发送写命令 -
配置步骤
主:无需配置
从:slaveof 主ip 主port -
实验:
1.往主写数据,观察从是否同步
2.关闭主,往从写数据(主死类从只能读)
五种数据类型
Redis 可以存储键与5种不同数据结构类型之间的映射,这5种数据结构类型分别为String(字符串)、Hash(散列)、List(列表)、Set(集合)和 Zset(有序集合)。
注:zset 赋值时k后面是下标,list 删除时k后面是相同的v的个数
string:key-value
#赋值:set key value
set test 123
#取值:get key
get test
#删除:del key
del test
#自增自减:incr key decr key
incr num
hash:map
#Hash:key-field-value
#赋值:HSET key field value
hset user name zhangsan
#取值:HGET key field
hget user name
#删除:HDEL key field
hdel user name
list:list
List:有顺序可重复,内部是使用双向链表实现的
#赋值lpush
192.168.25.153:6379> lpush list a b c d
(integer) 4
#赋值rpush
192.168.25.153:6379> rpush list 1 2 3 4
(integer) 8
#取值lrange
192.168.25.153:6379> lrange list 0 -1
1) "d"
2) "c"
3) "b"
4) "a"
5) "1"
6) "2"
7) "3"
8) "4"
#删除lrem
192.168.25.153:6379> lrem list 0 d
(integer) 1
192.168.25.153:6379> lrange list1 0 -1
1) "c"
2) "b"
3) "a"
4) "1"
5) "2"
6) "3"
7) "4"
set:set
Set:元素无顺序,不能重复
#赋值sadd
192.168.25.153:6379> sadd set1 a b c c c d
(integer) 4
#取值smembers
192.168.25.153:6379> smembers set1
1) "b"
2) "c"
3) "d"
4) "a"
#删除srem
192.168.25.153:6379> srem set1 a
(integer) 1
192.168.25.153:6379> smembers set1
1) "b"
2) "c"
3) "d"
zset:sort set
SortedSet(zset):有顺序,不能重复
#赋值zadd k 1 v...
192.168.25.153:6379> zadd zset1 2 a 5 b 1 c 6 d
(integer) 4
#取值zrange k 0 -1 withscores
192.168.25.153:6379> zrange zset1 0 -1
1) "c"
2) "a"
3) "b"
4) "d"
#删除zrem k v
192.168.25.153:6379> zrem zset1 a
(integer) 1
192.168.25.153:6379> zrange zset1 0 -1
1) "c"
2) "b"
3) "d"
192.168.25.153:6379> zrevrange zset1 0 -1
1) "d"
2) "b"
3) "c"
192.168.25.153:6379> zrange zset1 0 -1 withscores
1) "c"
2) "1"
3) "b"
4) "5"
5) "d"
6) "6"
192.168.25.153:6379> zrevrange zset1 0 -1 withscores
1) "d"
2) "6"
3) "b"
4) "5"
5) "c"
6) "1"
jedis
jedis介绍
- Jedis是Redis官方推荐的面向Java的操作Redis的客户端
- Redis不仅是使用命令来操作,现在基本上主流的语言都有客户端支持,比如java、C、C#、C++、php、Node.js、Go等。
- 在官方网站里的一些Java的客户端,有Jedis、Redisson、Jredis、JDBC-Redis、等其中官方推荐使用Jedis和Redisson。在企业中用的最多的就是Jedis,下面我们就重点学习下Jedis。
- Jedis托管在github上,地址:https://github.com/xetorthio/jedis
依赖
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
单实例连接
// 单实例连接redis
@Test
public void testJedisSingle() {
Jedis jedis = new Jedis("192.168.101.3", 6379);
jedis.set("name", "zangsan");
String name = jedis.get("name");
System.out.println(name);
jedis.close();
}
使用连接池连接
-
通过单实例连接redis不能对redis连接进行共享,可以使用连接池对redis连接进行共享,提高资源利用率
-
配置类application.yml
spring: redis: host: 192.168.182.130 port: 6379 jedis: pool: max-idle: 6 #最大空闲数 max-active: 10 #最大连接数 min-idle: 2 #最小空闲数
-
JedisConfig.java 配置类配置连接池
@Configuration public class JedisConfig { @Value("${spring.redis.host}")//读取application.yml中的配置信息,并注入到host private String host; @Value("${spring.redis.port}") private int port; @Value("${spring.redis.jedis.pool.max-active}") private int maxActive; @Value("${spring.redis.jedis.pool.max-idle}") private int maxIdle; @Value("${spring.redis.jedis.pool.min-idle}") private int minIdle; //连接池的配置信息 @Bean public JedisPoolConfig jedisPoolConfig(){ JedisPoolConfig poolConfig = new JedisPoolConfig(); poolConfig.setMaxIdle(maxIdle); poolConfig.setMinIdle(minIdle); poolConfig.setMaxTotal(maxActive); return poolConfig; } //连接池 @Bean public JedisPool jedisPool(JedisPoolConfig poolConfig){ return new JedisPool(poolConfig,host,port); } }
-
测试
@RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = HealthJobApp.class) public class JedisTest { @Autowired private JedisPool jedisPool; @Test public void testSet(){ Jedis jedis = jedisPool.getResource(); jedis.set("str", "test..................."); String str = jedis.get("str"); System.out.println(str); } @Test public void testGet(){ Jedis jedis = jedisPool.getResource(); String str = jedis.get("str"); System.out.println(str); } }
Redis集群搭建
集群架构
(1)所有的redis节点彼此互联(PING-PONG机制),节点的fail是通过集群中超过半数的节点检测失效时才生效.
(2)存取数据时连接任一节点都可以,但集群中有一个节点fail整个集群都会fail
Redis 集群中内置了 16384 个哈希槽,当需要在Redis 集群中放置一个 key-value 时,redis 先对 key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点
1、redis集群是多少台?
投票容错超过半数:3台 高可用:3台
2、为什么集群中有一个节点挂了,则整个集群都挂类?
redis集群中有16384个槽分给了3个节点,存取数据时:crc16(key)%16384=[0~16383]
集群搭建
-
Redis集群中至少应该有三个节点。要保证集群的高可用,需要每个节点有一个备份机。
-
Redis集群至少需要6台服务器。
-
搭建伪分布式。可以使用一台虚拟机运行6个redis实例。需要修改redis的端口号7001-7006
搭建步骤
-
使用ruby脚本搭建集群,安装ruby环境
cd /usr/upload yum install ruby yum install rubygems gem install redis-3.0.0.gem cd /usr/upload/redis-3.0.0/src ll *.rb
-
拷贝6个节点,并启动
#1、删除dump.rdb和appendonly.aof cd /usr/local/redis/bin rm -f dump.rdb rm -f appendonly.aof #2、拷贝6个节点 cd /usr/local mkdir redis-cluster cp -r redis redis-cluster/redis-7001 ...... cp -r redis redis-cluster/redis-7007 #3、修改配置文件 vim /usr/local/redis-cluster/redis-7001/bin/redis.conf: port 7001 cluster-enable yes ... ...
-
创建启动和关闭脚本
创建启动集群的脚本:start-all.sh 放在/usr/java/redis-cluster目录下cd /usr/local/redis-cluster/redis-7001/bin ./redis-server redis.conf cd /usr/local/redis-cluster/redis-7002/bin ./redis-server redis.conf cd /usr/local/redis-cluster/redis-7003/bin ./redis-server redis.conf cd /usr/local/redis-cluster/redis-7004/bin ./redis-server redis.conf cd /usr/local/redis-cluster/redis-7005/bin ./redis-server redis.conf cd /usr/local/redis-cluster/redis-7006/bin ./redis-server redis.conf [root@localhost redis-cluster]# chmod 777 start-all.sh
创建关闭集群的脚本:shutdown-all.sh,放在/usr/local/redis-cluster目录下。
cd redis-7001 ./redis7001/bin/redis-cli -p 7001 shutdown ./redis7001/bin/redis-cli -p 7002 shutdown ./redis7001/bin/redis-cli -p 7003 shutdown ./redis7001/bin/redis-cli -p 7004 shutdown ./redis7001/bin/redis-cli -p 7005 shutdown ./redis7001/bin/redis-cli -p 7006 shutdown [root@localhost redis-cluster]# chmod 777shutdown-all.sh
-
使用ruby脚本创建redis集群
cd /usr/upload/redis-3.0.0/src ./redis-trib.rb create --replicas 1 192.168.182.133:7001 192.168.182.133:7002 192.168.182.133:7003 192.168.182.133:7004 192.168.182.133:7005 192.168.182.133:7006
-
测试
./redis-cli -c -p 7001 #启动时使用-c参数来启动集群模式 cluster info #打印集群的信息 cluster nodes #列出集群当前已知的所有节点(node),以及这些节点的相关信息
SpringData Redis
Spring Data 介绍
Spring Data是一个用于简化数据库访问的开源框架。其主要目标是使得对数据的访问变得方便快捷,包含多个子项目:
-
Spring Data JDBC- 对JDBC的Spring Data存储库支持。
-
Spring Data JPA - 对JPA的Spring Data存储库支持。
-
Spring Data MongoDB - 对MongoDB的基于Spring对象文档的存储库支持。
-
Spring Data Redis - 从Spring应用程序轻松配置和访问Redis。
… …
Spring Data Redis 介绍
Spring Data Redis 是属于 Spring Data 下的一个模块,作用就是简化对于 redis 的操做。
spring-data-redis针对jedis提供了如下功能:
-
提供了一个高度封装的“RedisTemplate”类,里面封装了对于Redis的五种数据结构的各种操作,包括:
- redisTemplate.opsForValue():操作字符串 - redisTemplate.opsForHash():操作hash - redisTemplate.opsForList():操作list - redisTemplate.opsForSet():操作set - redisTemplate.opsForZSet():操作zset
-
SpringBoot2.x后RedisTemplate采用是lettuce(基于netty采用异步非阻塞式lO)进行通信,大并发下lettuce(netty)效率更高。
-
RedisTemplate模板使用序列化器操作redis数据,预定义的序列化方案有:
序列化器 说明 JdkSerializationRedisSerializer POJO对象的存取场景,使用JDK本身序列化机制,将pojo类通过ObjectInputstream/ObjectOutputstream进行序列化操作,最终redis-server中将存储字节序列。是目前最常用的序列化策略。 stringRedisSerializer Key或者value为字符串的场景,根据指定的charset对数据的字节序列编码成string,是"new String(bytes,charset)"和“string.getBytes(charset)"的直接封装。是最轻量级和高效的策略。 GenericJackson2JsonRedisSerializer jackson-json工具提供了javabean与json之间的转换能力,可以将pojo实例序列化成json格式存储在redis中,也可以将json格式的数据转换成pojo实例。
使用案例
-
导入依赖
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> </dependencies>
-
Pojo
public class User implements Serializable { private Integer id; private String name; private Integer age; //get...set...略 @Override public String toString() { return "User [id=" + id + ", name=" + name + ", age=" + age + "]"; } }
-
application.yml
spring: redis: cluster: nodes: - 192.168.182.133:7001 - 192.168.182.133:7002 - 192.168.182.133:7003 - 192.168.182.133:7004 - 192.168.182.133:7005 - 192.168.182.133:7006 jedis: pool: max-active: 20 #最大连接数 min-idle: 10 #最小空闲数 max-idle: 5 #最大空闲数
-
配置类
@Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(factory); //设置通用的序列化器 redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); return redisTemplate; } }
-
测试类
@RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = RedisApp.class) public class RedisTest { @Autowired private RedisTemplate<String, Object> redisTemplate; /**********StringRedisSerializer:使用场景string**********/ @Test public void testSet(){ redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new StringRedisSerializer()); redisTemplate.opsForValue().set("str","张三丰"); } @Test public void testGet(){ redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new StringRedisSerializer()); String str = (String) redisTemplate.opsForValue().get("str1"); System.out.println(str); } /**********JdkSerializationRedisSerializer 使用场景pojo 大小:243 乱码 **********/ @Test public void testSetPojo() { User user = new User(); user.setId(1); user.setAge(130); user.setName("张三丰"); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer()); redisTemplate.opsForValue().set("jdk-user",user); } @Test public void testGetPojo(){ redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer()); User user = (User) redisTemplate.opsForValue().get("jdk-user"); System.out.println(user); } /**********GenericJackson2JsonRedisSerializer 使用场景通用 大小:72 不乱码 **********/ //处理Pojo @Test public void testSetPojo1() { User user = new User(); user.setId(1); user.setAge(130); user.setName("张三丰"); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); redisTemplate.opsForValue().set("gen-user",user); } @Test public void testGetPojo1(){ redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); User user = (User) redisTemplate.opsForValue().get("gen-user"); System.out.println(user); } //处理String @Test public void testSet1(){ redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); redisTemplate.opsForValue().set("str1","张三丰"); } @Test public void testGet1(){ redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); String str = (String) redisTemplate.opsForValue().get("str1"); System.out.println(str); } /**********直接使用通用的序列化器**********/ @Test public void testSetPojo2(){ User user = new User(); user.setId(1); user.setAge(130); user.setName("张三丰"); redisTemplate.opsForValue().set("user2",user); } @Test public void testGetPojo2(){ User user = (User) redisTemplate.opsForValue().get("user2"); System.out.println(user); } }