一、Redis和NoSQL
1. 问题
- 读写分离,主从复制:提高读写性能和读库的可扩展性,解决单点问题
- 数据库分离:解决不同业务的访问数据库压力不同的问题
- NoSQL:解决大数据问题
- 单机Mysql的架构,随着用户数的增长,并发读写数据库成为瓶颈,引入本地缓存和分布式缓存得以解决
- 单机Tomcat压力大,响应逐渐变慢,引入反向代理实现负载均衡得以解决
2. NoSQL
2.1 Nosql介绍
NoSQL(Not Only SQL**),意即“不仅仅是SQL”,泛指非关系型的数据库。随着互联网网站的兴起,传统的关系数据库在应付特别是超大规模和高并发类型纯动态网站已经显得力不从心,暴露了很多难以克服的问题
2.2 NoSQL类别
-
KV型NoSql(代表----Redis) 基于内存,读写快速
-
列式NoSql(代表----HBase)适用于大规模实时数据
-
文档型NoSql(代表----MongoDB)使用于博客类型
-
搜索型NoSql(代表----ElasticSearch)适用于海量数据搜索,电商平台
特点:
-
格式灵活:存储数据的格式可以是key,value形式、文档形式、图片形式等等,文档形式、图片形式等等,使用灵活,应用场景广泛,而关系型数据库则只支持基础类型
-
速度快,高扩展性,低成本
-
不提供sql支持,学习和使用成本较高,无事务处理,数据结构相对复杂,复杂查询方面稍欠
3. Redis
3.1 Redis介绍
Redis是一个使用ANSI C编写的开源、包含多种数据结构、支持网络、基于内存、可选持久性的键值对存储数据库
3.2 Redis特性:
- 基于内存运行,性能高效
- 支持分布式
- key-value存储
- 提供多种语言的API
3.3 redis基础知识
- 默认16的库 0 - 15
- io多路复用技术
- 单线程
二、Redis下载安装
1. 拉取镜像
docker pull redis
2. 运行
docker run -itd --name myFirstRedis -p 6379:6379 redis
3. 检查
docker exec -it myFirstRedis /bin/bash
redis-server --version
4. 启动客户端
root@3f48963da1a0:/data# redis-cli
127.0.0.1:6379> exit
三、Redis的指令
1. 基本命令
# 选择库
select index
# 设置当前库下key value
set k 1
# 获取当前库k的value
get k
# 清空当前库
flushdb
# 清空所有库
flushall
2. key命令
# 查看当前库中所有的key(生产环境不可用)
keys * 获取
# 查看当前库中所有的key(生产环境可用)
redis-cli --scan "u*"
# 判断时候存在 返回个数
exists k k1
# 查看当前key 所储存的值的类型
type k
# 删除已存在的key
del k k2
# 给key设置time秒的过期时间。设置成功返回 1 当 key 不存在返回 0 单位s
expire key time
# 以秒为单位返回 key 的剩余过期时间 过期为-2
ttl k
# 移除给定 key 的过期时间,使得 key 永不过期 为-1
persist k
3. string 类型以及命令
# 给定k v
set key value
# 用于获取指定 key 的值
get k
# 将给定的value追加到key原值末尾 key 不存在 为set
127.0.0.1:6379> set k2 zh # OK
127.0.0.1:6379> get k2 # "zh"
127.0.0.1:6379> APPEND k2 ao # (integer) 4
127.0.0.1:6379> get k2 # "zhao"
# strlen 获取指定 key 所储存的字符串值的长度 不是字符串是错误
strlen key
# setex 给指定的 key 设置值及time 秒的过期时间
setex k t v
# setnx 只有在key不存在时设置key的值
# 分布式锁
setnx key value
# getrange 获取指定区间范围内的值
getrange k2 1 3
# setrange 获取指定区间范围内的值
setrange k2 5 bin
# incr 将 key 中储存的数字值增一
incr k
# 将 key 中储存的数字值增step
incrby key step
# dncr 将 key 中储存的数字值减一
dncr k
# 将 key 中储存的数字值减step
decrby key step
# 同时设置一个或多个 key-value
mset k v a b
# 返回所有(一个或多个)给定 key 的值
mget k a
# getset 获取并设置
getset k 101
4. list命令
# 双向链表,对两段操作性能极高,通过索引操作中间的节点性能较差
# lpush/rpush 从左边(头部)/右边(尾部)插入一个或多个值
# lpush/rpush k1 v1 v2 v3……
LPUSH kk 1 1 2 2 3 4 5
# lrange 返回key列表中的start和end之间的元素(包含start和end)
# lrange key start end
127.0.0.1:6379> LRANGE kk 0 -1
1) "5"
2) "4"
3) "3"
4) "2"
5) "2"
6) "1"
7) "1"
# lpop/rpop 移除并返回第一个值或最后一个值
# lpop/rpop key
127.0.0.1:6379> LRANGE kk 0 -1
1) "4"
2) "3"
3) "2"
4) "2"
5) "1"
6) "1"
# lindex 获取列表index位置的值(从左开始)
# lindex key index
127.0.0.1:6379> LINDEX kk 0
"4"
# llen 获取列表长度
# llen key
127.0.0.1:6379> LLEN kk
(integer) 6
# lrem 从左边开始删除与value相同的count个元素。
# lrem key count value
127.0.0.1:6379> LREM kk 4 2
(integer) 2
127.0.0.1:6379> LRANGE kk 0 -1
1) "4"
2) "3"
3) "1"
4) "1"
# linsert 在列表中value值的前边/后边插入一个new value值(从左开始)
# linsert key before/after value newvalue
127.0.0.1:6379> LRANGE kk 0 -1
1) "4"
2) "3"
3) "1"
4) "1"
127.0.0.1:6379> LINSERT kk before 4 0
(integer) 5
127.0.0.1:6379> LRANGE kk 0 -1
1) "0"
2) "4"
3) "3"
4) "1"
5) "1"
# lset 将索引为index的值设置为value
# lset k index value
127.0.0.1:6379> LSET kk 0 521
OK
127.0.0.1:6379> LRANGE kk 0 -1
1) "521"
2) "4"
3) "3"
4) "1"
5) "1"
5. set命令
# Set是String类型的无序集合
# 它底层其实是一个value为null的hash表,所以添加、删除、查找的时间复杂度都是O(1)
# sadd 将一个或多个元素添加到集合key中,已经存在的元素将被忽略
sadd k a b
127.0.0.1:6379> SADD set1 10 20 30 40 50
(integer) 5
# smember 取出该集合的所有元素
127.0.0.1:6379> SMEMBERS set1
1) "10"
2) "20"
3) "30"
4) "40"
5) "50"
# sismember 判断集合key中是否含有value元素,如有返回1,否则返回0
127.0.0.1:6379> SISMEMBER set1 10
(integer) 1
# scard 返回该集合的元素个数
127.0.0.1:6379> SCARD set1
(integer) 5
# srem 删除集合中的一个或多个成员元素,不存在的成员元素会被忽略
127.0.0.1:6379> SREM set1 40
(integer) 1
127.0.0.1:6379> SMEMBERS set1
1) "10"
2) "20"
3) "30"
4) "50"
# spop 随机删除集合中一个元素并返回该元素
127.0.0.1:6379> SPOP set1
"30"
# srandmember 随机取出集合中count个元素,但不会删除
srandmember set1 2
# smove 将value元素从sourcekey集合移动到destinationkey集合中
smove k1 k2 v5
# sinter 返回两个集合的交集元素
sinter set1 set2
# sunion 返回两个集合的并集元素
sunion set1 set2
# sdiff 返回两个集合的差集元素(key1中的,不包含key2)
sdiff set1 set2
6. hash命令
# Hash是一个键值对的集合 hash 特别适合用于存储对象
# hset 给key集合中的field赋值value
127.0.0.1:6379> HSET h1 name zhaobin age 22 sex man
(integer) 3
# hget 从key哈希中,取出field字段的值
127.0.0.1:6379> HGET h1 name
"zhaobin"
# hexists 判断指定key中是否存在field
127.0.0.1:6379> HEXISTS h1 name
(integer) 1
127.0.0.1:6379> HEXISTS h1 na
(integer) 0
# hkeys 获取该哈希中所有的field
# hvals key 获取该哈希中所有的value
127.0.0.1:6379> HKEYS h1
1) "name"
2) "age"
3) "sex"
127.0.0.1:6379> HVALS h1
1) "zhaobin"
2) "22"
3) "man"
# hincrby 为哈希表key中的field字段的值加上增量increment
127.0.0.1:6379> HINCRBY h1 age 20
(integer) 42
127.0.0.1:6379> HGET h1 age
"42"
# hdel 删除哈希表 key 中的一个或多个指定字段,不存在的字段将被忽略
127.0.0.1:6379> HDEL h1 name
(integer) 1
# hsetnx 给key哈希表中不存在的的字段赋值
127.0.0.1:6379> HSETNX h1 addr keDong
(integer) 1
7. zset
# Zset与Set非常相似,没有重复元素 不同的是Zset的每个元素都关联了一个score 这个分数被用来排序
# 集合的元素是唯一的但分数可以重复
# zadd 将一个或多个元素(value)及分数(score)加入到有序集key中
127.0.0.1:6379> ZADD z 100 ming 200 bin 300 li
(integer) 3
# zrange 返回key集合中的索引start和索引end之间的元素(包含start和end)
127.0.0.1:6379> ZRANGE z 0 -1
1) "ming"
2) "bin"
3) "li"
127.0.0.1:6379> ZRANGE z 0 -1 withscores
1) "ming"
2) "100"
3) "bin"
4) "200"
5) "li"
6) "300"
# zrangebyscore 返回key集合中的两个分数之间的元素 其中元素的位置按分数值递增(从小到大)来排序
127.0.0.1:6379> ZRANGEBYSCORE z 200 500
1) "bin"
2) "li"
# zincrby 为元素value的score加上increment的值
# zrem 删除该集合下value的元素
# zcount 统计该集合在minscore 到maxscore分数区间中元素的个数
# zrank 返回value在集合中的排名,从0开始
127.0.0.1:6379> ZRANGEBYSCORE z 200 500
1) "bin"
2) "li"
127.0.0.1:6379> ZINCRBY z 90 li
"390"
127.0.0.1:6379> ZREM z bin
(integer) 1
127.0.0.1:6379> ZCOUNT z 0 900
(integer) 2
127.0.0.1:6379> ZRANK z li
(integer) 1
127.0.0.1:6379> ZRANGE z 0 -1
1) "ming"
2) "li"
127.0.0.1:6379> ZRANGE z 0 -1 withscores
1) "ming"
2) "100"
3) "li"
4) "390"
8. Bitmaps
# 类似于数组和二进制的结合
# 适用于记录打卡天数 boolean 类型多存储
# setbit 设置Bitmaps中某个偏移量的值
127.0.0.1:6379> SETBIT day 0 0
(integer) 0
127.0.0.1:6379> SETBIT day 1 0
(integer) 0
# getbit 获取Bitmaps中某个偏移量的值
127.0.0.1:6379> GETBIT day 0
(integer) 0
127.0.0.1:6379> GETBIT day 1
(integer) 0
# bitcount 统计字符串被设置为1的bit数量。
127.0.0.1:6379> BITCOUNT day 0 -1
(integer) 1
# bitop 将多个bitmaps通过求交集、并集方式合并成一个新的bitmaps
# bitop and k3 k1 k2 通过求交集将k1 k2并成k3
# bitop or k3 k1 k2 通过求并集将k1 k2合并成k3
9 .Geospatia 地图
10 .HyperLogLog 统计数目、基数
四、java整合Redis
1. jedis
-
1. 引入依赖
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.6.0</version> </dependency>
-
2. 实例对象
// 第一个参数是ip地址 // 第二个参数是端口 Jedis jedis = new Jedis("8.130.19.220",6379);
2. Java整合Redis_Spring-Data-Redis
Spring封装了RedisTemplate对象来进行对Redis的各种操作,它支持所有的Redis原生的api
//字符串
redisTemplate.opsForValue();
//hash
redisTemplate.opsForHash();
//list
redisTemplate.opsForList();
//set
redisTemplate.opsForSet();
//set(scores)
redisTemplate.opsForZSet();
步骤
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
添加配置文件
#Redis服务器连接地址
spring.redis.host=192.168.56.31
#Redis服务器连接端口
spring.redis.port=6379
#连接池最大连接数(使用负值表示没有限制)
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=30000
添加配置类
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setKeySerializer(new StringRedisSerializer());
// 使用GenericJackson2JsonRedisSerializer 会报错 咱也不知道为啥
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
}
}
使用
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private StringRedisTemplate stringRedisTemplate;
在项目中使用缓存技术
@Autowired
private StringRedisTemplate redisTemplate;
// 需要在查询数据库是检查缓存时候存在key
redisTemplate.opsForValue().get(key);
// 若返回的不是空的则将json转换成对象
// 否则将对象读取并缓存
五、其他功能
1. 发布订阅
- 使用于聊天室
- SUBSCRIBE channel [channel …] 订阅
- PUBLISH channel message 发布
2. 流水线pipeline
// 减少在网络中传输的时间
// 在使用时使用
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
Pipeline pipeline = jedis.ppipelined();
pipeline.hset(k , v);
pipeline.syncAndReturnAll();
3. 持久化
内存 - - > 硬盘
Redis提供了两个不同形式的持久化方式:RDB(Redis DataBase) 、 AOF(Append Only File)
- docker默认为rdb
- rdb为五分钟快照一次
- aop为将历史写记录写入日志文件,可修改
在一定时间使用rdb持久化技术,在每秒使用aop技术
4. 事务
-
单独的隔离操作:事务中的所有命令都会按顺序地执行,事务在执行的过程中,不会被其他客户端发送来的命令请求所打断
-
无隔离级别的概念:队列中的命令没有提交之前都不会实际的被执行,redis是串行执行的,不存在事务内和事务外的问题
-
不保证原子性:redis同一个事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚
-
开启:以
MULTI
开始一个事务 -
入队:将多个命令入队到事务中,接到这些命令并不会立即执行,而是放到等待执行的事务队列里面
-
执行:由
EXEC
命令触发事务 -
// 使用MULTI开始事务 // 事务指令 // 使用EXEC 执行事务队列 或者 使用DISCARD取消事务 // 队列中有一个指令语法错误 其余全部指令全部不执行 // 有一个非语法错误 不执行当前指令