一、Redis介绍
现存问题:
- 关系型数据查询效率问题
- 单体架构搭建集群后,数据不同步(Session数据不共享)
- 单体架构搭建集群后,或者分布式架构中,传统的锁操作问题
- 高并发读写数据问题,海量数据问题
NoSQL:
- Not only SQL
- Redis是一个key-value的非关系型数据库
Redis介绍:
- Redis是基于C语言编写的。
- Redis是基于Key-Value存储数据的,并且对Value提供了丰富的数据结构。
- Redis是基于内存存储数据的,并且提供了响应的持久化操作。
- Redis在接收客户端请求时,是单线程接收处理,后续操作还是多线程。
- Redis还提供了各种主从,哨兵,集群的架构模式,方便弹性伸缩。
- Redis的读写能力,读的速度是110000次/s,写的速度是81000次/s 。
二、安装Redis
docker-compose.yml:
version: '3.1'
services:
redis:
restart: always
image: 10.9.12.254:5000/redis:5.0.7
container_name: redis
ports:
- 6379:6379
通过Redis容器内部的redis-cli命令连接Redis服务
- 进入Redis容器:docker exec -it redis bash
- 执行Redis客户端命令:redis-cli
图形化界面连接Redis(Redis DeskTop Manager)
三、Redis的存储结构
最常用的五种:
- key-string:value就是存储一个值
- key-hash:value就是一个Map结构,一般用于存储Java中的对象
- key-list:value就是一个列表,理解为是一个ArrayList,允许重复,存取有序,有下标
- key-set:value就是一个集合,理解为HashSet,不允许重复,存取无序,没有下标
- key-zset:value就是一个有序的集合,在HashSet的基础上,给每一个值提供一个分数,根据分数排序
不常用的三种:
- HyperLogLog:用来做统计运算的,在丢失一部分精准度的前提下,占用更少的内存,更高效的运算出统计结果。
- GEO:存储经纬度的。
- 位图:存储bitmap结构。
四、Redis常用命令
- set key value:针对string结构,添加一个key和value
- get key:针对string结构,通过key获取value
- setex key second value:针对string结构,添加一个key和value的同时,设置生存时间,单位是秒
- expire key second :针对key,给key设置生存时间
- del key:针对key,删除key和具体的value
- hset key field value:针对hash结构,设置一个key和一个field以及value
- hget key field:针对hash结构,通过key和field获取一个value
- hmset key field value [field value …]: 针对hash结构,批量添加操作
- hgetall key:针对hash结构,获取全部的field value数据
五、Java操作Redis
- 创建项目
- 导入依赖
- junit,jedis,lombok,spring-context
- 编写测试类
六、Redis的其他命令
- 存值:
- set key value:设置一个key,value
- mset key value [key value …]:批量添加一个/多个key,value
- setex key second value:设置key,value的同时,给key设置生存时间
- setnx key value:当key不存在时,正常的执行set功能,如果key存在,什么事都不做
- 取值:
- get key:通过key获取value
- mget key [key …]:批量获取数据
- 自增/自减
- incr key:针对key的value自增1
- decr key:针对key的value自减1
- incrby key increment:针对key的value自增increment
- decrbykey increment:针对key的value自减increment
- 其他
- strlen key:查看key对应的字符串长度
- append key value:在key字符串后,追加上value
hash的常用命令
- 存值:
- hset key field value:设置field value结构数据到hash中
- hmset key field value [field value …]:批量添加field和value到hash中
- 取值
- hget key field:通过key和field获取一个value
- hmget key filed [field]:通过key和一个/多个field获取value
- hgetall key:通过key获取全部的field和value
- 其他
- hdel key field:删除field,不删除key
- hlen key:获取当前hash结构中field的数量
list的常用命令
- 存值:
- lpush key value [value …]:从列表的头部插入数据
- rpush key value [value …]:从列表的尾部插入数据
- 取值
- lrange key start stop:获取指定列表的从start开始查看到stop的全部数据,stop为-1,代表最后一个
- lpop key:从头部移除并获取数据结果
- rpop key:从尾部移除并获取数据结果
- 其他操作
- lrem key count value:删除当前列表中count个value,count > 0 从头删,count < 0 从尾删,count = 0,删除全部的value
- ltrim key start stop:保留当前列表从start到stop的数据,其余数据删除
set的常用命令
- 存值
- sadd key member [member …]:存值操作
- 取值
- smembers key:获取全部数据
- spop key [count]:随机获取一个值,并删除
- srandmember key [count]:随机获取一个值,不删除
- 集合操作
- sinter key [key …]:交集操作
- sunion key [key …]:并集操作
- sdiff key [key …]:差集操作
- 其他操作
- srem key member [member …]:删除集中的元素
- sismember key member:查看当前元素是否在集合中
- scard key :查看当前集合长度
zset常用命令
- 存值:
- zadd key score member [score member …]:添加数据,并设置分数
- 取值
- 基于下标取值
- zrange key start stop [withscores]:基于下标范围取值
- zrevrange key start stop [withscores]:基于下标,从大到小的范围取值
- 基于分数取值
- zrangebyscore key min max [withscores] [limit offset count]:根据分数的范围,从小到大取
- zrevrangebyscore key max min [withscores] [limit offset count]:根据分数的范围,从大到小取
- -inf +inf:无穷小,无穷大
- (min (max:不带等于效果
- 基于下标取值
- 其他操作
- zincrby key score member:增加某个member的分数
key的常用命令
- 生存时间:
- expire key second:给key设置生存时间
- pexpire key millionsecond:给key设置生存时间,单位:毫秒
- expireat key timestamp:设置key能生存到什么时间节点
- pexpireat key milliontimestamp:设置key能生存到什么时间节点,单位:毫秒
- ttl key:查看key剩余的生存时间,-2(key不存在),-1(没有生存时间),0~+inf(单位秒)
- pttl key:查看key剩余的生存时间,-2(key不存在),-1(没有生存时间),0~+inf(单位毫秒)
- persist key:去除key的生存时间
- 查看key信息:
- keys patterns:查看当前库中的全部key,并返回 patterns可以编写通配符 *
- 删除key:
- del key [key …]:删除key
db的常用命令
- flushdb:清空当前库
- flushall:清空所有库
- dbsize:查看key的数量
七、Java操作Redis
- 使用Jedis对key和value进行操作。
- 为了存储对象:
- 基于jackson等json工具,将对象序列化为json字符串,存储到Redis
- 基于序列化工具,将对象序列化为byte[],存储到Redis
- 使用JedisPool连接池操作,避免频繁的创建Jedis和销毁Jedis造成的性能的损耗
- Jedis的管道操作:
- 当需要将大量的信息存储到Redis时,如果一次一次的将命令推送给Redis,在传输的过程中,消耗的时间是比较长的。
- 直接在客户端位置,将大量操作命令封装到管道中,一次性将全部命令发送给Redis服务。
八、改造项目
将WebMaster部署到Linux操作系统中
- 在Windows下成功启动:
- 创建了数据库,并且将脚本文件导入到数据库中
- 将连接数据库的db.properties文件的密码,修改为自己数据库的密码
- 启动项目,访问功能没有问题
- 将连接数据库的url等内容修改为Linux操作系统中的
- 将工程打成war包
- 将war包部署到Linux操作系统中
- 构建Dockerfile将openapi工程部署到Tomcat(JDK)的Docker容器中
- Dockerfile:
FROM daocloud.io/library/tomcat:8.5.55-jdk8
WORKDIR /usr/local/tomcat/webapps
RUN rm -rf *
RUN mkdir ROOT
COPY openapi.war /usr/local/tomcat/webapps/ROOT
WORKDIR /usr/local/tomcat/webapps/ROOT
RUN jar -xf openapi.war
docker-compose.yml:
version:
services:
openapi0:
build: ./openapi
image: openapi:1.0
container_name: openapi
ports:
- 8080:8080
openapi1:
build: ./openapi1
image: openapi1:1.0
container_name: openapi1
ports:
- 8081:8080
mysql:
image: 10.9.12.254:5000/mysql:5.7.24
container_name: mysql
ports:
- 3306:3306
environment:
MYSQL_ROOT_PASSWORD: root
TZ: Asia/Shanghai
command:
--lower_case_table_names=1
volumes:
- ./mysql_data/:/var/lib/mysql/
nginx:
image: 10.9.12.254:5000/nginx:lastest
container_name: nginx
ports:
- 80:80
volumes:
- ./conf.d/:/etc/nginx/conf.d/
通过Nginx代理到某一个服务器上时,会造成在8080或者8081的Tomcat服务器上登录,Tomcat服务器会给客户端返回一个JSessionID的Cookie值,客户端再次请求时,会携带这个JSessionID,并且会通过JSessionID去Tomcat中找Session,如果找到Session,直接使用,如果找不到,重新创建一个Session,并且需要返回一个全新的JSessionID。
九、Redis的配置信息
给Redis设置密码连接
设置密码连接的方式:
- 在第一次启动Redis后,可以直接通过客户端设置密码:CONFIG set requirepass password
- 修改Redis的配置文件,添加:requirepass password
修改docker配置映射Redis配置文件,并保证启动时,加载配置文件
- 创建数据卷映射Redis容器的目录
- 添加配置文件:redis.conf,并编写上述配置:
requirepass Zxcvb1.0
修改docker-compose.yml:
version: '3.1'
services:
redis:
restart: always
image: 10.9.12.254:5000/redis:5.0.7
container_name: redis
ports:
- 6379:6379
volumes:
- ./conf:/conf
command: ["redis-server","/conf/redis.conf"]
- 重启Docker容器,密码生效
三种客户端连接Redis:
- 图形化界面:在验证选项中,添加密码即可。
- redis-cli:需要在执行命令之前,先执行auth password
- Jedis:
- jedis.auth(“password”);
- 使用JedisPool由有参构造设置:
public JedisPool(GenericObjectPoolConfig poolConfig, String host, int port, int timeout, String password) { ...}
十、Redis的事务
- Redis的事务是将一系列命令存放到一个队列中,exec后一起执行,该成功的成功,该失败的失败。
- multi:开启事务 (开启事务后,所有后续的命令都会被放到队列中)
- exec:执行事务
- discard:取消事务
- Redis的事务时相对比较鸡肋的,一般情况下,配置watch去使用Redis的事务
- watch key [key …]:会监听一个或多个key的写操作,当有写操作后,可以配置Redis的事务自动取消
- unwatch:当事务执行或者取消后,自动执行unwatch
十一、Redis的持久化(重要)
RDB持久化机制:
- RDB默认开启
- RDB存储二进制数据,RDB在持久化和读取持久化文件时,速度较快
- RDB持久化的时机:
save second keys
# 在second时间内,执行了key次写操作,就执行RDB的持久化
-
- 优缺点:持久化效率高,RDB持久化时机导致RDB策略不安全 ,数据可能会丢失。
- AOF持久化机制:
- AOF默认关闭,需要将appendonly yes手动开启
- RDB默认持久化日志文件,将每次写操作的命令持久化到本地文件中,在持久化和读取持久化文件时,相对RDB较慢
- RDB的持久化时机:
appendfsync always # 每次执行写操作,立即执行AOF持久化
appendfsync everysec # 每秒执行一次AOF持久化,(推荐)
appendfsync no # Redis不去执行,由当前操作系统自动时间,时间不确定
-
- 优缺点:相对RDB更安全,但是持久化效率文件大小相对RDB较差
- 官网推荐同时开启RDB和AOF两种持久化机制:
- 在恢复数据时,AOF的优先级高于RDB
- RDB在执行持久化时,会通过AOF文件执行,导致AOF文件会覆盖RDB
- AOF持久化会存储很多无意义的命令,在期间针对一个key进行了多次写操作,可能只有最后一条写操作有效,AOF文件需要重写操作:
- 自动重写:
auto-aof-rewrite-percentage 100 # 默认AOF文件超过基准大小的100%时
auto-aof-rewrite-min-size 64mb # 默认AOF文件超过64M
手动重写:
bgrewriteaof # 手动在客户端执行当前命令,即可重写
#重写后的内容,会以二进制形式存在,再次执行写操作,还时以日志形式存储命令
十二、Redis的主从架构
- 现存问题:
- Redis的读写性能的瓶颈,单台Redis最多只能110000/s左右的读取性能,81000/s左右的写性能。
- 主从架构:
- Redis的主从架构需要搭建多台Redis服务,并且由一台作为Master,其他作为Slave
- Master节点负责读写操作,Slave节点只负责读操作
- Slave会主动找Master同步数据,并且多台Slave之间没有关联
- 一台Master可以由多个Slave,一台Slave只能属于一个Master
- 搭建主从架构:
- 在从节点的服务中,添加配置:
replicaof <masterip> <masterport> # 设置master节点的ip和port
masterauth <master-password> # 设置master节点的AUTH密码
十三、Redis的哨兵
- 现存问题:
- 主从架构虽然可以提升Redis的读取数据的瓶颈,但是当Master节点宕机后,主从架构无法进行写操作。
- 主从 + 哨兵:
- 哨兵的搭建是在主从架构基础上的,目的是为了解决主从的单点故障问题。
- 需要在每台Redis节点上都搭建一个哨兵,并且让哨兵之间可以相互通讯。
- 当Master节点宕机后,需要根据哨兵的投票结果,来确定Master是否真的宕机。
- 搭建主从 + 哨兵:
- 由于使用Docker搭建Redis的主从 + 哨兵,需要使用yml文件中的links顺序,来确定容器的名称,并且使用容器内部的端口。
- 准备yml文件:
version: "3.1"
services:
redis1:
image: daocloud.io/library/redis:5.0.7
restart: always
container_name: redis1
environment:
- TZ=Asia/Shanghai
ports:
- 7001:6379
volumes:
- ./conf/redis1.conf:/usr/local/redis/redis.conf # 提前将文件构建出来
- ./conf/sentinel1.conf:/data/sentinel.conf # 添加的内容
command: ["redis-server","/usr/local/redis/redis.conf"]
redis2:
image: daocloud.io/library/redis:5.0.7
restart: always
container_name: redis2
environment:
- TZ=Asia/Shanghai
ports:
- 7002:6379
volumes:
- ./conf/redis2.conf:/usr/local/redis/redis.conf
- ./conf/sentinel2.conf:/data/sentinel.conf # 添加的内容
links:
- redis1:master
command: ["redis-server","/usr/local/redis/redis.conf"]
redis3:
image: daocloud.io/library/redis:5.0.7
restart: always
container_name: redis3
environment:
- TZ=Asia/Shanghai
ports:
- 7003:6379
volumes:
- ./conf/redis3.conf:/usr/local/redis/redis.conf
- ./conf/sentinel3.conf:/data/sentinel.conf # 添加的内容
links:
- redis1:master
command: ["redis-server","/usr/local/redis/redis.conf"]
准备redis.conf和sentinel.conf文件
#redis.conf (从)
replicaof master 6379
# ============================================================
# sentinel.conf
# 哨兵需要后台启动
daemonize yes
# 指定Master节点的ip和端口(主)
sentinel monitor master localhost 6379 2
# 指定Master节点的ip和端口(从)
sentinel monitor master master 6379 2
# 哨兵每隔多久监听一次redis架构
sentinel down-after-milliseconds master 10000
十四、Redis集群(重要)
Redis集群的特点
搭建过程
准备Redis的配置文件
# redis.conf
# 指定redis的端口号
port 端口号
# 开启Redis集群
cluster-enabled yes
# 集群信息的文件
cluster-config-file 集群节点名称
# Docker安装需要配置
# 集群的对外ip地址
cluster-announce-ip 192.168.11.11
# 集群的对外port
cluster-announce-port 7006
# 集群的总线端口
cluster-announce-bus-port 17006
- 准备2n + 1个节点,再配上至少1个slave节点
- 启动6个Redis节点后,集群并没有启动,启动集群需要执行:
redis-cli --cluster create ip:port ... ipN:portN --cluster-replicas <args>
客户端连接Redis集群
- redis-cli:
- redis-cli -h ip地址 -p 端口号 -c
- 图形化界面:
- 连接任意一个几点即可,查看到整个Redis集群中的数据
- Java中通过Jedis连接集群:
- JedisCluster对象即可
十五、Redis的常见问题(重要)
删除策略:
Redis中的key到了生存时间,一定会被删除吗?
- 定期删除:默认情况下每100ms查询3个设置了生存时间的key,查看是否到期。
- 惰性删除:当你去查询一个设置了生存时间的key时,Redis会查看生存时间是否到期,如果到期,直接删除当前key,并且返回一个空。
淘汰机制
- volatile(设置生存时间的key),lru(最近最少使用),lfu(最近最少频次使用)
- volatile-lru -> Evict using approximated LRU among the keys with an expire set.
- allkeys-lru -> Evict any key using approximated LRU.
- volatile-lfu -> Evict using approximated LFU among the keys with an expire set.
- allkeys-lfu -> Evict any key using approximated LFU.
- volatile-random -> Remove a random key among the ones with an expire set.
- allkeys-random -> Remove a random key, any key.
- volatile-ttl -> Remove the key with the nearest expire time (minor TTL)
- noeviction -> Don’t evict anything, just return an error on write operations,抛异常
缓存的各种问题:
version: "3.1"
services:
elasticsearch:
image: daocloud.io/library/elasticsearch:7.6.2
restart: always
container_name: elasticsearch
ports:
- 9200:9200
environment:
- "discovery.type=single-node"
- "ES_JAVA_OPTS=-Xms256m -Xmx256m"
volumes:
- ./data:/usr/share/elasticsearch/data
kibana:
image: daocloud.io/library/kibana:7.6.2
restart: always
container_name: kibana
ports:
- 5601:5601
environment:
- elasticsearch_url=http://你虚拟机的ip地址:9200
depends_on:
- elasticsearch