一、什么是 NoSQL?
??NoSQL(Not Only SQL),“不仅仅是 SQL”, 泛指非关系型的数据库。NoSQL 不依赖业务逻辑方式存储,而以简单的 key-value 模式存储 。因此大大的增加了数据库的扩展能力。
NoSQL 的代表
NoSQL 的存储格式可以是列式存储,key-value 存储,文档存储,各自的代表产品如下: 1)键值存储: Redis 2)列式存储: HBase 3)文档型存储: MongoDB
二、Redis 简介
2.1 什么是Redis?应用场景?
??Redis 是一个高性能的(key/value)分布式内存数据库 ,基于内存运行 并支持持久化的NoSQL数据库,是当前最热门的NoSql数据库之一, 也被人们称为数据结构服务器。Redis 是一个开源(BSD 许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(string), 散列(hash), 列表(list), 集合(set), 有序集合(sorted set)。 ??Redis 性能极高 – Redis 能读的速度是 110000 次/s,写的速度是 81000 次/s 。
2.2 Redis 优点/应用场景
优点: ??Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。 Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储 Redis支持数据的备份,即 master-slave模式的数据备份 。
应用场景: ??内存存储和持久化:redis支持异步将内存中的数据写到硬盘上,同时不影响继续服务 取最新N个数据的操作,如: 可以将最新的10条评论的ID放在Redis的List集合里面 模拟类似于HttpSession这种需要设定过期时间的功能 发布、 订阅消息系统 定时器、计数器 。查看详细应用场景
2.3 CentOS7安装 Redis
2.3 安装
1) 上传安装包到 Linux; 2) 解压到自己指定的目录;(我这里解压到 /root/javaDevEnv/modules) 3) cd到 /root/javaDevEnv/modules/redis目录,输入make 命令 执行编译命令,接下来控制台会输出各种编译过程中输出的内容。(提示:安装 redis 需要先将官网下载的源码进行编译,编译依赖 gcc 环境,如果没有 gcc 环境,要先安装 gcc-c++(c 语言的环境),命令: yum -y install gcc-c++ 。如果已安装,可通过 gcc --version 查看版本。) 4) 安装;(在 redis 所在的目录执行命令)(执行完命令后会在redis的目录中生成一个bin目录,该目录中存放redis相关的重要的可执行文件)
make PREFIX=/root/javaDevEnv/modules/redis install
【提示】
- 这里多了一个关键字
PREFIX= 这个关键字的作用是编译的时候用于指定程序存放的路径 。比如我们现在就是指定了redis必须存放在/root/javaDevEnv/modules/redis目录。假设不添加该关键字Linux会将可执行文件存放在/usr/local/bin目录,库文件会存放在/usr/local/lib目录。配置文件会存放在/usr/local/etc目录。其他的资源文件会存放在usr/local/share目录。这里指定目录主要是为了后期维护方便。
2.3.1 前端启动(了解)
??在 bin 目录下执行 redis-server 即可启动服务器,会带 1 个界面。缺点是启动后,不能再进行其它操作。如要做其它操作,必须使用 Ctrl+C,此时 redis-server 结束,客户端就不能连接了。
2.3.2 后端启动(重点)
1) 修改redis.conf 配置文件 2) 执行 bin 下的 redis-server 时,带上 redis.conf 。
【提示】
- 开启服务器:在 redis 目录下可使用
./bin/redis-server redis.conf 来后台开启Redis服务器。 - 关闭服务器:在 redis 目录下可使用
./bin/redis-cli shutdown 来关闭Redis服务器。 - 查看Redis服务器进程:
ps -aux | grep redis 。 - 端口监听查看:
netstat -lanp | grep 6379 。
2.3.3 客户端连接(掌握)
??bin 目录下有 redis 的客户端,即 redis-cli(Redis Command Line Interface),它是 Redis 自带的基于命令行的 Redis 客户端。
执行 bin/redis-cli 即可连接(登录)redis 服务器。(quit命令或exit命令可退出终端) ??如果希望远程连接其它主机或者其它端口的 Redis 服务器,可以加上相应的参数,使用-h 设置远程主机的 ip,使用-p 设置远程主机的端口号。 ??redis-cli 连上 redis 服务后,可以在命令行向 Redis 服务器发送命令。比如 PING 命令用来测试客户端与 Redis 服务器的连接是否正常,如果连接正常会收到回复 PONG。
2.3.4 远程访问 Redis 服务器
1) 开放端口 6379;
firewall-cmd --zone=public --add-port=6379/tcp --permanent
firewall-cmd --reload
【提示】
- 查看开启的端口列表:
firewall-cmd --list-ports ;
2) 修改配置文件 redis.conf ,一、注释掉 bind 127.0.0.1(任何 ip 都能远程连接 redis)。二、找到 protected-mode 这行, 将值 yes 改为 no。
【提示】
- 可以输入“/protected-mode”在文件里搜索,改完后保存并退出。
3) 使用图形化工具连接redis服务器。 ??首先要启动 redis 服务器。
[root@localhost redis]# ./bin/redis-server redis.conf
【提示】
- 关闭redis服务器:
./bin/redis-cli shutdown 。
2.3.5 设置redis密码
【提示】后期补充。
三、Redis 的数据结构
Redis 中 key 是 String 类型,value 根据存储数据不同,有如下类型:
value 的数据类型 | 说明 |
---|
String | 字符串,最简单的 k-v 存储。 | List | 简单的 list,顺序列表,支持首位或者末尾插入数据。 | Set | 无序 list,查找速度快,适合交集、并集、差集处理。 | Sorted Set | 有序的 Set。 | Hash | hash 格 式 , value 为 field-value , 适合ID-Detail 这样的场景。 |
点击该博主查看该图片
命令 | 说明 |
---|
keys * | 查找所有的key。(包括string,hash,list,set,zset) | del key[key ...] | 删除指定key。 | `` | | `` | 更多命令可查看:www.redis.cn |
3.1 String
??String 是 redis 最基本的类型,一个 key 对应一个 value。String 类型是二进制安全的。意思是 redis 的 String 可以包含任何数据。比如jpg图片或者序列化的对象。 ??String 类型是 Redis 最基本的数据类型,String 类型的值最大能存储 512MB。
命令 | 说明 |
---|
set key value | 设定 key 持有指定的字符串 value,如果该 key 存在则进行覆盖操作。总是返回”OK”。 如果value中含有空格的话可以使用单引号'' 将整个value括起来。 | get key | 获取 key 的 value。 如果与该 key 关联的 value 不是 String 类型,redis将返回错误信息。 如果该 key 不存在,返回 null。 | incr key | 将指定的 key 的 value 原子性的递增 1。 如果该 key 不存在,其初始值为 0,在 incr 之后其值为 1。 如果 value 的值不能转成整型,如 hello,该操作将执行失败并返回相应的错误信息。 | decr key | 将指定的 key 的 value 原子性的递减 1。 如果该 key 不存在,其初始值为 0,在 decr 之后其值为-1。 如果 value 的值不能转成整型,如 hello,该操作将执行失败并返回相应的错误信息。 | append key value | 如果该 key 存在,则在原有的 value 后追加该值; 如果该key不存在,则重新创建一个 key/value。 |
3.2 List
??Redis 的列表相当于 Java 语言里面的 LinkedList,是链表而不是数组 。这意味着list 的插入和删除操作非常快,时间复杂度为 O(1),但是查找数据很慢,时间复杂度为 O(n) 。 ??Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。在插入时,如果该键并不存在,Redis 将为该键创建一个新的链表。与此相反,如果链表中所有的元素均被移除 ,那么该键也将会被从数据库中删除。 List 中可以包含的最大元素数量是 4294967295。
命令 | 说明 |
---|
lpush key value1 value2 ... | 在指定的 key 所关联的 list 的头部插入所有的values。 如果该 key 不存在,该命令在插入的之前创建一个与该 key 关联的空链表,之后再向该链表的头部插入数据。 插入成功,返回元素的个数。 | rpush key value1、value2 ... | 在该 list 的尾部添加元素。 如果该key不存在则创建该key的链表,再插入数据。 | lrange key start end | 获取链表中从 start 到 end 的元素的值。(下标从0开始) start、end 可为负数,若为-1 则表示链表尾部的元素,-2 则表示倒数第二个,依次类推… | lpop key | 返回并弹出(即删除)指定的 key 关联的链表中的第一个元素,即头部元素。 | rpop key | 从尾部弹出(即删除)元素。 | llen key | 返回指定的key关联的链表中的元素的数量。 | lset key index value | 设置链表中的 index 的脚标的元素值,0 代表链表的头元素,-1 代表链表的尾元素。 |
3.3 Set
??Redis 的 Set 是 string 类型的无序集合(集合中不允许有重复的元素)。集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。 ??和 List 类型相比,Set 类型在功能上还存在着一个非常重要的特性,即在服务器端完成多个 Sets 之间的聚合计算操作,如 unions、intersections 和 differences。由于这些操作均在服务端完成,因此效率极高,而且也节省了大量的网络 IO 开销。
命令 | 说明 |
---|
sadd key value、value2 ... | 向 set 中添加数据,如果该 key 的值已有则不会重复添加 。 | smembers key | 获取 set 中所有的成员。 | scard key | 获取 set 中成员的数量。 | sismember key value | 判断参数中指定的成员(value)是否在该 set 中,1 表示存在,0 表示不存在或者该 key 本身就不存在。 | srem key member1 member2 ... | 删除 set 中指定的成员。 | srandmember key | 随机返回 set 中的一个成员。 | sdiff key1 key2 | 返回 key1 与 key2 中相差的成员,而且与 key 的顺序有关。即返回差集。 即返回key1绑定的集合中key2绑定集合中没有的元素。 | sdiffstore destination key1 key2 | 将 在key1中而不在key2中的元素存储在destination 上。 | sinter key1 key2 ... | 返回交集。 | sinterstore destination key1 key2 ... | 将返回的交集存储在 destination 上。(如果destination中有值的话将会被覆盖!) | sunion key1 key2 ... | 返回并集。 | sunionstore destination key1 key2 ... | 将返回的并集存储在 destination 上。 |
3.4 ZSet
??Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。zset的成员是唯一的,但分数(score)却可以重复。
命令 | 说明 |
---|
zadd key score member score2 member2 ... | 将所有成员以及该成员的分数存放到 sorted-set 中 。 | zcard key | 获取集合中的成员数量。 | zcount key min max | 获取分数在[min,max]之间的成员数量。 | zincrby key increment member | 设置指定成员的增加的分数。返回增加后的分数。 | zrange key start end[withscores] | 获取集合中脚标为 start-end 的成员,[withscores]参数表明返回的成员包含其分数。end=-1:表示最后一个成员。 例如:获取key=ccbx,中从第一个成员到第2个成员的值并且同时输出分数: ???zrange ccbx 0 2 withscores | zrangebyscore key min max [withscores] [limit offset count] | 返回分数在[min,max]的成员并按照分数从从低到高排序。 [withscores] :显示分数; [limit offset count]:offset,表示从下表为offset的元素开始并返回count个成员。 | zrank key member | 返回成员在集合中的位置。(从0开始) | zrem key member ... | 移出集合中指定的成员,可以指定多个成员。 | zscore key member | 返回指定成员的分数。 |
3.5 Hash
??Redis hash 是一个键值(key=>value)对集合。Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。每一个Hash 可以存储 4294967295 个键值对。
命令 | 说明 |
---|
hset key field value | 为指定的key设定 field/value 对(键值对)。 多个属性也可以多次添加。(不太推荐) | hgetall key | 获取key中所有的 field-vlaue 。 | hget key field | 返回指定的 key 中的 field 的值。 | hmset key field1 value1 field2 value2 ... | 设置 key 中的多个 filed/value 。 | hmget key fileld1 field2 ... | 获取 key 中的多个 filed 的值。 | hexists key field | 判断指定的 key 对应 hash 中的 filed 是否存在。(返回1:存在。返回0:不存在) | hdel key field[field ...] | 删除指定 key 中一个或多个 field。(返回删除的数量) | hlen key | 获取 key 所包含的 field 的数量。 | hincrby key field increment | 设置 key 中 filed 的值增加 increment(注意数据类型)。如:age增加 20。 |
四、Java 操作Redis
【备注】此部分后期补充。
五、Spring Boot 使用Redis
5.1 引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.7.0</version>
</dependency>
5.2 application.yml配置redis
spring:
redis:
host: 192.168.238.66
port: 6379
database: 1
timeout: 30000
lettuce:
pool:
max-active: 20
max-wait: -1
max-idle: 6
min-idle: 0
5.3 自定义 Redis 配置类
package com.ccbx.config;
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
template.setKeySerializer(stringRedisSerializer);
template.setValueSerializer(jackson2JsonRedisSerializer);
template.setHashKeySerializer(stringRedisSerializer);
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(600))
.serializeKeysWith(RedisSerializationContext.SerializationPair
.fromSerializer(redisSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.
fromSerializer(jackson2JsonRedisSerializer))
.disableCachingNullValues();
RedisCacheManager cacheManager = RedisCacheManager.builder(factory).cacheDefaults(config).build();
return cacheManager;
}
}
5.4 调用 RedisTemplate 的 API
首先注入上面的自定义的Bean。
@Autowired
private RedisTemplate redisTemplate;
然后调用 redisTemplate 对象的相关命令,如下:
命令 | 说明 |
---|
redisTemplate.boundValueOps(key) | 操作 string | redisTemplate.boundListOps(key) | 操作 list | redisTemplate.boundSetOps(key) | 操作 set | redisTemplate.boundZSetOps(key) | 操作 ZSet | redisTemplate.boundHashOps(key) | 操作 Hash |
【备注】如果只是操作字符串,还可以用 StringRedisTemplate。 ?StringRedisTemplate 和 RedisTemplate 区别:
- StringRedisTemplate 继承 RedisTemplate。
- StringRedisTemplate 默认采用的是 String 的序列化策略(StringRedisSerializer),保存的key和 value 都是采用此策略序列化保存的。
- RedisTemplate 默认采用的是 JDK 的序列化策略(JdkSerializationRedisSerializer),保存的key 和 value 都是采用此策略序列化保存的。
六、Spring session(session共享)
??使用Spring Session+Redis实现session共享。 ??Session共享 就是在分布式系统中,解决session不一致问题。当我们的应用需要两个或者两个以上的tomcat服务器来支撑时,就要考虑Session共享了。Session共享也叫Session同步或者Session一致性。 ??Spring为我们提供了Spring Session进行管理我们的HttpSession。Spring session 是将HttpSession存放在Redis中,这样我们session就能在多个服务器共享了。Session共享的步骤如下:
6.1 引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
6.2 application.yml配置spring session
spring:
session:
store-type: redis
6.3 @EnableRedisHttpSession注解
??@EnableRedisHttpSession注解:这个注解的主要作用是注册一个 SessionRepositoryFilter ,这个 Filter 会拦截所有的请求,对 Session 进行操作,也就是开启 Session 共享功能。详细源码解析参考
@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 24*60*60)
public class RedisSessionConfig {
}
属性 | 说明 |
---|
maxInactiveIntervalInSeconds | 设置 Session 失效时间。(单位:秒) | redisFlushMode | Redis 会话的刷新模式。(默认值为 ON_SAVE ) | cleanupCron | 过期会话清理作业的 cron 表达式。 默认情况下每分钟运行一次。 | redisNamespace | 为键定义唯一的命名空间。 提示:该值用于通过将前缀从默认spring:session:更改为< redisNamespace>:来隔离会话。 | saveMode | 会话的保存模式。 |
6.4 测试的类
编写测试类:
@Controller
public class RedisSessionTestController {
@RequestMapping("/")
public String tologin(HttpSession session,HttpServletRequest req, Model model){
System.out.println("sessionID:"+session.getId()+"服务器端口号:"+req.getServerPort());
return "login";
}
@RequestMapping("tologin")
public String productlist(HttpSession session, HttpServletRequest req){
session.setAttribute("loginUser",new User("张良",22));
System.out.println("sessionID:"+session.getId()+"服务器端口号:"+req.getServerPort());
return "list";
}
}
使用图形界面查看redis中存入的相关信息: redis存储的session相关结构信息:
spring:session:expirations: (Set 结构):用户 ttl 过期时间记录 , 这个 key中的值是一个时间戳,根据这个 Session 过期时刻滚动至下一分钟而计算得出。这个 key 的过期时间为 Session 的最大过期时间 +5 分钟。spring:session:sessions: (Hash 结构):sessionAttr:Attribute名, 存储 Session 的详细信息,包括 Session 的过期时间间隔、最后的访问时间、attributes 的值。这个 key 的过期时间为 Session 的最大过期时间 +5 分钟。spring:session:sessions:expires: (String 结构):过期时间记录 : 这个 k-v 不存储任何有用数据,只是表示 Session 过期而设置。这个 key 在 Redis 中的过期时间即为 Session 的过期时间间隔。
6.5 更改 Spring Session的序列化器
??Spring Session 中默认的序列化器为(JdkSerializationRedisSerializer ),该序列化器效率低下,内存占用大。我们可以根据自己的需要更换其他序列化器,如 GenericJackson2JsonRedisSerializer 序列化器。部分源码解析如下:(从@EnableRedisHttpSession注解开始查看)
RedisHttpSessionConfiguration.class(部分代码)
@Configuration(proxyBeanMethods = false)
public class RedisHttpSessionConfiguration extends SpringHttpSessionConfiguration
implements BeanClassLoaderAware, EmbeddedValueResolverAware, ImportAware {
@Autowired(required = false)
@Qualifier("springSessionDefaultRedisSerializer")
public void setDefaultRedisSerializer(RedisSerializer<Object> defaultRedisSerializer) {
this.defaultRedisSerializer = defaultRedisSerializer;
}
@Bean
public RedisIndexedSessionRepository sessionRepository() {
RedisTemplate<Object, Object> redisTemplate = createRedisTemplate();
RedisIndexedSessionRepository sessionRepository = new RedisIndexedSessionRepository(redisTemplate);
if (this.indexResolver != null) {
sessionRepository.setIndexResolver(this.indexResolver);
}
if (this.defaultRedisSerializer != null) {
sessionRepository.setDefaultSerializer(this.defaultRedisSerializer);
}
return sessionRepository;
}
}
RedisIndexedSessionRepository.class(部分代码)
public class RedisIndexedSessionRepository
implements FindByIndexNameSessionRepository<RedisIndexedSessionRepository.RedisSession>, MessageListener {
private RedisSerializer<Object> defaultSerializer = new JdkSerializationRedisSerializer();
public void setDefaultSerializer(RedisSerializer<Object> defaultSerializer) {
Assert.notNull(defaultSerializer, "defaultSerializer cannot be null");
this.defaultSerializer = defaultSerializer;
}
}
在配置类中创建自定义序列化器:
@Configuration
public class SpringSessionConfig {
@Bean("springSessionDefaultRedisSerializer")
public RedisSerializer setSerializer(){
return new GenericJackson2JsonRedisSerializer();
}
}
再次运行项目查看序列化效果:
【注意】要序列化的类必须实现 java.io.Serializable 接口 (很重要!!)。未实现此接口的类将不会对其任何状态进行序列化或反序列化。
5.5 Nginx负载均衡查看session共享
1) 将完成的项目打成 jar 包,传到服务器的目录中:/root/jars 。 2) 启动Nginx服务器;(配置如下)
upstream myTomcatServer{
server 192.168.238.66:8080;
server 192.168.238.66:8081;
}
server{
listen 9005;
server_name www.123.com;
location / {
proxy_pass http://myTomcatServer;
}
}
3) 使用命令行运行 jar 包项目。
java -jar /root/jars/redis-session.jar
java -jar jars/redis-session.jar --server.port=8081
4) 查看session共享情况。
【拓展知识】Spring Session提供了3种方式存储session的方式:
@EnableRedisHttpSession -存放在缓存redis@EnableMongoHttpSession -存放在Nosql的MongoDB@EnableJdbcHttpSession -存放在数据库
七、Redis工具类(自定义功能)
|