Redis学习
一、介绍
- 由与用户量增大,请求数量也随之增大,数据压力过大
- 多台服务器之间数据不同步
- 多台服务器之间的锁已经不存在互斥性了
Redis是基于内存存储数据和读取数据的
可以将之前存在Session中的共享数据统一放在Redis中
Redis基于它接受用户请求时单线程的,实现类似锁的功能
NoSQL
代表非关系型数据库not only sql
- Key-Value:Redis
- 文档型:ElasticSerch,Solr,Mongodb
- 面向列:hbase,cassandra
- 图形化:Neo4j
二、安装
version: '3.1'
services:
redis:
image: daocloud.io/library/redis:5.0.7
restart: always
container_name: redis
environment:
- TZ=Asia/Shanghai
ports:
-6379:6379
和之前操作一样
创一个文件夹新建一个文件yml的,然后docker-compose up -d运行以下就行
下载redis官网的图形化操作软件之后的操作
其他不用动先
三、Redis常用命令
3.1Redis存储数据的结构
常用的物种数据结构
key-string:一个key对应一个值
key-hash: 一个key对应一个map(存储对象数据)
key-list:一个key对应一个列表 (使用list实现栈和队列结构)
key-set:一个key对应一个集合 (交集差集和并集的操作)
key-zset:一个key对应一个有序的集合 (排行榜)
另外三种
HyperLogLog:计算近似值
GEO:地理位置
BIT:一般存储的也是一个字符串,存储的时byte[]
3.2 string常用命令
1.添加值
set key value
2.取值
get key
3.批量操作
mset 、mget
4.自增命令(自增1)
incr key
5.自减功能
decr key
6.自增自减指定数量
incrby key increment
decrby key increment
7.设置值的同时,指定生成时间
setex key second value
8.设置值,如果当前key不存在,如果存在,什么事不发生
setnx key value
9.在key对应的value过后追加内容
append key value
10.查看value长度
strlen key
3.3 hash常用命令
1.存储数据
hset key field value
2.获取数据
hget key field
3.批量操作
hmset key field value field value
hmget key field field ..
4.自增(指定自增的值)
hincrby key field increment
5.设置值的方式
hsetnx key field value
6.检查field是否存在
hexists key field
7删除field的记录
hdel key field
8.获取当前哈希结构中的全部field和value
hgetall key
9获取全部field
hkeys key
#获取全部value
hvals key
11.获取field数量
hlen key
3.4list常用命令
1.存储数据
lpush key value ...
rpush key value.. 从右侧插入
2.存储数据 如果key不存在,或者不是list结构什么事都不做
lpushx key valuie 只能增加一个
rpushx key value
3.修改数据(指定索引位置,会覆盖索引位置的数据)
lset key index value
4.获取数据 弹栈方式(左侧或者右侧弹出数据)
lpop key
rpop key
5.获取范围内的数据(stop-1代表最后一个)
lrang key start stop
6.指定索引位置的内容
lindex key index
7.获取整个列表的长度
llen key
8.删除(删除当前列表中count个value值,若是count>0从左侧向右侧删除小于0则容右侧向左侧删除 count==0删除全部value)
lrem key count value
9保留列表中范围内的数据
ltrim key start stop
10.将列表中最后一个数据,插入到另外一个列表头部位置
rpoplpush list1 list2
3.5set常用命令
#1.存储数据
sadd key member [member..]
2.获取全部数据
smembers key
3.随机获取(同时移除)
spop key
4.交集
sinter set1 set2...
5.并集
sunion set1 set2
6.差集
sdiff set1 set2 ...
7.删除数据(指定value)
srem key member [member..]
8.查看set是否包含这个值
sismember key member
3.6zset命令
1.添加数据(score必须是数值,member不允许重复)
zadd key score member(。。。)
2.修改member的分数(如果member存在key中,正常添加,如果member不存在,就相当于zadd
zincrby key increment member
3.查询(查看指定member的分数)
zscore key member
4.查询数量
zcard key
5.查询score范围内的member数量
zount key min max
6.删除
zrem key member..
7.根据分数从小到大排序,获取指定范围内的,如果添加scop会返回member对应的分数
zrange key srart stop [withscore]
8.根据分数从大到小排序,获取指定范围内的,如果添加scop会返回member对应的分数
zrevrange key start stop [withscore]
9.zrangebyscore key min max [withscore]] [limit offset count]
10.
zrevrangebyscore key max min [withscore] [limit offset count]
如果写(那就是小于但不等于的方式找 +inf -inf最大值和最小值
3.7key命令
1.全部key
keys pattern
2.查看某一个key是否存在
exists key(1存在 0不存在)
3.删除key
del key。。
4.设置key的生存时间
expire key second
pexpire key milloseconds
5.设置时间点
expireat key timestamp
pxeprieat key milloseconds
6.查看剩余时间
ttl key
pttl key -2 当前key不存在 -1 没有设置时间
7.移除生成时间
persist key (1移除成功 0 不存在生存时间 或者key不存在
8.操作库
select 0~15
9移动库
move key 0~15
3.8库的常用命令
1.清空当前所在的库
flushdb
2.清空全部
flushall
3.查看当前数据库有多少key
dbsize
4.最后一次操作时间
lastsave
5.实时监控redis服务接收到的命令
monitor
Java连接redis
- jedis连接
- lettuce连接redis
4.1
- 创建Maven项目
- 导入依赖
- 测试
<dependencies>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.10.RELEASE</version>
</dependency>
</dependencies>
public static void main(String[] args) {
/*连接redis*/
Jedis jedis=new Jedis("192.168.0.106",6379);
/*操作redis*/
jedis.set("name","李四");
/*释放资源*/
jedis.close();
}
4.2存储对象
@Test
public void setByteArray(){
Jedis jedis=new Jedis("192.168.0.106",6379);
/*准备key-value*/
String key="user";
User user=new User(1,"张三");
/*将key和value转换成byte【】*/
byte[] value=SerializationUtils.serialize(user);
byte[] keys = SerializationUtils.serialize(key);
/*存储到redis*/
jedis.set(keys,value);
/*释放资源*/
jedis.close();
}
@Test
public void getall(){
Jedis jedis=new Jedis("192.168.0.106",6379);
/*准备key*/
String key="user";
/*将key装换位byte*/
byte[] keys=SerializationUtils.serialize(key);
/*jedis去redis、获取value*/
byte[] value=jedis.get(keys);
/*将value反序列化*/
User user=(User)SerializationUtils.deserialize(value);
/*输出*/
System.out.println(user);
/*释放资源*/
jedis.close();
}
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
Jedis连接池
@Test
public void pool(){
/*创建连接池*/
JedisPool pool=new JedisPool("192.168.0.106",6379);
/*通过连接词获得对象*/
Jedis jedis=pool.getResource();
String value=jedis.get("user");
System.out.println(value);
jedis.close();
}
@Test
public void pool2(){
/*创建连接池的连接信息*/
GenericObjectPoolConfig poolConfig=new GenericObjectPoolConfig();
poolConfig.setMaxTotal(100);/*连接池中最大的活跃数*/
poolConfig.setMaxIdle(10);/*最大空弦数*/
poolConfig.setMinIdle(5);
poolConfig.setMaxWaitMillis(3000);/*当连接池空了,多久没获取到连接池,就超时*/
/*创建连接池*/
JedisPool pool=new JedisPool(poolConfig,"192.168.0.106",6379);
Jedis jedis=pool.getResource();
String value=jedis.get("user");
System.out.println(value);
jedis.close();
}
5.1redis管道操作\
解决如下问题
解决方案
@Test
public void pipeline(){
long i=System.currentTimeMillis();
/*创建连接池*/
JedisPool jedisPool=new JedisPool("192.168.0.106",6379);
/* *//*获取一个连接对象*//*
Jedis jedis=jedisPool.getResource();
*//*执行ince-100000*//*
for (int j = 0; j <10000 ; j++) {
jedis.incr("pp");
}
*//*释放资源*//*
jedis.close();*/
/*管道的方式*/
/*获取一个连接对象*/
Jedis jedis1=jedisPool.getResource();
/*创建管道*/
Pipeline pipeline=jedis1.pipelined();
/*执行ince*/
for (int j = 0; j <10000 ; j++) {
pipeline.incr("qq");
}
/*执行命令*/
pipeline.syncAndReturnAll();
/*释放资源*/
jedis1.close();
System.out.println(System.currentTimeMillis()-i);
}
时间减少了很多倍
5.2修改配置文件
version: '3.1'
services:
redis:
image: daocloud.io/library/redis:5.0.7
restart: always
container_name: redis
environment:
- TZ=Asia/Shanghai
ports:
- 6379:6379
volumes:
- ./conf/redis.conf:/usr/local/redis/redis.conf
command: ["redis-server","/usr/local/redis/redis.conf"]
增加密码
在conf目录下的redis.conf里面添加如下代码
requirepass xxxx
这样再次重启redis容器然后进入redis-cli后会要求你输入密码
auth xxx (这是校验的代码)
在java代码中可以通过连接池的构造函数传入密码
在linux中可以通过在redis-cli中输入
config set requirepass xxxx
@Test
public void pool2(){
/*创建连接池的连接信息*/
GenericObjectPoolConfig poolConfig=new GenericObjectPoolConfig();
poolConfig.setMaxTotal(100);/*连接池中最大的活跃数*/
poolConfig.setMaxIdle(10);/*最大空弦数*/
poolConfig.setMinIdle(5);
poolConfig.setMaxWaitMillis(3000);/*当连接池空了,多久没获取到连接池,就超时*/
/*创建连接池*/
JedisPool pool=new JedisPool(poolConfig,"192.168.0.106",6379,3000,"xxxx");
Jedis jedis=pool.getResource();
String value=jedis.get("user");
System.out.println(value);
jedis.close();
}
5.3Redis事务
Redis事务,一次事务操作,该成功的成功,该失败的失败
先开启事务,执行一些命令,但是不会立即执行,会被放在队列中,如果执行事务,那么这个队列中的命令全部执行,如果取消了事务,一个队列中的命令全部作废
- 开启事务:multi
- 输入要执行的命令->被放到队列中
- 执行事务:exec
- 取消事务:discard
watch监听机制
在开启事务之前,先通过watch命令去监听一个或多个key,在开启事务之后,如果由其他个客户端修改了我监听的key,事务会自动取消
5.4redis持久化机制
rdbcompression yes
dbfilename dump.rdb
rdb是redis默认的持久化机制
1.RDB持久化文件速度比较快,而且存储的是一个二进制文件,传输起来方便
2. 持久化的时间
3. save 900 1 在900秒内,有一个key改变了,就执行rdb持久化
save 300 10
save 60 10000
4,无法保证数据的绝对安全
文件存到了之前yml配置下的data路径 可通过cat查看
最后shutdown save即可以持久化了
5.5AOF
#开启AOF持久化
appendonly yes
#aof文件的名称
appendfilename "redis.aof"
#aof持久化执行的时机
# appendfsync always
appendfsync everysec
# appendfsync no
最后shutdown nosave也可以持久化
1.aof持久化相对于rdb较慢的,存储的是一个文本文件,到了后期文件会比较大,传输困难
2.时机
# appendfsync always 没执行一个写操作,立即持久化到aof文件中,性能比较低
appendfsync everysec 每秒执行一次持久化
# appendfsync no 根据你的操作系统不同,环境不同,在一定时间内执行一次持久化
3.AOF相对RDB更安全,推荐同时开启AOF和RDB
4.如果同时开启RDB和AOF持久化,那么在Redis宕机重启之后,需要加载一个持久化文件,优先选择AOF文件。
5.如果先开启RDB,在开启AOF,如果RBD执行了持久化,那么RDB文件中的内容会被AOF覆盖掉
5.5主从架构
分担读写的压力,相对于之前的单机版的redis,主从架构的redis有多个redis,并且各司其职
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
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
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
links:
- redis1:master
command: ["redis-server","/usr/local/redis/redis.conf"]
#从节点配置
replicaof master 6379
问题:如果master挂掉了,整个redis架构i就挂掉了
5.6哨兵
#哨兵需要后台启动
daemonize yes
#指定master节点的ip
sentinel monitor master localhost 6379 2(主的)
sentinel monitor master master 6379 2(sleve)
#哨兵每隔多久执行一次
sentinel down-after-milliseconds mymaster 10000
启动哨兵
每个redis都要启动一次包括redis2和3的
5.7集群搭建
Redis集群在保证主从 加哨兵的基本功能之外,还能提升存储数据的能力
#dicker-compose.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:7001
- 17001:17001
volumes:
- ./conf/redis1.conf:/usr/local/redis/redis.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:7002
- 17002:17002
volumes:
- ./conf/redis2.conf:/usr/local/redis/redis.conf
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:7003
- 17003:17003
volumes:
- ./conf/redis3.conf:/usr/local/redis/redis.conf
command: ["redis-server","/usr/local/redis/redis.conf"]
redis4:
image: daocloud.io/library/redis:5.0.7
restart: always
container_name: redis4
environment:
- TZ=Asia/Shanghai
ports:
- 7004:7004
- 17004:17004
volumes:
- ./conf/redis4.conf:/usr/local/redis/redis.conf
command: ["redis-server","/usr/local/redis/redis.conf"]
redis5:
image: daocloud.io/library/redis:5.0.7
restart: always
container_name: redis5
environment:
- TZ=Asia/Shanghai
ports:
- 7005:7005
- 17005:17005
volumes:
- ./conf/redis5.conf:/usr/local/redis/redis.conf
command: ["redis-server","/usr/local/redis/redis.conf"]
redis6:
image: daocloud.io/library/redis:5.0.7
restart: always
container_name: redis6
environment:
- TZ=Asia/Shanghai
ports:
- 7006:7006
- 17006:17006
volumes:
- ./conf/redis6.conf:/usr/local/redis/redis.conf
command: ["redis-server","/usr/local/redis/redis.conf"]
以下内容放在conf目录下,每一个redis都要有一个
#指定端口号
port 7001
#开启集群
cluster-enable yes
#集群信息的文件
cluster-config-file nodes-7001.conf
#集群对外ip地址
cluster-announce-ip 192.168.0.106
cluster-announce-port 7001
#集群总线端口
cluster-announce-bus-port 17001
在启动的时候,搭建集群代码
redis-cli --cluster create 192.168.0.106:7001 192.168.0.106:7002 192.168.0.106:7003 192.168.0.106:7004 192.168.0.106:7005 192.168.0.106:7006 --cluster-replicas 1(有几个从节点,只管备份的)
启动时redis redis-cli -h 192.168.0.106 -p 7001就会在之后增加数据时自动计算加在哪一个集群中。
用java连接
Set nodes=new HashSet<>();
nodes.add(new HostAndPort(“192.168.0.106”,7001));
nodes.add(new HostAndPort(“192.168.0.106”,7002));
nodes.add(new HostAndPort(“192.168.0.106”,7003));
nodes.add(new HostAndPort(“192.168.0.106”,7004));
nodes.add(new HostAndPort(“192.168.0.106”,7005));
JedisCluster jedisCluster=new JedisCluster(nodes);
jedisCluster.get(“a”);
常见问题
6.1如果key的生存时间到了,会立即删除嘛?
- 定期删除
Redis每个一段时间就回去查看Redis设置了过期时间的key,会在100ms的见各种默认查看3个key - 惰性删除
如果去查询已经过了生存时间的key,Redis会先查看当前key的生存时间,是否已经到了,直接删除key,返回用户一个控制。
6.2Reids的淘汰机制
在Redis中内存已经满的时候添加了新的策略,会进行淘汰机制。
#volatile-lru:在内存不足时,Redis会在是指过了生存时间的key中干掉一个最近最少使用的key
#allkeys-lru:在全部key中干掉一个最近最少使用的
#volatile-lfu:Redis会在设置了生存时间的key中干掉一个最近最少频次使用的key
#allkey-lfu;Redis会在全部的key中干掉一个最近最少频次使用的key
#volatile-random:Redis会在设置了生存时间的key中随机干掉一个
#allkey-random:Redis会在全部的key中随机干掉一个
#volatile-ttl:会在设置过生存时间的key中剩余时间最少的key
#noeviction:直接报错
maxmemory-policy 规则
maxmemory 字节大小
6.3缓存问题
缓存穿透
缓存击穿
缓存雪崩
缓存倾斜