redis 学习总结

本贴最后更新于 679 天前,其中的信息可能已经沧海桑田

redis 学习总结

数据类型

通用命令

del key  删除指定key的value
keys * 查看数据库中所有的key
exists key 判断key是否存在
type key获取key的类型
expire key seconds/milliseconds 为指定key设置有效期
ttl key  获取key剩余的时效  返回3种结果,一是在有效期返回有效时长,而是不在有效期返回-2,三是既没有有效期又存在返回-1

persist key  设置key的时效为永久
rename key newkey 为key进行重命名
Sort key 对集合类型排序
select index 切换数据库
move key db 移动数据到指定的库
dbsize(查看库中的key总数)
flushdb(删除当前库的数据)
flushall(删除所有库的数据)

ps -ef | grep redis    查看redis进程

启动redis命令:进入到redis-server存在的目录执行./redis-server /usr/local/redis/conf/redis-6379.conf   表示后台启动,以配置文件的方式

cat redis.conf | grep -v "#" | grep -v "^$" > redis-6379.conf  表示复制redis.conf文件到当前目录下,并且去除注释和换行

./redis-cli -p 6379 :指定连接端口号的redis服务器

kill -9 进程号   表示杀死进程

slaveof 真机的ip 端口:表示在从机下连接主机(搭建集群)

启动redis并连接主机:进入到redis-server存在的目录执行./redis-server /usr/local/redis/conf/redis-6379.conf --slaveof 主机ip  主机端口
sed "s/6379/6380/g" redis-jiqun-6379.conf > redis-jiqun-6380.conf:表示复制文件并将文件中的6379替换为6380

String 数据类型

set key value:添加一组键值对
get key :根据key获取value
strlen key :统计该key对应的value的字符个数
append key value :表示给存在的key的value追加数据,注意当key不存在会新增key
mset key1 value1 key2 value2...:批量添加多个键值对
mget key1 key2 key3...:一次获取多个key的值,建议使用批量操作,效率更高
incr key  	:增量1,注意key的value不能是字母类型,可以是数字类型
decr key   :减量1
incrby key 值	:增加指定的整数值
decrby key 值	:减少指定的整数值
incrbyfloat key 值	:表示增加指定的小数值
decrbyfloat key 值	:表示减少指定的小数值
set EXPIRE key     :EXPIRE 将key的生存时间设置为ttl秒
set PEXPIRE key	:PEXPIRE 将key的生成时间设置为ttl毫秒
set EXPIREAT key	:EXPIREAT 将key的过期时间设置为timestamp所代表的的秒数的时间戳
set PEXPIREAT key	:PEXPIREAT 将key的过期时间设置为timestamp所代表的的毫秒数的时间戳

注意 incr 和 decr 的数据值有上限 Long.MAX_VALUE 9223372036854775807 超过上限报错

应用场景
海选投票,设计主播各大平台的粉丝数量自动更新问题

hash 数据类型

因为 string 类型存储对象数据增删改不太灵活,所以就有了 hash 类型,针对 hash 类型来讲 key 都是字符串类型,value 就是个 hash 类型(小 key+value)

所以在 redis 中 hash 底层采用的是哈希表实现

哈希表的实现(数组 + 拉链法 | 数组 + 寻址法)

hset key field value	:添加数据(hset user username zhangsan)key和filed存在就覆盖
hget key field	:获取数据
hgetall key	:获取所有的field以及对应的value值
hdel key field1 field...	:删除某个field以及value
hmset key field1 value1 field2 value2...	:添加或者修改多个数据
hmget key field1 field2...	:获取多个数据
heln key 	:获取hash中value的键值对个数
hexists key field   :判断hash中是否存在某个field
hkeys key	:获取hash中所有的field
hvals key		:获取hash中所有的value																				
hincrby key field  值	:增加指定的值
hincrbyfloat key filed 值	:增加指定的小数值
hdecrby key field  值	:减少指定的值
hdecrbyfloat key filed 值		:减少指定的小数值
hsetnx key field value	:如果key中的field存在就什么都不做,否则添加新的field以及对应的value

注意事项:key 和 field 不可重复,但是 value 可以重复
应用场景

购物车设计,充值券抢购

注意:String 和 hash 的区别?

string 应该的是数据的整体性,追求的是读为主,而 hash 使用了 field 把属性隔离开,追求的是更新操作更加灵活,所以修改较多使用 hash,只是存储使用 string

list 数据类型

对于 Redis 中 List 数据类型底层的实现原理是链表(双向)

lpush key value1 value2...	:从左边推入元素(list结构中可以允许添加重复元素)
rpush key value1 value2...	:从右边推入元素
lrange key start end:从key中取元素,从start开始,end结束
lindex key index:根据索引找key对应value的元素
llen key	:获取list中所有的元素个数
lpop key	:从左边删除(弹出)元素
rpop key	:从右边删除(弹出)元素
blpop key timeout	:从key中左边弹出元素,有就弹出,没有等待指定时间,超时还是没有获取不到
brpop key timeout	:从key中右边弹出元素,有就弹出,没有等待指定时间,超时还是没有获取不到

应用场景

朋友圈点赞记录

set 数据类型

1.新的存储需求:存储大量的数据,在查询方面提供更高的效率(list 很鸡肋的是结构是链表)

2.需要的存储结构:能够保存大量的数据,高效的内部存储机制,便于查询

3.set 类型:与 hash 存储结构完全相同,仅存储键,不存储值(nil),并且值式不允许重复的

链表增删效率比较低,set 跟 hash 结构完全相同,不相同的地方在于键不允许重复,value 也不允许重复,且无序

sadd key value1 value2...:添加元素
smembers key	:获取所有元素
srem key value...:删除元素
scard key	:获取set中所有元素的个数
sismember key value:判断集合中是否包含某个元素
spop key:从key中随机取出一个value
sinter key1 key2...:求交集(取出集合重合的内容)
sunion key2 key2...	:求并集(相同的只合并一次)
sdiff key1 key2...:求差集(谁在前就保留谁的数据)
sinterstore  key3 key1 key2...	:表示将后面的交集数据存到key3集合中
sunionstore  key3 key1 key2...:表示将后面的并集数据存到key3集合中
sdiffstore  key3 key1 key2...	:表示将后面的差集集数据存到key3集合中
smove key3 key value	:表示将key集合中的value移动到key3集合中

应用场景

热点推荐,权限控制,统计网站的 PV(网站访问量)UV(活跃用户)IP( 独立访问的 ip)等,黑白名单问题

sorted_set 数据类型

sorted_set 底层存储还是基于 set 结构的,因此数据不能重复,如果重复添加相同的数据,score 值将被反复覆盖,保留最后一次修改的结果

zadd key score1 memeber1...	:存储数据(zadd scores 94 qiangqiang)
zrange key start end:取出元素(从低到高)zrange scores 0 -1
zrevrange scores start end:取出元素(从高到低)zrevrange scores 0 -1
后面加上withscores可以看到详细信息,即显示排名又显示数据
zrem key memeber	:删除某个元素
zscore key member	:获取某个member的数据
zincrby key increment member:对某个member的score进行增量或者减量(zincrby movies 1 aa)
zrangebyscore key min max withscores	:按照条件获取数据(min和max表示score范围)后面还可以跟分页
zremrangebyrank key start stop:按照条件进行删除(zremrangebyrank  scores 0  -1)
zinterstore destination numkeys key1 key2…:交集(注意会进行求和操作)
zunionstore destination numkeys key1 key2…:并集 
zinterstore s5 3 s1 s2 s3 aggregate max:在取出并集的时候取出最大的值存到s5

应用场景

热门排行的数据获取,基于一些会员临时体验 或者会员到期提醒,权重记录

redis 持久化

redis 作为缓存,所有数据真正保存到 mysql,为什么 redis 需要持久化?

解释:redis 中存储了大量的数据,如果我们的 redis 突然宕机,客户端发起的请求都会去冲击我们的 mysql,很容易造成 mysql 宕机,做了 redis 持久化,在宕机之后,重启会读取磁盘中的数据保证不用查询数据库,当然在 redis 读取大量的数据恢复-> 需要缓存预热

持久化方式--RDB(快照,持久化的内容是数据)

RDB 启动的方式

1、save 指令(在每次操作后执行 save 命令,不建议使用 save 指令,底层是将每个命令放到队列中依次执行,会造成长时间线程阻塞)

2、bgsave(后台进程启动,会将 bgsave 前面的数据进行持久化,发送 bgsave 后会发送指令调用 fork 函数生成子进程,创建 rdb 文件进行持久化)

3、redis.conf 配置文件中进行配置 save second changes (指定时间内多少个 key 发生变化就持久化)底层还是 bgsave,持久化的时候可以开启 LZF 压缩以及数据校验,但是校验可能存在 10% 时间消耗

RDB 常见的持久化常见:比如数据容灾备份,比如主从复制(全量复制)

RDB 优缺点

1.RDB 优点

RDB 是一个紧凑压缩的二进制文件,存储效率较高

RDB 内部存储的是 redis 在某个时间点的数据快照,非常适合用于数据备份,全量复制等场景

RDB 恢复数据的速度要比 AOF 快很多

应用:服务器中每 X 小时执行 bgsave 备份,并将 RDB 文件拷贝到远程机器中,用于灾难恢复

2.RDB 缺点

RDB 方式无论是执行指令还是利用配置,无法做到实时持久化,具体较大的可能性丢失数据

bgsave 指令每次运行要执行 fork 操作创建子进程,要牺牲掉一些性能

Redis 的众多版本中未进行 RDB 文件格式的版本统一,有可能出现个版本服务之间数据格式无法兼容现象(比如 3.x 和 4.x 的版本)

也有可能在在开启子就进行持久化,然后设置了新数据,设置完了之后服务器宕机了

持久化方式--AOF(持久化的内容是指令)

appendonly yes|no 是否开启 AOF 持久化

appendfsync always|everysec | no 持久化的策略

持久化原理,是当满足策略之后,会使用父进程将数据刷到缓冲区,然后父进行会调用 fork 函数开启子进程将缓冲区的数据持久化到磁盘的 aof 文件

AOF 重写概念: 为了节省 AOF 文件空间,不会持久化一些没必要的操作命令,例如对 key 的重复操作,简单说就是将同样一个数据的若干个命令执行结果转换为最终结果数据对应的指令进行记录,AOF 重写是子进程在缓冲区完成的

AOF 为什么要进行重写?

解释:因为当我们进行重复的操作就没有必要进行持久化,多余的操作不需要持久化

AOF 重写方式:(重写主要是重写缓冲区的数据)

1.手动重写 Bgrewriteaof (类似 rdb 执行一条指令就重写)

2.自动重写 auto-aof-rewrite-min-size size, auto-aof-rewrite-percentage percentage

AOF 触发重写机制

Redis 会记录上次重写时的 AOF 大小,默认配置是当 AOF 文件大小是上次 rewrite 后大小的一倍且文件大于 64M 时触发

重写虽然可以节约大量磁盘空间,减少恢复时间。但是每次重写还是有一定的负担的,因此设定 Redis 要满足一定条件才会进行重写。

auto-aof-rewrite-percentage :设置重写的基准值,文件达到 100% 时开始重写(文件是原来重写后文件的 2 倍时触发)

auto-aof-rewrite-min-size:设置重写的基准值,最小文件 64MB。达到这个值开始重写

例如 ∶ 文件达到 70MB 开始重写,降到 50MB,下次什么时候开始重写?100MB

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

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

AOF 重写的作用:降低磁盘占用量,提高磁盘利用率(多余的命令没有了)

提高持久化效率,提高 IO 性能(降低子进程的工作量)

降低数据恢复时间,提高数据恢复效率(数据量变小了)

AOF 重写规则:

1.进程内已超时的数据不再写入文件

2.忽略无效指令,重写时使用进程内数据直接生成,这样新的 AOF 文件只保留最终数据的写入命令,如 del key1,hdel key2,srem key3,set key 222 等

3.对统一数据的多条命令合并为一条命令

如 lpush list1 a ,lpush list1 b,lpush list1 c 可以转化为 lpush list1 a b c

4.为防止数据量过大造成客户端缓冲区溢出,对 list,set,hash,set 等类型,每条指令最多写入 64 个元素

AOF 重写的弊端

aof 重写的弊端就是如果有海量数据需要进行持久化子进程在进行持久化的时候在进行 I0 操作,非常消耗 cpu 性能(redis 为了解决这个问题采用管道)

Redis 之管道优化 AOF 重写:为了提高 aofrewrite 效率,redis 通过在父子进程间建立管道,把 aofrewrite 期间的写命令通过管道同步给子进程,Redis 管道技术可以在服务器未响应时,客户端可以继续向服务器发送请求,并最终一次性获取所有服务器的响应,大大提高了 AOF 重写的效率。如果不使用管道,当我们的缓冲区调用子进程写数据的同时线程处于阻塞状态,就不能在写数据的同时发送请求

Redis4.0 开始支持混合持久化,Redis5.0 开始默认就开启了混合持久化

混合持久化的开启 aof-use-rdb-preamble yes

注意混合持久化开启之后,文件是 AOF 的,但是这个文件开头是 RDB 后面则是 AOF 的内容,只要在 AOF 的文件中看到 REDIS 表示就是混合持久化

两种持久化选择

占用存储空间:RDB 占用相对较小,AOF 相对较大

存储速度:RDB 存数据很慢,AOF 存指令很快

恢复速度:RDB 直接恢复数据所以恢复速度快,AOF 慢

数据安全性:RDB 会丢失数据,AOF 依据策略决定

资源消耗:RDB 持久化数据所以高,AOF 低

启动优先级:AOF 高于 RDB

redis 事务

redis 做事务太拉胯了,实际开发中不使用 redis 实现事务控制

事务的三个命令:multi 类似 mysql 中开启事务,开启后将各种命令放入队列中,输入 exec(类似 mysql 中提交事务)开始执行队列中的命令,如果在此期间某个命令输入错误,可以输入 discard(类似 mysql 中的回滚事务)放弃组队,也就是队列中的命令不执行

redis 事务执行流程:服务器在接收命令之前会判断是否为事务状态,① 如果不是事务状态,那么当监测到有 MULTI 命令写入就开启事务,并且创建队列,将之后的命令存入到队列中,当检测到有 EXEC 命令时,就开始执行队列中的命令,如果存入到队列中的命令有误则不会执行队列中的命令,如果检测到有 DISCARD 命令将销毁队列。② 如果是事务状态就直接监听 EXEC 和 DISCARD 命令,就不用创建队列

使用锁解决 redis 事务问题

watch key1 key2... 对 key 添加监视锁,如果在执行 exec 前 key 发生变化那么终止事务执行

unwatch key 取消对 key 的监视

使用分布式锁解决 redis 事务问题

setnx lock-key value 使用 setnx 设置一个公共锁(setnx 判断某个 key 是否存在)如果返回 1 表示获取锁成功,0 表示获取锁失败,有别的客户端在使用锁

del lock-key 操作完毕通过 del 释放锁

为了避免死锁(持有锁的客户端宕机了),我们需要预计业务时长设置时效,也就是给 key 设置时效,如果网络延迟导致业务还没执行完,需要对锁(key)进行续期,例如每隔 10 秒去检测业务是否执行完,没完就重新执行 setnx lock-key

相当于在一个业务中加了同一把锁,就是 java 中重入锁的概念

redis 内存问题

删除策略解决 redis 内存上限问题

数据删除策略(只针对过期数据)

1、定时删除:用处理器性能换区存储空间,也就是时间换空间,redis 启动服务会读取 conf 文件中的 server.hz 的值,默认是 10,表示每秒执行 10 次

优点:没有内存的压力

缺点:如果此时 cpu 处于繁忙,然后取删除海量数据会拉低 redis 性能

2、惰性删除:原理是调用函数(expirelfNeeded())实现,数据达到过期时间不做处理,等到下次访问该数据,如果发现已经过期就删除数据,存储空间换取处理器性能,空间换时间

优点:节约 cpu 性能

缺点:内存压力很大

3、定期删除(重点)

redis 会将 0-16 号数据库分为 8 个分区,current_db 记录当前在那个分区,redis 启动时会读取 conf 文件中的 server.hz 的值,默认是 10,表示每秒进行 10 次 activeExpireCycle()函数,也就是 10 次检测,值越大检测时间就越短,这个函数会对每一个分区进行逐一检测,在检测第一个分区时,会随机挑选 W 个 key 进行检测是否过期,过期就删除,如果删除的 key 的数量大于 W 的 1/4,循环此过程,直到某一轮删除的 key 数量小于等于 W 的 1/4,根据 current_db 的值进入下一个分区继续检测

数据逐出策略(数据没有过期,就是内存满了)

策略 1:检测某一个库

volatile-lru:挑选最近最少使用的数据淘汰

volatile-lfu:挑选最近使用次数最少的数据淘汰

volatile-ttl:挑选将要过期的数据淘汰

volatile-random:任意选择数据淘汰

策略 2:检测全部库

allkeys-lru:挑选最近最少使用的数据淘汰

allkeys-lfu:挑选最近使用次数最少的数据淘汰

allkeys-random:任意选择数据淘汰

建议使用 volatile-lru 策略

redis 集群(多台机器,从机连接主机)

第一种配置方式:主从复制

① 在从机中使用(slaveof 主机 ip 端口)连接主机

② 在启动从机时后面跟上参数(--slaveof 主机 ip 端口)连接主机

③ 在从机启动的配置文件中添加一条配置(slaveof 主机 ip 端口)连接主机 ,可以使用 slaveof no one 命令断开与主机的连接,也可以在主机配置文件中配置(requirepass 密码)连接的密码,从机连接就需要在配置文件中配置(masterauth 密码)

主从复制数据同步的原理:当我们的从机连接主机时会发送请求数据同步指令,连接成功主机会调用 fork 子进程创建缓冲区,执行 bgsave 进行持久化,生成 RDB 文件通过 socket 发送给 slave,从机接收到 RDB,先清空本地数据,然后执行 RDB 文件,这是主从第一次数据同步,也就是全量复制,数据同步结束之后从机会发送命令已经恢复完成,此时如果主机继续向缓冲区刷新数据,这时从机在恢复数据时采用 AOF 重写,所以这个时候数据同步方式是部分复制

第二种配置方式:哨兵模式

哨兵工作原理:哨兵的工作就是主从切换

主从切换经历的阶段

1、监控:监控主、从、和其他哨兵,哨兵与哨兵之间通过 ping 命令进行互相监控,哨兵与 master 和 slave 是通过 info 信息进行监控

2、通知:哨兵与 redis 节点之间通过发布与订阅进行通知

3、故障转移:哨兵发现主机宕机了,会通知其他哨兵,其他哨兵会确认主机是否真的下线了,如果确认主机确实下线了,然后此时各个哨兵之间会投票选一个哨兵作为老大,通过选出的老大去观察各个 slave 哪个性能比较好(在线并且响应快,与原 master 断开时间久的,断开时间越久说明最先发现 master 宕机),投票选择出性能好的 slave 作为新的 master, 然后向新的 master 发送 slaveof no one,并告诉其他 slave 新的 master 的 ip 和端口, 其他 slave 会去连接新的 master 主机

第三种配置方式:集群模式

QPS:表示每秒请求数

当我们的并发量变得更高,可以使用集群模式搭建,不使用主从复制搭建一个主,而是采用集群方式多个主进行搭建

集群就是使用网络将若干个计算机进行连接,并提供统一的管理方式,使其对外呈现单机的服务效果,就好像 Nginx+tomcat 集群

集群的作用

1、分散单台服务器的访问压力,实现负载均衡

2、分散单台服务器的存储压力,实现可扩展性

3、降低单台服务器宕机带来的业务灾难

集群的数据结构设计

1、通过算法计算出 key 应该保存的位置,调用 redis 的函数( CRC16(key)%16384 )计算 key 的存储位置

2、将所有的存储空间计划切割为 16384 份,每台主机保存一部分

3、每份代表的是一个存储空间也就是 slot 槽,如果有新的节点(主机)添加进来,这时其他节点会分出自己的一部分给新的节点,使其让新的节点具有与其他节点相同大小的空间,同理,有一台节点下线了,会将空间均匀分散到其他节点,那么此时有个问题是槽换了位置不知道要存在哪个空间,这时候集群内部通讯设计是每个存储空间中都会独立存储一块记录各节点的存储范围,好比目录一样,这样去寻找 key 到底存在哪个槽就只需要找两次,先随意找一块槽,然后根据计算的存储位置到目录中找。

4、将 key 按照计算出的结果放到对应的存储空间

注意集群方式搭建 redis 集群,如果主机宕机了,小弟并不会上位,而是继续当大哥,等 master 重新上线之后 master 还是大哥

redis 发布与订阅

redis 发布与订阅是通过通道实现的,例如一个客户端发布消息到通道,另外几个客户端去订阅,必须名称一样,否则不能订阅

发布:pulish 发布的通道 发布的消息

订阅:subscribe 发布的通道 (客户端会一直处于阻塞状态,有消息发布就会结束阻塞)

redis 其他三种高级数据类型

GEO(和地图有关,不建议使用,直接调用第三方接口就 ok)

hyperLoLog

bitmaps(位图):将一个数据转换为二进制存储起来,位图可以理解为一个普通的字符串

set str a -》97 -》 0100 1000

存储字符串是将字符串二进制数组的形式存储在 redis 中,位图可以直接对二进制的数组操作,位图的优势在于可以用 О 和 1 来存储布尔值,这大大降低了我们的存储空间消耗。由于这个特性,我们用位图来记录签到信息,记录活跃用户等,可以达到节省空间的能力

基本存取: setbit | getbit

  • Redis

    Redis 是一个开源的使用 ANSI C 语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API。从 2010 年 3 月 15 日起,Redis 的开发工作由 VMware 主持。从 2013 年 5 月开始,Redis 的开发由 Pivotal 赞助。

    284 引用 • 248 回帖 • 124 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...