Redis

Redis的优点

以下是 Redis 的一些优点:
异常快 - Redis 非常快,每秒可执行大约 110000 次的设置(SET)操作,每秒大约可执行 81000 次的读取/获取(GET)操作。
支持丰富的数据类型 - Redis 支持开发人员常用的大多数数据类型,例如列表,集合,排序集和散列等等。这使得 Redis 很容易被用来解决各种问题,因为我们知道哪些问题可以更好使用地哪些数据类型来处理解决。
操作具有原子性 - 所有 Redis 操作都是原子操作,这确保如果两个客户端并发访问,Redis 服务器能接收更新的值。
多实用工具 - Redis 是一个多实用工具,可用于多种用例,如:缓存,消息队列(Redis 本地支持发布/订阅),应用程序中的任何短期数据,例如,web应用程序中的会话,网页命中计数等。
缺点是存储的数据缺少结构化,MongoDB也是键值对但是value是结构化的。

Redis 五种基本数据结构

Redis 有 5 种基础数据结构,它们分别是:string(字符串)、list(列表)、hash(字典)、set(集合) 和 zset(有序集合)。这 5 种是 Redis 相关知识中最基础、最重要的部分。

1)字符串 string

Redis 中的字符串是一种 动态字符串,这意味着使用者可以修改,它的底层实现有点类似于 Java 中的 ArrayList,有一个字符数组。注:Redis 规定了字符串的长度不得超过 512 MB。

*设置和获取键值对

> SET key value
OK
> GET key
"value"

我们通常使用 SET 和 GET 来设置和获取字符串值。
值可以是任何种类的字符串(包括二进制数据),例如你可以在一个键下保存一张 .jpeg 图片,只需要注意不要超过 512 MB 的最大限度就好了。
当 key 存在时,SET 命令会覆盖掉你上一次设置的值

*使用 EXISTS 和 DEL 关键字来查询是否存在和删除键值对:

> EXISTS key
(integer) 1
> DEL key
(integer) 1
> GET key
(nil)

*批量设置键值对(MSET,MGET)

> SET key1 value1
OK
> SET key2 value2
OK
> MGET key1 key2 key3    # 返回一个列表
1) "value1"
2) "value2"
3) (nil)
> MSET key1 value1 key2 value2
> MGET key1 key2
1) "value1"
2) "value2"

*redis有四种命令可以用于设置键的生存时间和过期时间:

    EXPIRE <KEY> <TTL> : 将键的生存时间设为 ttl 秒
    PEXPIRE <KEY> <TTL> :将键的生存时间设为 ttl 毫秒
    EXPIREAT <KEY> <timestamp> :将键的过期时间设为 timestamp 所指定的秒数时间戳
    PEXPIREAT <KEY> <timestamp>: 将键的过期时间设为 timestamp 所指定的毫秒数时间戳.

*实现自增的INCR命令

将 key 中储存的数字值增一。

如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作。

如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。

redis> SET page_view 20
OK
redis> INCR page_view
(integer) 21
redis> GET page_view    # 数字值在 Redis 中以字符串的形式保存
"21"

*返回原值的 GETSET 命令

列表List

Redis 的列表相当于 Java 语言中的 LinkedList(双向链表),注意它是链表而不是数组。这意味着 list 的插入和删除操作非常快,时间复杂度为 O(1),但是索引定位很慢,时间复杂度为 O(n)。
*链表的基本操作
LPUSH 和 RPUSH 分别可以向 list 的左边(头部)和右边(尾部)添加一个新元素;
LRANGE 命令可以从 list 中取出一定范围的元素;
LINDEX 命令可以从 list 中取出指定下表的元素,相当于 Java 链表操作中的 get(int index) 操作;

> rpush mylist A
(integer) 1
> rpush mylist B
(integer) 2
> lpush mylist first
(integer) 3
> lrange mylist 0 -1    # -1 表示倒数第一个元素, 这里表示从第一个元素到最后一个元素,即所有
1) "first"
2) "A"
3) "B"

*从列表两端弹出元素
LPOP key
RPOP key
弹出后返回被弹出的元素,可配合rpush+lpop实现队列,rpush+rpop实现栈
*其他命令
llen:返回列表的元素个数
LRANGE KEY_NAME START END:返回列表区间(-1表示最后一元素,-2表示倒数第二个)

字典 hash

Redis 中的字典相当于 Java 中的 HashMap,内部实现也差不多类似,都是通过 “数组 + 链表” 的链地址法来解决部分 哈希冲突,同时这样的结构也吸收了两种不同数据结构的优点。

> HSET books java "think in java"    # 命令行的字符串如果包含空格则需要使用引号包裹
(integer) 1
> HSET books python "python cookbook"
(integer) 1
> HGETALL books    # key 和 value 间隔出现
1) "java"
2) "think in java"
3) "python"
4) "python cookbook"
> HGET books java
"think in java"
> HSET books java "head first java"  
(integer) 0        # 因为是更新操作,所以返回 0
> HMSET books java "effetive  java" python "learning python"    # 批量操作
OK
#判断某个字段是否存在
HEXISTS key field

集合SET

Redis 的集合相当于 Java 语言中的 HashSet,它内部的键值对是无序、唯一的。它的内部实现相当于一个特殊的字典,字典中所有的 value 都是一个值 NULL。

> SADD books java
(integer) 1
> SADD books java    # 重复
(integer) 0
> SADD books python golang
(integer) 2
> SMEMBERS books    # 注意顺序,set 是无序的
1) "java"
2) "python"
3) "golang"
> SISMEMBER books java    # 查询某个 value 是否存在,相当于 contains
(integer) 1
> SCARD books    # 获取长度
(integer) 3
> SPOP books     # 弹出一个
"java"

*SET的集合交并运算
差集:sdiff setA setB
交集:sinter setA setB
并集:sunion setA setB

有序列表 zset(sorted set)

这可能使 Redis 最具特色的一个数据结构了,它类似于 Java 中 SortedSet 和 HashMap 的结合体,一方面它是一个 set,保证了内部 value 的唯一性,另一方面它可以为每个 value 赋予一个 score 值,用来代表排序的权重。
在某些方面有序集合和列表类型有些相似。
1、二者都是有序的。
2、二者都可以获得某一范围的元素。
但是,二者有着很大区别:
1、列表类型是通过链表实现的,获取靠近两端的数据速度极快,而当元素增多后,访问中间数据的速度会变慢。
2、有序集合类型使用散列表实现,所有即使读取位于中间部分的数据也很快。
3、列表中不能简单的调整某个元素的位置,但是有序集合可以(通过更改分数实现)
4、有序集合要比列表类型更耗内存。

> ZADD books 9.0 "think in java"
> ZADD books 8.9 "java concurrency"
> ZADD books 8.6 "java cookbook"

> ZRANGE books 0 -1     # 按 score 排序列出,参数区间为排名范围
1) "java cookbook"
2) "java concurrency"
3) "think in java"

> ZREVRANGE books 0 -1  # 按 score 逆序列出,参数区间为排名范围
1) "think in java"
2) "java concurrency"
3) "java cookbook"

> ZCARD books           # 相当于 count()
(integer) 3

> ZSCORE books "java concurrency"   # 获取指定 value 的 score
"8.9000000000000004"                # 内部 score 使用 double 类型进行存储,所以存在小数点精度问题

> ZRANK books "java concurrency"    # 排名
(integer) 1

> ZRANGEBYSCORE books 0 8.91        # 根据分值区间遍历 zset
1) "java cookbook"
2) "java concurrency"

> ZRANGEBYSCORE books -inf 8.91 withscores  # 根据分值区间 (-∞, 8.91] 遍历 zset,同时返回分值。inf 代表 infinite,无穷大的意思。
1) "java cookbook"
2) "8.5999999999999996"
3) "java concurrency"
4) "8.9000000000000004"

> ZREM books "java concurrency"             # 删除 value
(integer) 1
> ZRANGE books 0 -1
1) "java cookbook"
2) "think in java"

持久化

Redis支持两种方式的持久化,一种是RDB方式,一种是AOF方式。可以单独使用其中一种或将二者结合使用。
RDB方式的持久化是通过快照(snapshotting)完成的,当符合一定条件时Redis会自动将内存中的数据进行快照并持久化到硬盘。
RDB是Redis默认采用的持久化方式,在redis.conf配置文件中默认有此下配置:
save 900 1
save 300 10
save 60 10000
save 开头的一行就是持久化配置,可以配置多个条件(每行配置一个条件),每个条件之间是“或”的关系,“save 900 1”表示15分钟(900秒钟)内至少1个键被更改则进行快照,“save 300 10”表示5分钟(300秒)内至少10个键被更改则进行快照。
问题总结:
通过RDB方式实现持久化,一旦Redis异常退出,就会丢失最后一次快照以后更改的所有数据。这就需要开发者根据具体的应用场合,通过组合设置自动快照条件的方式来将可能发生的数据损失控制在能够接受的范围。如果数据很重要以至于无法承受任何损失,则可以考虑使用AOF方式进行持久化。
默认情况下Redis没有开启AOF(append only file)方式的持久化,可以通过appendonly参数开启:
appendonly yes
开启AOF持久化后每执行一条会更改Redis中的数据的命令,Redis就会将该命令写入硬
盘中的AOF文件。AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的,默认的文件名是appendonly.aof,可以通过appendfilename参数修改:appendfilename appendonly.aof

Redis集群

一、主从复制模式

主从复制原理:
从服务器连接主服务器,发送SYNC命令;
主服务器接收到SYNC命名后,开始执行BGSAVE命令生成RDB文件并使用缓冲区记录此后执行的所有写命令;
主服务器BGSAVE执行完后,向所有从服务器发送快照文件,并在发送期间继续记录被执行的写命令;
从服务器收到快照文件后丢弃所有旧数据,载入收到的快照;
主服务器快照发送完毕后开始向从服务器发送缓冲区中的写命令;
从服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令;(从服务器初始化完成)
主服务器每执行一个写命令就会向从服务器发送相同的写命令,从服务器接收并执行收到的写命令(从服务器初始化完成后的操作)
主从复制的作用:
数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。
负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
高可用基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。
主从复制优缺点:

优点:

支持主从复制,主机会自动将数据同步到从机,可以进行读写分离
为了分载Master的读操作压力,Slave服务器可以为客户端提供只读操作的服务,写服务仍然必须由Master来完成
Slave同样可以接受其它Slaves的连接和同步请求,这样可以有效的分载Master的同步压力。
Master Server是以非阻塞的方式为Slaves提供服务。所以在Master-Slave同步期间,客户端仍然可以提交查询或修改请求。
Slave Server同样是以非阻塞的方式完成数据同步。在同步期间,如果有客户端提交查询请求,Redis则返回同步之前的数据
缺点:

Redis不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复。
主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性。
Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。
主从配置
基本只需要修改从服务器即可,从服务器默认只读,写的话也容易被覆盖

redis主服务器(master)一般不用怎么配置 ,端口用默认的6379就行, 但需要开放访问权限 。

1)找到redis配置文件
vim redis.conf;

2)注释bind 127.0.0.1 和 bind 127.0.0.1 ::1 
(bind ip 表示绑定ip才能访问这台redis,现在redis从服务器(slave)要访问并同步数据。注释掉说明所有ip都可以访问)
并且设置保护模式为no :protected-mode no ; 
如果考虑安全问题可以设置主服务器访问密码:masterauth <master-password> , 从服务器也要对应设置

3)重启redis服务:
./redis-cli shutdown 
./redis-server ../etc/redis.conf

4)测试能不能远程访问:
./redis-cli -h 192.168.82.31 -p 6379
# 进入redis说明配置成功,外部可以通过ip远程访问redis


slave从服务器配置也比较简单:

1)只需配置:slaveof <masterip> <masterport> 
vim redis.conf;
# 在slaveof的地方添加:
slaveof 192.168.82.31 6379

2)重启redis服务(重复上面步骤3即可)
3)进入redis从从服务器
./redis-cli
输入:info replication 可以查看redis主从信息。然后就可以在master测试有没数据同步过来

4)其他配置可以根据需要配:
daemonize yes , #是否在后台打开
slave-read-only yes , #从服务器是否只读 , (no表示可写,但很容易被master覆盖)

哨兵模式

基于主从复制模式
哨兵的作用就是监控Redis系统的运行状况。它的功能包括以下两个。

(1)监控主服务器和从服务器是否正常运行。 
(2)主服务器出现故障时自动将从服务器转换为主服务器。

1、它会将失效 Master 的其中一个 Slave 升级为新的 Master, 并让失效 Master 的其他Slave 改为复制新的 Master。
2、当客户端试图连接失效的 Master 时,集群也会向客户端返回新 Master 的地址,使得集群可以使用现在的 Master 替换失效 Master。
3、Master 和 Slave 服务器切换后,Master 的 redis.conf、Slave 的 redis.conf 和sentinel.conf 的配置文件的内容都会发生相应的改变,即 Master 主服务器的 redis.conf 配置文件中会多一行 slaveof 的配置,sentinel.conf 的监控目标会随之调换。
哨兵模式的优缺点

优点:

哨兵模式是基于主从模式的,所有主从的优点,哨兵模式都具有。
主从可以自动切换,系统更健壮,可用性更高。
缺点:

Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。

Redis-cluster 集群模式

(配置方式见黑马java宜立方商城项目教案day06)
redis集群是一个由多个主从节点群组成的分布式服务器群,它具有复制、高可用和分片特性。Redis集群不需要sentinel哨兵也能完成节点移除和故障转移的功能。需要将每个节点设置成集群模式,这种集群模式没有中心节点,可水平扩展,据官方文档称可以线性扩展到上万个节点(官方推荐不超过1000个节点)。redis集群的性能和高可用性均优于之前版本的哨兵模式,且集群配置非常简单。
Redis-Cluster采用无中心结构,它的特点如下:

*所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽。

*节点的fail是通过集群中超过半数的节点检测失效时才生效。

*客户端与redis节点直连,不需要中间代理层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。

缓存穿透、击穿、雪崩问题

缓存穿透:
是指访问一个一定不存在的数据,导致大量请求来到数据库,导致数据库挂掉。
一般的代码流程是:
参数传入对象主键ID
根据key从缓存中获取对象
如果对象不为空,直接返回
如果对象为空,进行数据库查询
如果从数据库查询出的对象不为空,则放入缓存(设定过期时间)

解决办法:在数据库查询不到时,也在缓存里放一个null,设置过期时间短一些比如30秒,这样这个id再来查时,缓存直接给反一个null,就不用来到数据库了。

缓存击穿:
某一个key特别热点,当缓存失效的一瞬间导致大量请求来到数据库导致数据库挂掉。

解决办法:
1、设置缓存永不过期
2、设置双重缓存备份A和B,当A缓存失效时,使用B缓存。

缓存雪崩:

大量的缓存key在同一时间同时失效过期,导致所有的请求到落到了数据库。

解决办法:
1、加锁
2、设置不同的过期时间,比如加一个随机时间
3、缓存备份

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值