Redis6.0学习笔记(入门)

Redis6.0学习笔记(入门)

Redis中的key操作
查看当前库中所有的key值
keys  * 
设置key-value值
set key_name value //返回ok代表成功
查看key是否存在
exists key_name //返回值为1表示存在,0则不存在
查看key的type
type key_name 

redis的操作是原子操作,即不会被线程调度机制打断的操作

同时设置一个或多个key-value对
mset key1_name value1 key2_name value2.....
key值不存在的时候设置key的值
setnx key_name value
key值不存在的时候设置一个或多个key的值
msetnx key1_name value1 key2_name value2 key3_name value3.... // 原子性,有一个失败则都失败
设置键值的同时,设置过期时间
setex key_name time value
设置旧值的同时设置新值
getset key_name value
获取key的value值
get key_name
获取一个或多个key-value对
mget key1_name key2_name key3_name...
将给定的值追加到目标key的末尾
append key_name value
获得key值长度
strlen key_name   
将key中存储的数字增长1
incr key_name  //只能对数字值操作,如果为空,新增值为1
将key中存储的数字增长任意值
incrby key_name value
将key中存储的数字减一
decr key_name
将key中存储的数字减去任意值
decrby key_name value
删除指定key
del key_name //返回值为1表示成功
非阻塞删除
unlink key_name  //仅将keys从keyspace元数据中删除,真正的删除会在后续一步操作
设置key过期时间
expire key_name time //以s为单位
查看key的过期时间
ttl key_name  // -1代表永不过期 -2代表已经过期
查看当前库的key的数量
dbsize
清空当前库
flushdb
清空所有库
flushall
Redis的基本数据类型
String

String类型是二进制安全的,意味着Redis中的sring可以包含任何数据,比如jpg图片或者序列化对象
一个Redis中字符串valu最多可以是512M
字符串的实际分配空间(capacity)一般会大于他的len,当字符串小于1M的时候,每次扩容都是加倍现有空间
当字符串大于1M的时候,扩容一次只会增加1M的空间,字符串最大为512M

获取key的指定范围的值
getrange key_name start_number end_number
在key的指定位置插入值
setrange key_name start_number value
List

允许重复值

单键多值
底层:双向链表 对两端的操作性能高,中间的操作性能低

数据结构

qickList:链表+ziplist

插入数据
从左边插入一个或多个数据
lpush key_name value1 value2 value3...

例子: lpush k1 v1 v2 v3 
	 lrange k1 0 -1  //代表取所有
	 (1)v3
	 (2)v2
	 (3)v1
	 
从右边插入一个或多个数据
rpush key_name value1 value2 value3...

在list的某一值前或后插入数据
linsert key_name target_value new_value 

lpush原理(推箱子):

v1
v2v1
v3v2v1

rpush原理(同理):

v1
v1v2
v1v2v3
获取范围内的数据
从左边取
lrange key_name start_number end_number  // lrange k1 0 -1 代表list中的全部value 

lindex key_name index //取list中的某一索引的值

llen key_name  //获取list长度
弹出数据
rpop/lpop key_name //字面意思从左边或者右边弹出值

rpoppush key1_name key2_name //从key1列表右边吐出一个值,插到key2左边 
删除数据
lrem key_name n value // 从左边删除n个value
数据的替换
lset key_name index value //将列表下标为index的值替换为value
Set(集合 )

Set是string类型的无序集合.底层是一个value为null的hash表,所以添加,删除,查找的复杂度都是o(0),单位是menmber(成员)

集合中的值具有唯一性

数据结构

Set的数据结构是dict字典,字典是用哈希表实现的

添加一个或多个数据
sadd key_name value1 value2 value3....   
查看集合中所有的数据
smembers key_name 
判断集合中是否有某一个值
sismember key_name value
查询集合元素个数
scard key_name
删除集合中的某一元素
srem key_name value1 value2 value3...
在集合中随机弹值
spop key_name
集合中随机取n个值
srandmember key_name n
集合间传值
smove key1_name key2_name key1_value //相同的值会忽略,但是还是会删除
集合间取交集
sinter key1_name key2_name 
集合间取并集
sunion key1_name key2_name
集合间取差集
sdiif key1_name key2_name //key1中有的,不包含key2中的
Hash(哈希)

hash是一个string类型的field和value的映射表,hash特别适合用来储存对象如:

keyvalue
userfieldvalue
id1
name张三
age20

存储格式

第一种

​ key value

​ user: {id:1,name:jack,age:20} 修改太麻烦(不推荐)

第二种

​ key value

​ user :id 1

​ user :name jack

​ user :age 20

第三种 hash

​ key value

​ field value

​ user id 1

		name   jack

​ age 20

存储数据(单个)
hset key_name field_name field_value //给key_name集合中的 field键赋值
存储数据(多个)
hmset key_name field1_name value1 field2_name value2
取出数据
hget key_name field_name
取出key hash集合内所有的field值
hkeys key_name
取出key hash集合内所有的value值
hvals key_name  
查看key中的field是否存在
hexists key_name field_name
key 中的hash集合中field加值
hincrby key_name field_name n
key 中hash集合中的field值不存在的时候设置一个field值
hsetnx key_name field_name value
Zset(有序集合)

Redis有序集合zset与普通合集set非常相似,是一个没有重复元素的字符串集合

不同之处在于有序集合中的每一个成员都关联了一个评分,这个评分(score)被用来按照从低到高的方式

排序集合中的成员,集合的成员是唯一的,但是评分可以是重复的

数据结构

(1) hash(存储成员) field value

​ member_name score

(2)跳跃表(可以快速找到成员)

添加一个或多个成员
zadd key_name score1 value1 score2 value2 score3 value3... //score(评分)
输出范围中的值
zrange key_name start_index end_index [withscores] // 0 -1代表输出所有的值
//输出下标在start_index和end_index之间的元素,添加withscores则会将评分(score)一起输出

zrangebyscore key_name min_score max_score [withscore] [limit offset count]
//返回有序集合key中,所有score值介于min和max之间的(包括min或max)成员
//有序集合按score值从小到大的次序排列

zrevrangescore key_name min_score max_score [withscore] [limit offset count]
//返回有序集合key中,所有score值介于min和max之间的(包括min或max)成员
//有序集合按score值大到小 的次序排列
增加成员的值
zincrby key_name incre_number member_name 
删除成员
zrem key_name member_name
统计评分区间的成员个数
zcount key_name min_score max_score
查看成员在集合中的排名
zrank key_name member_name //返回索引(索引从0开始)
Redis 的配置文件

只支持bytes不支持bit

NETWORK
修改配置以网络连接(默认只能本地连接)
bind 127.0.0.1 -::1 (默认本地连接)
// 用#号注释掉即可允许远程连接

protected-mode yes //保护模式(只允许本机连接) 将yes改为no即可支持远程访问 

Port

默认6379

tcp-backlog

默认值511

backlog其实是一个连接队列,backlog队列的总和=未完成三次握手队列+已完成三次握手的队列

高并发环境下需要一个高backlog来避免慢客户端的连接问题

tcp-backlog 511
timeout
timeout 0 //客户端未操作指定时间后断开连接 默认值为0(永不过期) 单位为秒  
tcp-keepalive

检测客户端的tcp是否活着(操作) 默认每300秒检查一次

tcp-keepalive 300
GENERAL
允许后台启动(默认为no)
daemonize yes
Limits
设置最大的客户端连接数
maxclients 10000 //默认最大连接数10000 
设置最大的内存占用量
maxmemory <bytes>   //达到最大的内存占用数后根据maxmemory-policy规则进行操作
设置最大内存占用规则
maxmemory-policy 
Redis发布和订阅
订阅频道
SUBSCRIBE channel_name //   返回值为订阅人数
向频道发送信息
publish channel_name message
Redis6新数据类型
Bitmap

Bitmap本身不是一种数据类型,它实际上就是字符串(key-value)

但是它可以对字符串进行位操作

通过jedis操作Redis
Jedis jedis = new Jedis(192.168.44.168,6379)
    
jedis.set()
jedis.get()
jedis.setex()
...... 
将Redis整合到springboot

具体方法上网百度,大概就是在application.properties中创建redis配置

在java文件夹中创建redis的配置类

使用
//controller
@Autuired
private RedisTemplate redisTemplate

@GetMapping
public String testRedis(){
    //设置值到redis
    redisTemplate.opsForValue().set("name","lucy")
    //从redis中获取值
    String name = (String)redisTemplate.opsForValue().get("name")
    return name;
}
Redis中的事务操作
Multi、Exec、discard

从输入Multi命令开始,输入的命令一次进入命令队列,但不会执行,知道输入Exec后,Redis会将之前的命令队列中的命令依次执行

组队的时候可以通过discard来放弃组队

--redis客户端
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)>set key1 value1
QUEUED
127.0.0.1:6379(TX)>set key2 value2
QUEUED
127.0.0.1:6379(TX)>exec
1) OK
2) OK
127.0.0.1:6379>


127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)>set key1 value1
QUEUED
127.0.0.1:6379(TX)>set key2 value2
QUEUED
127.0.0.1:6379(TX)>discard
OK
127.0.0.1:6379>
事务的错误处理
提交错误

使用exec语句提交时,命令队列无法执行

--redis客户端
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)>set key1 value1
QUEUED
127.0.0.1:6379(TX)>set key2 //语句有语法错误
(error) ERR wrong number of arguments for 'set' command
127.0.0.1:6379(TX)>exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379>
执行错误

能正确执行exec提交,但是命令队列中错误的语句报错

--redis客户端
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)>set key1 value1
QUEUED
127.0.0.1:6379(TX)>incr key1 //此时语句语法正确 但是逻辑不正确
QUEUED
127.0.0.1:6379(TX)>set key2 value2
QUEUED
127.0.0.1:6379(TX)>exec
1) OK
2) (erro)ERR value is not an integer or out of range
3) OK
127.0.0.1:6379>
事务冲突问题
悲观🔒

每次拿数据的时候都认为其他事务会修改数据,所以每次拿数据的时候都会上🔒

这样别的事务想拿这个数据就会被block(阻塞)直到它拿到🔒

乐观🔒

在数据上添加版本号,当有事务对数据成功进行更新后同步更新版本号,同时间在进行的事务会将开始事务时

获得的版本号与现在的版本号进行对比,若版本号更新则更新数据后再进行事务的操作

在执行multi之前,先执行watch指令 ,可以监视一个(或多个)key,如果在事务执行之前这个(或这些)key被其他命令所改动

那么事务将会被打断(返回nil)

watch key1 [key2]
事务的三特性
单独隔离操作

​ 事务中的所有命令都会序列化、按顺序的执行.事务在执行的过程中,不会

​ 被其他客户端发送来的命令请求所打断

没有隔离级别

​ 队列中的命令么有提交之前都不会实际被执行,因为事务提交前任何指令都

​ 不会被实际执行

不保证原子性

​ 事务中有一条命令执行失败,其后命令仍然会被执行,没有回滚

并发
测试工具

ab模拟测试

安装:

yum install httpd-tools
通过浏览器测试
ab -n 1000 -c 100 -p ~/postfile -T application/x-www-form-urlencoded http://192.168.137.1:8080/seckill/doseckil 
//ab -n 1000 -c 100 代表1000个请求中有100个是并发操作 
//-p ~/postfile 代表此目录下的postfile文件
// -p的意思是提交类型为POST ,-T的意思是 content-type的类型
//http://192.168.137.1:8080/seckill 本地的cotroller方法的路径
并发出现的问题
出现秒杀后商品存量为负值(超买超卖)
--乐观锁解决
//监视库存
jedis.watch(kcKey)

//使用事务
Transaction multi = jedis.multi();
//组队操作
multi.decr(kcKey) //kcKey 库存key
multi.sadd(userKey,uid) //秒杀成功的用户uid

并发量太大出现连接超时问题
--使用jedis连接池解决


库存遗留问题(秒杀结束,但是商品未被抢完)
LUA脚本
将复杂的或者多步的redis操作,写为一个脚本,一次提交给redis执行,减少反复连接redis的次数,性能
利用LUA脚本淘汰用户,解决超卖问题
(实际上是redis利用其单线程的特性,用任务队列的方式解决多任务并发问题)


Redis中的持久化操作

Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中

待持久化过程结束了,再用这个临时文件取替换上次持久化好的文件.整个过程中,主进程

是不进行任何IO操作的,这就确保了极高的性能,如果需要进行大规模数据的恢复,且对数据恢复

完整性并不是很敏感,那RDB方式要比AOF方式更加高效.RDB的缺点是最后一次持久化后

数据可能会丢失

dump.rdb

在redis.conf 中的配置文件,默认为dump.rdb

dbfilename dump.rdb

在指定目录生成rdb文件(默认在启动目录生成文件)

dir ./

save

save time num //在time时间(秒)内至少有num个key被改变时执行数据持久化操作

redis无法写入硬盘时停止写入(默认no)

stop-writes-on-bgsave-error yes

对存入到磁盘中的快照是否进行压缩

rdbcompression yes //使用LZF算法进行压缩 但是会消耗CPU性能

检查快照完整性

rdbchecksum yes //开启会有大概10%的数据损耗
RDB(Redis DataBase)

在指定时间间隔内将内存中的数据集快照写入磁盘

AOF(Append only File)

以日志的形式来记录每个写操作(增量保存),将Redis执行过的所有写指令记录下来(读操作不记录)

之追加文件但不改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据

日志文件的内容将写指令从前到后执行一次完成数据的恢复工作

AOF开启

AOF和RDB同时开启后,系统默认取AOF数据(数据不会存在丢失)

在redis.conf中修改

appendonly no // 默认为no 启动为yes
生成路径
跟RDB生成路径相同
异常恢复

如果遇到AOF文件损坏,通过/usr/loacl!/bin/redis-check-aof–fix appendonly.aof 来进行修复

redis-check-aof --fix appendonly.aof
AOF配置

AOF同步频率设置

appendfsync always
//始终同步,每次Redis的写入都会立刻记入日志;性能较差但是数据完整性较好

appendfsync everysec
//每秒同步,每秒记入日志一次,如果宕机,本秒的数据可能丢失 
appendfsync no
redis不主动进行同步,把同步时机交给操作系统
Rewrite压缩

AOF采用文件追加方式,文件会越来越大,为避免出现此种情况,新增了重写机制

当AOF文件超过所设定的阈值时,Redis就会采用AOF文件压缩,只保留可以恢复

数据的最小指令集.可以使用命令bgrewriteaof

auto-aof-rewrite-min-size:设置重写基准值,最小文件为64位.达到这个值后开始重写
重写后达到前一次重写大小的200%后再次重写

系统载入或者上次重写完毕时,Redis会记录此时AOF大小,设为base_size

如果Redis的AOF当前大小>= base_size + base_size*100% 且当前大小>=64mb(默认)的情况下,Redis会对AOF进行重写

持久化操作总结

官方推荐两个都启用

如果对数据不敏感,可以单独用RDB

不建议单独用AOF ,因为会出现bug

如果只是做纯内存缓存,可以都不用

Redis的主从复制

主服务器进行写操作,从服务器只能进行读操作

  • 读写分离,性能拓展
  • 容灾的快速恢复

在这里插入图片描述

  1. 当从服务器连接到主服务器后,从服务器向主服务器发送进行数据同步的消息

  2. 主服务器收到消息后对数据进行持久化操作,生成rdb文件,再将rdb文件发送给

    从服务器,从服务器拿到rdb文件后进行读取

  3. 每次主服务器进行写操作之后,就会和服务器进行数据同步

配置
查看主机信息
info replication
在从机上设置主机
slaveof <主机ip> <主机端口号> 
//从服务器挂掉后重启并不能自动连接之前的主服务器,而是恢复成默认(将自己认为是主服务器),必须重新设置
//主服务器挂了后重启还是主服务器,他的从服务器仍不会"篡位"
薪火相传

在这里插入图片描述

反客为主

大哥挂了小弟立马上位(主服务器挂了,从服务器变成主服务器)

slaveof no one  //将从机设置为主机 必须我们在从机手动设置 
哨兵模式(反客为主自动版)

反客为主自动版,能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库

规则:

  1. 优先级:值越小优先级越高
  2. 偏移量:获得原主机数据最全的
  3. runid:每个redis实例启动后都会生成一个随机的runid
--在自定义的/myredis目录下新建sentinel.conf文件,名字绝对不能错

--在文件中配置哨兵,填写内容
sentinel monitor mymaster 127.0.0.1 6379 1 //  mymaster是为监控对象起的服务器名称,1为至少有多少个哨兵同意迁移的数量



启动
redis-sentinel /myredis/sentinel.conf

从服务器的优先级在redis.conf中默认
slave-priority 100 --值越小优先度越高  
Redis集群

容量不够,redis如何进行扩容?

并发操作,redis如何分摊?

解决方法:无中心化集群

代理服务器模式

在这里插入图片描述

无中心化集群模式

在这里插入图片描述

无中心化集群配置
//redis.conf中进行配置

cluster-enbled yes --打开集群模式

cluster-config-file nodes-6379.conf --设定节点配置文件名,自定义

cluster-node-timeout 15000 设定节点失联时间,超过该时间(毫秒),集群自动进行主从切换 
合体

进入redis的主目录下的src目录

redis-cli --cluster create --cluster-replicas 1 192.168.11.101:6379 192.168.11.101:6380 192.168.11.101:6381 192.168.11.101:6389 192.168.11.101:6390 192.168.11.101:6391 

--replicacs 1 采用最简单的方式配集群,一台主机,一台从机正好三组


以集群的方式连接Redis

redis-cli -c -p 6379 // -c是以集群的策略连接Redis  -p是连接到6379端口(从服务器)
这时候在从服务器中执行写操作,Redis集群会自动切换到集群中的写服务器(主服务器 )

查看集群中的服务器信息

cluster nodes
集群如何分配系节点

一个集群至少要有三个主节点

选项–cluster-reolicas 1表示我们希望为集群中的每一个主节点创建一个从节点

插槽(slot)

一个Redis集群中包含16384个插槽,数据库中的每个键都属于这16384个插槽的其中一个

集群使用CRC16(key)%16384来计算键key属于哪个槽,其中CRC16(key)语句用于计算键

key的CRC16的校验和

集群中的每个节点负责处理一部分插槽.举个例子,如果一个集群可以有主节点,其中:

节点A负责处理0号至5460号插槽

节点B负责处理5461号至10922号插槽

节点C负责处理10923号至16384号插槽

在集群中设置多个值

> mset name lucy age 20 address china
>(error) CROSSSLOT Keys in request don't hash to the same slot
> mset name{user} lucy age{user} 20 address(user) china //为这些值设置一个共同的组

查询集群中的值

cluster keyslot key_name
返回值为插槽值

故障恢复

//redis.conf

cluster-require-full-coverage yes/no --yes(如果某段插槽的主从服务器全部挂掉,那么整个集群都挂掉)
									 --no(如果某一段插槽的主从都挂掉,那么该插槽数据全部不能使用,也无法存储)
集群的Jedis开发
HostAndPort hostAndPort =  new HostAndPort("集群中任意服务器的IP地址",集群中任意服务器的端口号)
JedisCluster jediscluster = new Jediscluster(hostAndPort)
jedisCluster.set("b1","value1") //设置值
String value = jedisCluster.get("b1") //取值
jedisCluster.close()
Redis应用问题的解决
缓存穿透
现象
  1. 应用服务器压力突然变大
  2. redis命中率降低
  3. 一直查询数据库

缓存中查询不到数据,redis一直查询数据库,导致数据库独自承压,最终导致数据库崩溃

  1. redis查询不到数据库
  2. 出现很多非正常的url访问
解决方案
1.对空值进行缓存
2.设置可访问的名单(白名单)
3.采用布隆过滤器
缓存击穿
现象
  1. 数据库访问压力瞬时增大

  2. redis中并没有大量key过期

  3. redis正常运行

  4. redis某个key过期了,但是有大量的访问使用这个key

解决方案
1.预先设置热门数据:在redis高峰访问之前,把一些热门数据提前存入到redis里面,加大热门数据key时长
2.实时调整:现场监控哪些数据热门,实时调整key的过期时长
3.使用锁
缓存雪崩
现象
  1. 在极少的时间段,查询大量key的集中过期
解决方法
1.构建多级缓存框架:nginx缓存+redis缓存+其他缓存(ehcache等)
2.使用锁或者队列
3.设置过期标志更新缓存(记录缓存数据是否过期(设置为提前量),如果过期会触发通知另外的线程去更新实际的key的缓存)
4.将缓存失效的时间分散开:通过随机数设置过期时间
分布式锁(共享锁)

单传的java API 并不能提供分布式锁的能力.为了解决这个问题就需要一种跨

JVM的互斥机制来控制共享资源的访问

分布式锁的主流实现方法:

  1. 基于数据库实现分布式锁
  2. 基于缓存(redis等)
  3. 基于Zookeeper

每一种分布式锁解决方案都有各自的优缺点:

  1. 性能:redis最高
  2. 可靠性;Zookeeper最高
基于redis实现分布式锁
--设置值的时候上锁
 setnx key_name value 
 
--解锁
del key_name (删除key时解锁)

 --上锁的时候同时设置过期时间(时间过期后锁自动解除)
 set key_name value nx ex 12 (nx代表锁,ex后接过期时间(s ))
Redis6的新功能
ACL(访问控制列表)

将用户的权限进行更细粒度的权限控制

(1),接入权限:用户名,密码

(2)用户可执行的命令

(3)用户可以操作的key

命令
acl list //展现当前所有用户的信息
acl cat  //查看具体操作的指令列表 
acl setuser user_name //添加用户以及权限  
acl whoami //查看当前用户名
IO多线程
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值