NoSQL

关系型数据库存在的问题

  • 网站的用户并发性非常高,往往达到每秒上万次读写请求,对于传统关系型数据库来说,硬盘 I/O 是一个很大的瓶颈
  • 网站每天产生的数据量是巨大的,对于关系型数据库来说,在一张包含海量数据的表中查询,效率是非常低的。因此,关系型数据不适合持久存储海量数据
  • 很难进行横向扩展(增加服务器),也就是说想要提高数据处理能力,要使用性能更好的计算机(纵向扩展
  • 性能欠佳:导致关系型数据库性能欠佳的最主要原因就是多表的关联查询,为了保证数据库的ACID特性,必须尽量按照范式要求设计数据库,关系数据库中的表存储的往往是一个固定的、格式化的数据结构

特点

  1. 高可扩:数据之间没有联系
  2. 高性能:nosql是细粒度的缓存
  3. 高可用:CAP和BASE(异地多活)
  4. 数据类型多样:键值对,列,文档,图
  5. 没有固定查询语言
  6. 最终一致性
  7. 不遵循 ACID 特性(不提供对事务的处理)

NoSQL 的四大分类

KV 键值对

  • 新浪:Redis
  • 美团:Redis + Tair
  • 阿里、百度:Redis + memecache

文档型数据库(bson 格式和 json一样)

  • MongoDB (一般必须要掌握)

    • MongoDB 是一个基于分布式文件存储的数据库,C++ 编写,主要用来处理大量的文档!
    • MongoDB 是一个介于关系型数据库和非关系型数据中中间的产品!MongoDB 是非关系型数
      据库中功能最丰富,最像关系型数据库的!
  • ConthDB

列存储数据库

  • HBase
  • 分布式文件系统

图形 (Graph) 数据库

  • 他不是存图形,放的是关系,比如:朋友圈社交网络,广告推荐
  • Neo4j,InfoGrid;

🍹 四者对比:

img

Redis 入门

🎉 免费和开源!是当下最热门的 NoSQL 技术之一,也被人们称之为结构化数据库

🏠 官网 http://www.redis.cn/documentation.html

Redis(Remote Dictionary Server ),即远程字典服务:是一个开源的使用 ANSI C 语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的API。与传统数据库不同的是 Redis 的数据是存在内存中的 ,也就是它是内存数据库,所以读写速度非常快,因此 Redis 被广泛应用于缓存方向

端口:6379

🆚 Redis 与其他 key - value 缓存产品相比有以下三个特点

  • Redis 支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
  • Redis 不仅仅支持简单的 key-value 类型的数据,同时还提供 list,set,zset,hash 等数据结构的存储。
  • Redis支持数据的备份,即 master-slave 主从模式的数据备份。

👍 Redis 优势

  • 性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
  • 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
  • 原子 – Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。Redis单条命令是具有原子性的,但是事务并不保证原子性。
  • 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。

Redis 是很快的(10w+QPS),官方表示,Redis 是基于内存操作,CPU 不是 Redis 的性能瓶颈,Redis 的瓶颈取决于机器的内存和网络带宽,所以 Redis 是基于单线程的。Redis 将所有的数据全部放在内存中,如果使用多线程,那么 CPU 的上下文切换是一个耗时的操作。对于内存系统来说,如果没有上下文切换,多次读写都是在一个CPU上的,那么效率就是最高的。Redis 便基于此。

❓ Redis 相比 memcached 有哪些优势?

  • memcached 所有的值均是简单的字符串, redis 作为其替代者,支持更为丰富的数据类型
  • redis 的速度比 memcached 快很多
  • redis 可以持久化其数据

Redis使用场景

  • 会话缓存( Session Cache)
    最常用的一种使用 Redis 的情景是会话缓存( session cache)。用 Redis 缓存会话比其他存储(如 Memcached)的优势在于: Redis 提供持久化。
  • 全页缓存( FPC)
    除基本的会话 token 之外, Redis 还提供很简便的 FPC 平台。回到一致性问题,即使重启了Redis 实例,因为有磁盘的持久化,用户也不会看到页面加载速度的下降,这是一个极大改进,类似 PHP 本地 FPC。再次以 Magento 为例, Magento 提供一个插件来使用 Redis 作为全页缓存后端。此外,对 WordPress 的用户来说, Pantheon 有一个非常好的插件 wp-redis,这个插件能帮助你以最快速度加载你曾浏览过的页面。
  • 队列
    Reids 在内存存储引擎领域的一大优点是提供 list 和 set 操作,这使得 Redis 能作为一个很好的消息队列平台来使用。 Redis 作为队列使用的操作,就类似于本地程序语言(如 Python)对 list 的 push/pop 操作。
  • 排行榜/计数器
    Redis 在内存中对数字进行递增或递减的操作实现的非常好。集合( Set)和有序集合(Zset)也使得我们在执行这些操作的时候变的非常简单。
  • 发布/订阅
    最后(但肯定不是最不重要的)是 Redis 的发布/订阅功能。发布/订阅的使用场景确实非常多。我已看见人们在社交网络连接中使用,还可作为基于发布/订阅的脚本触发器,甚至用Redis 的发布/订阅功能来建立聊天系统 。

启动

注意!连接远程服务器的redis服务protected-mode nodaemonize yes,注释bind,更改端口(不然会被扫描挖矿),设置安全组

# 服务器
redis-server redis.conf
# 客户端
redis-cli

Redis系统命令

Redis 不区分大小写命令

redis 默认有 16 个数据库,默认使用的是第0个

# 选择数据库
select 3
# 查看数据库大小
dbsize
# 清除当前数据库
flushdb
# 清除所有数据库
flushall
# 清屏
clear
# 查看信息
# Server,Clients,Memory,Persistence,Stats,Replication,CPU,Cluster,Keyspace
info [section]
# 设置密码
config set requirepass 123456
# 授权密码
auth 123456

Redis架构

底层数据结构

① SDS 字符串(String)

在Redis中,C语言的字符串只会用于一些无需对字符串修改的地方,如日志打印等。而Redis默认的字符串实现是简单动态字符串(simple dynamic string,SDS)。

如set命令中的key底层即是一个SDS,而value如果是一个字符串类型,则底层也是SDS,如果value是列表,则列表里的每个元素底层都是SDS。除了set命令外,SDS还被用作缓冲区:AOF模块中的AOF缓冲区,客户端状态中的输入缓冲区等都是SDS实现的。

SDS的定义在Redis源码的src目录下的sds.h和sds.c文件中,定义如下:

typedef struct sdshdr{//记录buf数组中已使用字节的数量//等于 SDS 保存字符串的长度unsigned int len;//记录 buf 数组中未使用字节的数量unsigned int free;//字节数组,用于保存字符串char buf[];
}
image-20220116195535997

【优点】:封装了 len 属性,直接获取长度;增加时动态扩张

sds 扩容

  • 当字符串进行初始化的时候,buf=len+1,就是加上’\0’作为长度
  • 当修改后的 SDS 长度 len 小于 1MB,len=len*2,即buf加倍
  • 当修改后的 SDS 长度 len 大于 1MB,len=len*2+1MB

SDS 缩短

并不会立即使用内存重分配来回收多出来的字节,而是使用 free 属性将这些字节的数量记录下来,等待将来使用。 通过此策略,可以避免内存重分配,同时将来增长操作也有空间。 同时 SDS 也有相应的 API ,用来真正释放未使用空间,不用担心内存的浪费。

二进制存储

SDS 由于有 len 属性的存在,使用 len 来判断字符串是否结束,而不是空字符。这样就避免了二进制数据的问题,可以用来保存图片,音频,视频等文件的二进制数据。

② list 链表(List)

C 语言中没有内置链表的数据结构,Redis 实现了自己的链表结构。Redis 中列表的底层实现之一就是链表。

由于在list的结构中定义了头尾指针和长度,可以让push/pop、或者是求长度的操作复杂度只有o(1)。使用了void*的操作实现了多态,可以保存不同的类型的数据。

typedef struct list{//表头节点listNode *head;//表尾节点listNode *tail;//链表所包含的节点数量unsigned long len;//节点值复制函数void (*free) (void *ptr);//节点值释放函数void (*free) (void *ptr);//节点值对比函数int (*match) (void *ptr,void *key);
}list;typedef  struct listNode{//前置节点struct listNode *prev;//后置节点struct listNode *next;//节点的值void *value;  
}listNode
image-20220116204730362

③ ziplist 压缩链表(List,Hash,Zset)

压缩列表是列表键和哈希键的底层实现之一。当一个列表键只包含少量列表项并且每个都是小整数值或者长度比较短的字符串时,Redis 就采用压缩列表做底层实现。当一个哈希键只包含少量键值对,并且每个键值对的键和值也是小整数值或者长度比较短的字符串时,Redis 就采用压缩列表做底层实现。

压缩列表是 Redis 为了节约内存而实现的,是一系列特殊编码的连续内存块组成的顺序型数据结构。

image-20220116205401185

zlbytes :4 字节。ziplist的总长度(内存字节数),在内存重分配或者计算 zlend 的位置时使用。
zltail :4 字节。表尾节点相对表头的偏移地址,可以直接确定表尾节点的地址,无需遍历。
zllen :2 字节。节点数量,由于只有 2 字节大小,那么小于 65535 时,表示节点数量;等于 65535 时,需要遍历得到总数。
entry :列表节点,长度不定,由内容决定。
zlend :1 字节,特殊值 0xFF ,用于标记压缩列表的结束。

每个压缩列表节点的结构如图:

image-20220116205900475

prelen是前一个实体的长度,encode是指当前信息的编码信息,content是指编码过的信息

④ hashtable 字典(Hash,Set)

字典在 Redis 中应用很广泛,Redis 底层数据库就是用字典来实现的。任意一个键值对,无论是什么类型,都是存放在数据库的字典中的。另外,字典还是哈希对象的底层实现之一。

typedef struct dictht{//哈希表数组dictEntry **table;//哈希表大小unsigned long size;//哈希表大小掩码,用于计算索引值unsigned long sizemask;//该哈希表已有节点的数量unsigned long used;}dictht;
typedef struct dictEntry{void *key;union{void *val;uint64_tu64;int64_ts64;}v;struct dictEntry *next;
}dictEntry
image-20220119235003834

新增时,先根据键值对的键计算出哈希值,然后根据 sizemask 属性和哈希值,计算索引值——即落入数组中的哪个位置。之后如果有一个位置多个键值对要存入时,组成单向链表即可。
这里和 HashMap 的不同之处在于,链表添加时总是添加在表头位置。因为 dictEntry 节点组成的链表没有指向链表表尾的指针,为了速度考虑,总是将新节点加在链表的表头位置。

扩容和缩容

负载因子=哈希表中已有元素和哈希桶数的比值

  • 负载因子小于1一定不扩容

  • 负载因子大于5一定扩容

  • 负载因子如果在1-5之间,redis没有进行save/rewrite的操作就会扩容

  • 负载因子如果是0.1,那么会进行缩容

扩容会变成原来的2倍,缩容会变成原来的1/2。

渐进式 rehash

如果键值对量巨大时,一次性全部 rehash 必然造成一段时间的停止服务。所以要分多次、渐进式的将键值对从 ht[0] 慢慢的 rehash 到 ht[1] 中。

具体过程:

  1. 为 ht[1] 分配空间,同时有 ht[0] 和 ht[1] 两个哈希表。
  2. 在字典中维持一个索引计数器变量 rehashindex ,并将其置为 0 ,表示 rehash 正式开始。
  3. 在 rehash 期间,每次对字典执行添加、删除、查找或者更新操作时,程序除了执行指定的操作之外,还会顺便将 ht[0] 哈希表在 rehashindex 索引上的所有键值对 rehash 到 ht[1] 上,当 rehash 工作完成之后,程序将 rehashindex 的值加一。
  4. 随着字典操作的不断进行,最终在某个时间点,ht[0] 的所有键值对都被 rehash 到 ht[1] ,这时程序将 rehashindex 的值置为 -1 ,表示 rehash 工作完成。
    渐进式 rehash 的过程中,更新删除查找等都会在两个哈希表上进行,比如查找,先在 ht[0] 中查找,如果没找到,就去 ht[1] 中查找。而新增操作,直接新增在 ht[1] 中,ht[0] 不会进行任何的新增操作。保证 ht[0] 的数量只减不增,最终变为空表。

⑤ intset 整数集合(Set)

当一个集合只包含整数值元素,并且这个集合的元素数量不多时,Redis 就会使用整数集合作为集合键的底层实现。

默认其中存放的数据是从小到大有序的。

int8_t不保存对应的值,真正的类型由encoding决定,可以保存 int16_t ,int32_t ,int64_t 的整数值

typedef struct intset{//编码方式uint32_t encoding;//集合包含的元素数量uint32_t length;//保存元素的数组int8_t contents[];
}intset

插入的时候先二分查找到插入的位置(O(log(N))),然后在对插入位置后面所有的元素往后移动一个位置。复杂度(O(N))

当插入的一个数据比原有的数据的字节都大,那么整个数据中的所占用的字节都会进行升级。

⑥ skiplist 跳跃表(Zset,集群节点)

有序链表只能逐一查找元素,导致操作起来非常缓慢,于是就出现了跳表。具体来说,跳表在链表的基础上,增加了多级索引,通过索引位置的几个跳转,实现数据的快速定位。

image-20220120012950765

跳表有很多层组成。每一层都是有序的,除了最后一层都只包含部分数据。最底层的包含所有的数据

typedef struct zskiplistNode {// 每一层都有前进指针和跨度,从头到尾遍历时,访问会沿着层的前进指针进行。struct zskiplistLevel{struct zskiplistNode *forward;unsigned int span;}level[];// 后退指针,指向前一个节点,从尾到头遍历时使用。struct zskiplistNode *backward;// 分值,跳跃表中的分值按从小到大排列。double score;// 成员对象,各个节点保存有各个成员对象。robj *obj;} zskiplistNode;
typedef struct zskiplist{//表头节点和表尾节点struct skiplistNode *header, *tail;//表中节点的数量unsigned long length;//表中层数最大的节点的层数int level;}zskiplist;
image-20220120010918153

搜索:从最上层开始搜索,如果发现当前层的最大值小于搜索的值。那么就去下一层寻找,往复如此的操作,直到找到最下面的一层。复杂度(O(Log(N)))

数据淘汰策略

  • noeviction:返回错误当内存限制达到并且客户端尝试执行会让更多内存被使用的命令(大部分的写入指令,但 DEL 和几个例外)
  • allkeys-lru:尝试回收最少使用的键(LRU),使得新添加的数据有空间存放。
  • volatile-lru:尝试回收最少使用的键(LRU),但仅限于在过期集合的键,使得新添加的数据有空间存放。
  • allkeys-random:回收随机的键使得新添加的数据有空间存放。
  • volatile-random:回收随机的键使得新添加的数据有空间存放,但仅限于在过期集合的键。
  • volatile-ttl:回收在过期集合的键,并且优先回收存活时间(TTL)较短的键,使得新添加的数据有空间存放。

分区

分区可以让 Redis 管理更大的内存, Redis 将可以使用所有机器的内存。如果没有分区,你最多只能使用一台机器的内存。分区使 Redis 的计算能力通过简单地增加计算机得到成倍提升,Redis 的网络带宽也会随着计算机和网卡的增加而成倍增长。

分区实现方案

  • **客户端分区:**就是在客户端就已经决定数据会被存储到哪个redis节点或者从哪个redis节点读取。大多数客户端已经实现了客户端分区。

  • **代理分区:**意味着客户端将请求发送给代理,然后代理决定去哪个节点写数据或者读数据。代理根据分区规则决定请求哪些 Redis 实例,然后根据 Redis 的响应结果返回给客户端。 redis和 memcached 的一种代理实现就是 Twemproxy

  • **查询路由(Query routing) :**客户端随机地请求任意一个 redis 实例,然后由 Redis 将请求转发给正确的 Redis 节点。 Redis Cluster 实现了一种混合形式的查询路由,但并不是直接将请求从一个 redis 节点转发到另一个 redis 节点,而是在客户端的帮助下直接 redirected 到正确的 redis 节点。

性能测试

redis-benchmark 是一个官方自带的压力(性能)测试工具。

redis 性能测试工具可选参数如下:

我们来简单测试下:

# 测试:100个并发连接 100000请求
redis-benchmark -h localhost -p 6379 -c 100 -n 100000

img

5️⃣ 五大数据类型

Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库缓存消息中间件MQ。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。

Redis 五大基本数据类型

  • String
  • List
  • Set
  • Zset
  • Hash

Redis 键 (key) 命令

Redis 键命令用于管理 redis 的键。

# 存
set key value
# 取
get key
# 查看所有key
keys *
# 判断当前的key是否存在
EXISTS key
# 移动key到其它数据库
move name 2
# 设置key的过期时间,单位是秒
EXPIRE key second
# 查看当前key的剩余时间
ttl key
# 查看当前key的类型
type key

1. String 字符串

string的数据使用string

value可以是字符串或数字,最大512M

统计(浏览量,粉丝数)

# 追加字符串,如果当前key不存在,就相当于set key
APPEND key "hello"
# 获取字符串的长度
STRLEN key# 自增/自减1
incr key
decr key
# 可以设置步长,指定增量
incrby key 10
decrby key 10# 截取字符串 [0,3]
GETRANGE key 0 3 
# 获取全部的字符串(从0开始,到倒数第1个)
GETRANGE key 0 -1
# 替换从指定位置 1 开始的字符串
SETRANGE key 1 val# 设置过期时间
setex key 30
# 如果key不存在,创建key(分布式锁常用)
setnx key val# 同时设置多个值
mset k1 v1 k2 v2 k3 v3 
# 同时获取多个值
mget k1 k2 k3
# msetnx 是一个原子性的操作,要么一起成功,要么一起失败
msetnx k1 v1 k4 v4
# 设置一个user:1 对象 值为 json字符来保存一个对象
set user:1 {name:zhangsan,age:3}
# 这里的key是一个巧妙的设计: user:{id}:{filed}
mset user:1:name zhangsan user:1:age 2
mget user:1:name user:1:age# getset 先get然后在set
# 如果不存在值,则返回 nil,并设置新的值; 如果存在值,获取原来的值,并设置新的值
getset db redis

2. List 列表

list内部使用list和ziplist,当数量比较小的时候会使用ziplist来减少内存使用,否则使用linkedlist

所有的 list命令都是用 l 开头的

实现栈,队列,阻塞队列

# 头部插入 (左)
LPUSH list val
# 尾部插入 (右)
Rpush list val
# 移除list的第一个元素
Lpop list
# 移除list的最后一个元素
Rpop list # 通过区间获取具体的值
LRANGE list 0 1 
# 通过下标获得 list 中的某一个值
lindex list index
# 返回列表的长度
Llen list # 移除list集合中指定个数的value,精确匹配
lrem list 1 val # 通过下标截取指定的长度,这个list只剩下被截取的元素
ltrim list 1 2 # 移除列表的最后一个元素,将他移动到新的列表中
rpoplpush list list2# 将下标0的值替换为val,如果该列表不存在或下标越界,更新就会报错
lset list 0 val # 将某个具体的value插入到列表中某个元素的前面或者后面
linsert list before val val2
linsert list after val val2

3. Set 集合

set在数据都是整数类型时,使用intset,否则使用hashtable

set 中的值是不能重复的。

共同关注,共同爱好,推荐好友

# 添加
sadd set val
# 移除set集合中的指定元素
srem set val# 查看指定set的所有值
smembers set
# 判断某一个值是不是在set集合中
sismember set val
# 获取set集合中的内容元素个数
scard set # 随机抽选出(指定个数的)元素
SRANDMEMBER set
SRANDMEMBER set 2# 随机删除一些set集合中的元素
spop set# 将一个指定的值,移动到另外一个set集合
smove set set2 val # 计算差集,交集,并集
sdiff set1 set2 # set1 - set2
sinter set1 set2
sunion set1 set2
# 并存储
sdiffstore set3 set1 set2
sinterstore set3 set1 set2
sunionstore set3 set1 set2

4. Zset 有序集合

Zset使用ziplist和skiplist+hashtable,当数据量小的时候使用ziplist,否则使用skiplist+hashtable

Zset 在 set 的基础上,增加了一个值得分: k1 score1 v1

排行榜,消息等级

# 添加
zadd set score val# 区间获取,默认升序
zrange set 0 -1 	# 升序
zrevrange set 0 -1 	# 降序
# 按score获取
# WITHSCORES作用是也输出分数
zrangebyscore set -inf +inf WITHSCORES# 移除有序集合中的指定元素
zrem set val
# 获取有序集合中的个数
zcard set# 获取指定 score 区间的成员数量
zcount set 101 103

5. Hash 哈希

Hash使用hashtable和ziplist,当数据量比较小的时候使用ziplist,否则使用hashtable

对象的存储

# get,set
hset hash key val
hget hash key
# 获取全部的数据
hgetall hash
# 只获得所有field
hkeys hash
# 只获得所有value
hvals hash# 删除hash指定key字段
hdel hash key# 获取hash表的字段数量
hlen hash 
# 判断hash中指定字段是否存在
hexists hash key# 指定增量
hincrby hash key 1# 如果字段不存在则可以设置
hsetnx hash key val

3️⃣ 三种特殊数据类型

1. Gerspatial 地理位置

GEO底层的实现原理其实就是 Zset,我们可以使用 Zset命令来操作 geo

ZRANGE china:city 0 -1 # 查看地图中全部的元素
zrem china:city beijing # 移除指定元素# 添加地理位置
# 两级无法直接添加,我们一般会下载城市数据,直接通过 java 程序一次性导入
# 有效的经度从-180度到180度。有效的纬度从-85.05112878度到85.05112878度。
# 当坐标位置超出上述指定范围时,该命令将会返回一个错误。
geoadd china:city 116.40 39.90 beijing# 获取指定的城市的经度和纬度
geopos china:city beijing# 获取两地之间的距离
# 单位有 m米,km千米,mi英里,ft英尺
geodist china:city beijing shanghai km # 查看上海到北京的直线距离# 以给定的经纬度为中心, 找出某一半径内的元素
georadius china:city 110 30 1000 km # 以110,30 这个经纬度为中心,寻找方圆1000km内的城市
withdist # 显示到中间距离的位置
withcoord # 显示他人的定位信息
count 1 #筛选出指定数量的结果# 找出位于指定元素周围的其他元素
georadiusbymember china:city beijing 1000 km# 将二维的经纬度转换为一维11个字符的Geohash字符串字符串。如果两个字符串越接近,那么则距离越近
geohash china:city beijing chongqin

2. Hyperloglog 基数统计

基数 & 基数估计:

比如数据集 {1, 3, 5, 7, 5, 7, 8}, 那么这个数据集的基数集为{1, 3, 5 ,7, 8}, 基数(不重复元素)为 5。 基数估计就是在误差可接受的范围内,快速计算基数。(也就是说 Hyperloglog 是有一定的错误率的,很小)

Redis 在 2.8.9 版本添加了 HyperLogLog结构,用来做基数统计

优点

计算基数所需的空间总是固定的、并且是很小的,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。

缺点

  • 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog不能像集合那样,返回输入的各个元素。
  • 有一定的错误率的(0.81)

对于统计网页的 UV (一个人访问一个网站多次,但是还是算作一个人)来说 ,传统的方式是利用 set保存用户的 id,然后就统计 set中的元素数量。这个方式如果保存大量的用户 id,就会比较麻烦。我们的目的是为了计数,而不是保存用户 id。

🚩 如果允许容错,那么一定可以使用 Hyperloglog,如果不允许容错,就使用 set或者自定义数据类型。

# 创建一组元素
PFadd key a b c d e f g h i j# 统计基数数量
PFCOUNT key # 合并两组 key1 key2 => key3 并集
PFmerge key3 key1 key2 

3. Bitmap 位图

Bitmap就是通过一个 bit 位来表示某个元素对应的值或者状态, 其中的 key 就是对应元素本身,可以极大的节省储存空间。

Redis 从2.2.0版本开始新增了setbit,getbit,bitcount等几个 bitmap 相关命令。虽然是新命令,但是并没有新增新的数据类型,因为setbit等命令只不过是在set上的扩展。

用户签到(状态:签到,未签到),统计活跃用户(状态:活跃,不活跃)等只有 0 和 1 两个状态的都可以使用 Bitmap

# 设置sign的第3个位为1
setbit sign 3 1
# 获取
getbit sign 3
# 统计1的个数
bitcount sign

事务 Transaction

严格意义来讲,Redis的事务和我们理解的传统数据库(如mysql)的事务是不一样的。

Redis 事务本质:一组命令的集合。一个事务中的所有命令都会被序列化,在事务的执行过程中,按照顺序执行。

Redis事务没有没有隔离级别的概念。

Redis 事务可以一次执行多个命令, 并且带有以下三个重要的保证:

  • 批量操作在发送 EXEC命令前被放入队列缓存。(所有的命令在事务中,并没有直接被执行,只有发起执行命令 EXEC的时候才会执行)

  • 收到 EXEC命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。(Redis单条命令是具有原子性的,但是事务并不保证原子性

  • 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。

一个事务从开始到执行会经历以下三个阶段:

# 开始事务
MULTI
# 命令入队
SET book-name "Mastering C++ in 21 days"
GET book-name
SADD tag "C++" "Programming" "Mastering Series"
SMEMBERS tag
# 执行事务
EXEC# 取消事务 
DISCARD
# 监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。
WATCH key [key ...]
# 取消 WATCH 命令对所有 key 的监视。
UNWATCH 

Redis WATCH 监控事务

在 Redis 中使用 watch命令可以决定事务是执行还是回滚。可以在 multi命令之前使用 watch命令监控某些键值对。

当 Redis 使用 exec命令执行事务的时候,它首先会去比对被 watch命令所监控的键值对,如果没有发生变化,那么它会执行事务队列中的命令,提交事务;如果发生变化,那么它不会执行任何事务中的命令,而去事务回滚。无论事务是否回滚,Redis 都会取消执行事务前的 watch 命令,这个过程如下图所示:

🚨 execdiscardunwatch 命令都会清除所有监视。

Redis 参考了多线程中使用的 CAS 去执行的。

事务正常运行,没有被其他线程修改:

127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> watch money # 监视 money 对象
OK
127.0.0.1:6379> multi 
OK
127.0.0.1:6379> DECRBY money 20
QUEUED
127.0.0.1:6379> INCRBY out 20
QUEUED
127.0.0.1:6379> exec # 事务正常结束,数据期间没有发生变动,这个时候就正常执行成功
1) (integer) 80
2) (integer) 20

Jedis—Java 使用 Redis

Jedis 是 Redis 官方推荐的 Java 连接开发工具, 使用 Java 操作 Redis 的中间件。

首先需要导入 Jedis 的 maven 依赖:

<!--导入jedis的包-->
<dependencies><!-- https://mvnrepository.com/artifact/redis.clients/jedis --><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>3.2.0</version></dependency><!--fastjson--><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.62</version></dependency>
</dependencies>

接下来进行测试是否能够正确访问到 Redis 服务:

import redis.clients.jedis.Jedis;
public class TestPing {public static void main(String[] args) {// 1、 new Jedis 对象即可Jedis jedis = new Jedis("127.0.0.1",6379);// jedis 所有的命令就是我们之前学习的所有指令!所以之前的指令学习很重要System.out.println(jedis.ping());}
}

① String(字符串) 实例

public class RedisStringJava {public static void main(String[] args) {//连接本地的 Redis 服务Jedis jedis = new Jedis("localhost");//设置 redis 字符串数据jedis.set("name","smallbeef");// 获取存储的数据并输出System.out.println("redis 存储的字符串为:" + jedis.get("name"));}
}

② List(列表) 实例

public class RedisListJava {public static void main(String[] args) {Jedis jedis = new Jedis("127.0.0.1",6379);// 存入列表jedis.lpush("mylist","one");jedis.lpush("mylist","two");jedis.lpush("mylist","three");// 获取数据List<String> mylist = jedis.lrange("mylist", 0, 2);for(int i=0; i<mylist.size(); i++) {System.out.println("列表项为: "+mylist.get(i));}
}

③ keys 实例

public class RedisKeysJava {public static void main(String[] args) {Jedis jedis = new Jedis("127.0.0.1",6379);Set<String> keys = jedis.keys("*");Iterator<String> iterator = keys.iterator();while(iterator.hasNext()){String key = iterator.next();System.out.println(key); // name mylist}}
}

④ 事务实例

public class TestTX {public static void main(String[] args) {Jedis jedis = new Jedis("127.0.0.1",6379);jedis.flushDB();JSONObject jsonObject = new JSONObject();jsonObject.put("hello","world");jsonObject.put("name","smallbeef");// 开启事务Transaction multi = jedis.multi();String s = jsonObject.toJSONString();// jedis watchtry{multi.set("user1", s);multi.set("user2", s);int i = 1/0 ; // 抛出异常,执行失败multi.exec();} catch (Exception e){multi.discard(); // 放弃事务e.printStackTrace();} finally {System.out.println(jedis.get("user1")); // nullSystem.out.println(jedis.get("user2")); // nulljedis.close(); // 关闭连接}}
}

📑 Redis 配置文件详解

centos中通过yum安装的默认的配置文件在 /etc/redis.conf

通过配置启动:redis-server /etc/redis.conf

1. 网络相关

bind 127.0.0.1 # 绑定的主机地址,如果需要允许外网访问,需要将此行注释,或者改为 bind 0.0.0.0protected-mode yes # 保护模式port 6379 # 指定 Redis 监听端口,默认端口为 6379timeout 300	# 当客户端闲置多长秒后关闭连接,如果指定为 0 ,表示关闭该功能

2. 守护进程

daemonize yes # 以守护进程的方式运行,默认是 no,我们需要自己开启为 yes# 当 Redis 以守护进程方式运行时,Redis 默认会把 pid 写入 /var/run/redis.pid 文件,可以通过 pidfile 指定
pidfile /var/run/redis.pid

3. 日志

# 指定日志记录级别,Redis 总共支持四个级别:debug、verbose、notice、warning,默认为 notice
loglevel noticelogfile "" # 日志的文件位置名# 日志记录方式,默认为标准输出,如果配置 Redis 为守护进程方式运行,而这里又配置为日志记录方式为标准输出,则日志将会发送给 /dev/null
logfile stdout

4. RDB 配置

持久化,在规定的时间内,执行了多少次操作,则会持久化到文件 .rdb. aof

# 如果900s内,如果至少有一个1 key进行了修改,就进行持久化操作
save 900 1# 持久化如果出错,是否还需要继续工作
# 当启用了RDB且最后一次后台保存数据失败,Redis是否停止接收数据。这会让用户意识到数据没有正确持久化到磁盘上,否则没有人会注意到灾难(disaster)发生了。如果Redis重启了,那么又可以重新开始接收数据了
stop-writes-on-bgsave-error yes # 指定存储至本地数据库时是否压缩数据,默认为 yes,Redis 采用 LZF 压缩,如果为了节省 CPU 时间,可以关闭该选项,但会导致数据库文件变的巨大
rdbcompression yes # 保存rdb文件的时候,进行错误的检查校验
rdbchecksum yes # 快照文件名
dbfilename dump.rdb# 快照文件的存放路径
dir /var/lib/redis 

5. 安全

可以在这里设置redis的密码,默认是没有密码

# 获取redis的密码
config get requirepass 
# 设置redis的密码
config set requirepass "123456" 
# 使用密码进行登录
auth 123456

设置密码后可使用如下命令进行登录:

redis-cli -a 密码

6. 限制

databases 16 # 数据库的数量,默认是 16 个数据库# 设置同一时间最大客户端连接数,默认无限制,Redis 可以同时打开的客户端连接数为 Redis 进程可以打开的最大文件描述符数,如果设置 maxclients 0,表示不作限制。当客户端连接数到达限制时,Redis 会关闭新的连接并向客户端返回 max number of clients reached 错误信息
maxclients 10000 # 指定 Redis 最大内存限制,Redis 在启动时会把数据加载到内存中,达到最大内存后,Redis 会先尝试清除已到期或即将到期的 Key,当此方法处理 后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作。Redis 新的 vm 机制,会把 Key 存放内存,Value 会存放在 swap 区
maxmemory <bytes> 

7. AOF 配置

# 指定是否在每次更新操作后进行日志记录,Redis 在默认情况下是异步的把数据写入磁盘,如果不开启,可能会在断电时导致一段时间内的数据丢失。因为 redis 本身同步数据文件是按上面保存条件来同步的,所以有的数据会在一段时间内只存在于内存中。
# 默认为 no,即默认使用rdb方式持久化的,在大部分所有的情况下,rdb完全够用
appendonly no # 指定更新日志文件名(持久化的文件的名字),默认为 appendonly.aof
appendfilename "appendonly.aof" # 指定更新日志条件,共有 3 个可选值:- no:不执行 sync,表示等操作系统进行数据缓存同步到磁盘(快)- always:表示每次更新操作后手动调用 fsync() 将数据写到磁盘(慢,安全)- everysec:表示每秒同步一次(折中,默认值),可能会丢失这1s的数据
appendfsync everysec 

持久化

Redis是一个内存数据库,为了避免内存中数据丢失,Redis提供了对持久化的支持,我们可以选择不同的方式将数据从内存中保存到硬盘当中,使数据可以持久化保存。

持久化流程

既然redis的数据可以保存在磁盘上,那么这个流程是什么样的呢?

要有下面五个过程:

  • 客户端向服务端发送写操作(数据在客户端的内存中)。

  • 数据库服务端接收到写请求的数据(数据在服务端的内存中)。

  • 服务端调用write这个系统调用,将数据往磁盘上写(数据在系统内存的缓冲区中)。

  • 操作系统将缓冲区中的数据转移到磁盘控制器上(数据在磁盘缓存中)。

  • 磁盘控制器将数据写到磁盘的物理介质中(数据真正落到磁盘上)。

这5个过程是在理想条件下一个正常的保存流程,但是在大多数情况下,我们的机器等等都会有各种各样的故障,这里划分了两种情况:

  • Redis数据库发生故障,只要在上面的第三步执行完毕,那么就可以持久化保存,剩下的两步由操作系统替我们完成。

  • 操作系统发生故障,必须上面5步都完成才可以。

这里它提供了两种策略机制。

RDB(Redis DataBase)

RDB其实就是把数据以快照(snapshot)的形式保存在磁盘上。在指定的时间间隔内将内存中的数据集快照写入磁盘。也是默认的持久化方式,这种方式是就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为 dump.rdb。

优点

  • RDB文件紧凑,全量备份,非常适合用于进行备份和灾难恢复。
  • 生成RDB文件的时候,redis主进程会fork()一个子进程来处理所有保存工作,主进程不需要进行任何磁盘IO操作。
  • RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快

缺点

  • 需要一定的时间间隔进程操作!如果 redis 意外宕机了,这个最后一次修改数据就没有了
  • fork 进程的时候,会占用一定的内容空间

触发机制

① save 触发方式

时间复杂度: O(N), N 为要保存到数据库中的 key 的数量。

使用 save命令执行一个同步保存操作,将当前 Redis 实例的所有数据快照 以 RDB 文件的形式保存到硬盘:

该命令会阻塞当前Redis服务器,具体流程如下:

一般来说,在生产环境很少执行 SAVE操作,因为它会阻塞所有客户端,这种方式显然不可取。

保存数据库的任务通常由 BGSAVE命令异步地执行。然而,如果负责保存数据的后台子进程不幸出现问题时, SAVE可以作为保存数据的最后手段来使用。

② bgsave 触发方式

时间复杂度: O(N), N 为要保存到数据库中的 key 的数量。

在后台异步保存当前数据库的数据到磁盘。

bgsave 命令执行之后立即返回 OK ,然后 Redis fork 出一个新子进程,原来的 Redis 进程(父进程)继续处理客户端请求,而子进程则负责将数据保存到磁盘,然后退出。

具体操作是Redis进程执行fork操作创建子进程,RDB持久化过程由子进程负责,完成后自动结束。阻塞只发生在fork阶段,一般时间很短。基本上 Redis 内部所有的RDB操作都是采用 bgsave命令。

③ 自动化触发方式

自动触发是由我们的配置文件来完成的。

[相关配置项见Redis 配置文件详解](# 📑 Redis 配置文件详解)

AOF(Append Only File)

全量备份总是耗时的,有时候我们提供一种更加高效的方式,即日志记录:redis 会将每一个收到的写命令都通过 write函数追加到文件 appendonly.aof 中,恢复的时候就把这个文件全部在执行一遍。

优点

  • AOF 可以更好的保护数据不丢失,一般AOF会每隔1秒,通过一个后台线程执行一次 fsync操作,最多丢失1秒钟的数据。
  • AOF 日志文件没有任何磁盘寻址的开销,写入性能非常高,文件不容易破损。
  • AOF 日志文件即使过大的时候,出现后台重写操作,也不会影响客户端的读写。
  • AOF 日志文件的命令通过非常可读的方式进行记录,这个特性非常适合做灾难性的误删除的紧急恢复。比如某人不小心用 flushall命令清空了所有数据,只要这个时候后台 rewrite 还没有发生,那么就可以立即拷贝AOF文件,将最后一条 flushall命令给删了,然后再将该AOF文件放回去,就可以通过恢复机制,自动恢复所有数据

缺点

  • 相对于数据文件来说,AOF 远远大于 RDB,修复的速度也比 RDB慢
  • AOF 运行效率也要比 RDB慢,所以 Redis 默认的配置就是 RDB持久化
# 默认是不开启的,我们需要手动进行配置, 我们只需要将 appendonly 改为 yes就开启了
appendonly yes

重写

AOF 提供重写机制保证文件不会越来越大。

如果 aof 文件大于 64m, redis 提供了 bgrewriteaof命令,将内存中的数据以命令的方式保存到临时文件中,同时会 fork 出一条新进程来将文件重写。重写 AOF 文件的操作,并没有读取旧的 AOF 文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的 AOF 文件,这点和快照有点类似。

触发机制

  • 每次修改同步 always:同步持久化,每次更新操作后调用 fsync() 将数据写到磁盘,性能较差但数据完整性比较好

  • 每秒同步 everysec:异步操作,每秒记录。可能会丢失这1s的数据

  • 不同步 no:从不同步,等操作系统进行数据缓存同步到磁盘

# 指定更新日志条件,共有 3 个可选值
appendfsync everysec 

修复aof文件

redis-check-aof --fix appendonly.aof

使用建议

  1. 只做缓存时,不需要持久化
  2. 同时开启两种持久化方式时,重启时会优先载入AOF,因为AOF通常要比RDB数据完整
  3. 建议:
    • RDB更适合备份,因为不需要持续IO,不会持续变化,快速重启,不会有潜在Bug
    • 只在Slave上备份RDB,只保留save 900 1
    • AOF重写大小可以设到5G以上,尽量减少AOF rewrite 频率
    • 如果不Enable AOF,只靠 Master-Slave Replication 也可实现高可用,省掉一大笔IO,代价是Master/Slave同时挂,会丢失十几分钟数据,启动脚本也需要比较他们的RDB文件,载入较新的

发布订阅

Redis 发布订阅 (pub/sub) 是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。

👨‍💼 三大角色:消息发布者;频道 channel;消息订阅者

Redis 客户端可以订阅任意数量的频道。

广泛用于构建即时通信应用,比如网络聊天室和实时广播、实时提醒等。

命令

命令描述
PSUBSCRIBE pattern [pattern ...]订阅一个或多个符合给定模式的频道。
PUNSUBSCRIBE [pattern [pattern ...]]退订所有给定模式的频道。
SUBSCRIBE channel channel [...]订阅给定的一个或多个频道的信息。
UNSUBSCRIBE [channel [channel ...]]退订给定的频道。
PUBLISH channel message将信息发送到指定的频道。
PUBSUB subcommand [argument [argument ...]]查看订阅与发布系统状态。

实例

以下实例演示了发布订阅是如何工作的。在我们实例中我们创建了订阅频道名为 redisChat

订阅端:

redis 127.0.0.1:6379> SUBSCRIBE redisChatReading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "redisChat"
3) (integer) 1
# 等待读取推送的信息

发布端:

redis 127.0.0.1:6379> PUBLISH redisChat "Redis is a great caching technique"(integer) 1redis 127.0.0.1:6379> PUBLISH redisChat "Learn redis"(integer) 1# 订阅者的客户端会显示如下消息
1) "message" # 消息
2) "redisChat" # 哪个频道的消息
3) "Redis is a great caching technique" # 消息的具体内容
1) "message"
2) "redisChat"
3) "Learn redis"

原理

Redis 底层是使用 C 实现的,通过分析 Redis 源码里的 pubsub.c 文件,了解发布和订阅机制的底层实现,加深对 Redis 的理解。

Redis 提供两种信息机制:

① 频道

每个 Redis 服务器进程都维持着一个表示服务器状态的 redis.h/redisServer 结构, 结构的 pubsub_channels 属性是一个字典, 这个字典就用于保存订阅频道的信息

struct redisServer {// ...dict *pubsub_channels;// ...
};

⭐ 其中,字典的键为正在被订阅的频道, 而字典的值则是一个链表, 链表中保存了所有订阅这个频道的客户端

比如说,在下图展示的这个 pubsub_channels 示例中, client2client5client1 就订阅了 channel1 , 而其他频道也分别被别的客户端所订阅:

当客户端调用 SUBSCRIBE 命令时, 程序就将客户端和要订阅的频道在 pubsub_channels 字典中关联起来。

举个例子,如果客户端 client10086 执行命令 SUBSCRIBE channel1 channel2 channel3 ,那么前面展示的 pubsub_channels 将变成下面这个样子:

通过 pubsub_channels 字典, 程序只要检查某个频道是否为字典的键, 就可以知道该频道是否正在被客户端订阅; 只要取出某个键的值, 就可以得到所有订阅该频道的客户端的信息。

② 模式

redisServer.pubsub_patterns 属性是一个链表,链表中保存着所有和模式相关的信息:

struct redisServer {// ...list *pubsub_patterns;// ...
};

链表中的每个节点都包含一个 redis.h/pubsubPattern 结构:

typedef struct pubsubPattern {redisClient *client; // 订阅模式的客户端robj *pattern;		// 被订阅的模式
} pubsubPattern;

每当调用 PSUBSCRIBE 命令订阅一个模式时, 程序就创建一个包含客户端信息和被订阅模式的 pubsubPattern 结构, 并将该结构添加到 redisServer.pubsub_patterns 链表中。

作为例子,下图展示了一个包含两个模式的 pubsub_patterns 链表, 其中 client123client256 都正在订阅 tweet.shop.* 模式:

如果这时客户端 client10086 执行 PSUBSCRIBE broadcast.live.* , 那么 pubsub_patterns 链表将被更新成这样:

通过遍历整个 pubsub_patterns 链表,程序可以检查所有正在被订阅的模式,以及订阅这些模式的客户端。

当使用 PUBLISH命令发送信息到某个频道时, 不仅所有订阅该频道的客户端会收到信息, 如果有某个/某些模式和这个频道匹配的话, 那么所有订阅这个/这些频道的客户端也同样会收到信息。

下图展示了一个带有频道和模式的例子, 其中 tweet.shop.* 模式匹配了 tweet.shop.kindle 频道和 tweet.shop.ipad 频道, 并且有不同的客户端分别订阅它们三个。当有信息发送到 tweet.shop.kindle 频道时, 信息除了发送给 clientXclientY 之外, 还会发送给订阅 tweet.shop.* 模式的 client123client256

集群

主从模式

主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master/leader),后者称为从节点(slave/follower)。Master以写为主,Slave 以读为主。

**数据的复制是单向的,只能由主节点到从节点。**主机中的所有信息和数据,都会自动被从机保存。

默认情况下,每台 Redis 服务器都是主节点;且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。

🚩 主从复制的作用主要包括:

  • 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
  • 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。
  • 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
  • 高可用(集群)基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是 Redis 高可用的基础。

一般来说,要将Redis运用于工程项目中,只使用一台Redis是万万不能的(宕机),原因如下:

  • 从结构上,单个Redis服务器会发生单点故障,并且一台服务器需要处理所有的请求负载,压力较大

  • 从容量上,单个Redis服务器内存容量有限,就算一台Redis服务器内存容量为256G,也不能将所有内存用作Redis存储内存,一般来说,单台Redis最大使用内存不应该超过20G。

电商网站上的商品,一般都是一次上传,无数次浏览的,说专业点也就是"多读少写"。对于这种场景,我们可以使用如下架构:

主从复制,读写分离。其实在 80% 的情况下都是在进行读操作,一般使用一主二从来减缓服务器的压力。

环境配置

配置从节点:端口,pid名字,log文件名,rdb文件名

查看主节点的信息:

info replication # 查看当前库的信息

复制原理

Slave 启动成功连接到 master 后会发送一个 sync同步命令,Master 接到命令,启动后台的存盘进程,同时收集所有接收到的用于修改数据集的命令,在后台进程执行完毕之后,master 将传送整个数据文件到 slave,并完成一次完全同步。

全量复制:slave 服务在接收到数据库文件数据后,将其存盘并加载到内存中。

增量复制:Master 继续将新的所有收集到的修改命令依次传给 slave,完成同步

注意:只要是重新连接 master,将自动执行全量复制。

薪火相传

上一个 Slave (记为 A)可以是下一个 slave 的 Master(注意 A 在它的主节点面前仍然是从节点),A 同样可以接收其他 slaves 的连接和同步请求,那么 A 作为了链条中下一个的 master, 可以有效减轻 master 的写压力。

使用slaveof命令将中间从机升级为主机,但还是无法写入,原主机slave-1。如果源主机断开,则升级为真正的主机。

反客为主

从节点可以使用 slaveof no one 升级成为主节点。

哨兵模式 sentinel

主从切换技术的方法是:当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务不可用。Redis 从 2.8开始正式提供了 Sentinel(哨兵) 架构来解决这个问题。它是反客为主的自动版,能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库

哨兵模式是一种特殊的模式,首先 Redis 提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待 Redis 服务器响应,从而监控运行的多个 Redis 实例。

这里的哨兵有两个作用:

  • 通过发送命令,让 Redis 服务器返回监控其运行状态,包括主服务器和从服务器。
  • 当哨兵监测到 master 宕机,会自动将 slave 切换成 master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机

然而一个哨兵进程对 Redis 服务器进行监控,可能会出现问题,为此,我们可以使用多个哨兵进行监控。各个哨兵之间还会互相进行监控,这样就形成了多哨兵模式

假设主服务器宕机,哨兵1先检测到这个结果,系统并不会马上进行 failover(故障转移) 过程,仅仅是哨兵1主观的认为主服务器不可用,这个现象成为主观下线。当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行 failover 操作。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这个过程称为客观下线

优点

  • 哨兵集群,基于主从复制模式,所有的主从复制优点,它全有

  • 主从可以切换,故障可以转移,系统的可用性就会更好

  • 哨兵模式就是主从模式的升级,手动到自动,更加健壮

缺点

  • Redis 不易于在线扩容,集群容量一旦到达上限,在线扩容就十分麻烦
  • 实现哨兵模式的配置其实是很麻烦的,里面有很多选择(多哨兵需要配置哨兵端口等信息)

测试

首先需要在主机 Redis 的目录下新建 sentinel.conf 文件,文件内容如下

#                监控名称   host     port 得票数阈值
sentinel monitor host6379 127.0.0.1 6379 1

后面的这个数字 1,代表主机挂了后 slave 进行投票看让谁接替成为主机,得票数多少后(此处为 1 票)就会成为主机

启动哨兵:

redis-server sentinel.conf --sentinel

关闭主机服务后,哨兵将自动选取新的主机,哨兵日志如下:

可以看到,哨兵选择了 6380 为新的主机:

🚩 如果此时主机重新开启了,会归并到新的主机下,当做从机

Cluster模式

sentinel模式基本可以满足一般生产的需求,具备高可用性。但是当数据量过大到一台服务器存放不下的情况时,主从模式或sentinel模式就不能满足需求了,这个时候需要对存储的数据进行分片,将数据存储到多个Redis实例中。cluster模式的出现就是为了解决单机Redis容量有限的问题,将Redis的数据根据一定的规则分配到多台机器。

cluster集群特点:

  • 多个redis节点网络互联,数据共享,去中心化,连接哪个节点都可以获取和设置数据。
  • 所有的节点都是一主一从(也可以是一主多从),其中从不提供服务,仅作为备用
  • 不支持同时处理多个key(如MSET/MGET),因为redis需要把key均匀分布在各个节点上, 并发量很高的情况下同时创建key-value会降低性能并导致不可预测的行为
  • 支持在线增加、删除节点
  • 客户端可以连接任何一个主节点进行读写

**哈希槽:**Redis 集群没有使用一致性 hash,而是引入了哈希槽的概念, Redis 集群有 16384 个哈希槽,每个 key 通过 CRC16 校验后对 16384 取模来决定放置哪个槽,集群的每个节点负责一部分hash 槽。

环境搭建

三台机器,分别开启两个redis服务(端口)

192.168.30.128              端口:7001,7002
192.168.30.129              端口:7003,7004
192.168.30.130              端口:7005,7006

修改配置文件:

192.168.30.128

mkdir /usr/local/redis/cluster
cp /usr/local/redis/redis.conf /usr/local/redis/cluster/redis_7001.conf
cp /usr/local/redis/redis.conf /usr/local/redis/cluster/redis_7002.conf
chown -R redis:redis /usr/local/redis
mkdir -p /data/redis/cluster/{redis_7001,redis_7002} && chown -R redis:redis /data/redis
# vim /usr/local/redis/cluster/redis_7001.confbind 192.168.30.128
port 7001
daemonize yes
pidfile "/var/run/redis_7001.pid"
logfile "/usr/local/redis/cluster/redis_7001.log"
dir "/data/redis/cluster/redis_7001"
#replicaof 192.168.30.129 6379
masterauth 123456
requirepass 123456
appendonly yes
cluster-enabled yes
cluster-config-file nodes_7001.conf
cluster-node-timeout 15000
# vim /usr/local/redis/cluster/redis_7002.confbind 192.168.30.128
port 7002
daemonize yes
pidfile "/var/run/redis_7002.pid"
logfile "/usr/local/redis/cluster/redis_7002.log"
dir "/data/redis/cluster/redis_7002"
#replicaof 192.168.30.129 6379
masterauth "123456"
requirepass "123456"
appendonly yes
cluster-enabled yes
cluster-config-file nodes_7002.conf
cluster-node-timeout 15000

其它两台机器配置与192.168.30.128一致

启动redis服务:

redis-server /usr/local/redis/cluster/redis_7001.conf
tail -f /usr/local/redis/cluster/redis_7001.log
redis-server /usr/local/redis/cluster/redis_7002.conf
tail -f /usr/local/redis/cluster/redis_7002.logredis-cli -a 123456 --cluster create 192.168.30.128:7001 192.168.30.128:7002 192.168.30.129:7003 192.168.30.129:7004 192.168.30.130:7005 192.168.30.130:7006 --cluster-replicas 1

登录集群:

redis-cli -c -h 192.168.30.128 -p 7001 -a 123456                  # -c,使用集群方式登录

集群操作

# 查看集群信息
CLUSTER INFO                
# 集群中增加节点
CLUSTER MEET 192.168.30.129 7007
# 删除节点
CLUSTER FORGET 1a1c7f02fce87530bd5abdfc98df1cffce4f1767
# 更换节点身份
# 将新增的192.168.30.130:7008节点身份改为192.168.30.129:7007的slave
redis-cli -c -h 192.168.30.130 -p 7008 -a 123456 cluster replicate e51ab166bc0f33026887bcf8eba0dff3d5b0bf14
# 保存配置,将节点配置信息保存到硬盘
CLUSTER SAVECONFIG

🌁 缓存穿透和雪崩(高可用)

Redis缓存的使用,极大的提升了应用程序的性能和效率,特别是数据查询方面。

但同时,它也带来了一些问题。其中,最要害的问题,就是数据的一致性问题,从严格意义上讲,这个问题无解。如果对数据的一致性要求很高,那么就不能使用缓存。

另外的一些典型问题就是,缓存穿透、缓存雪崩和缓存击穿。

缓存穿透(查不到)

当用户很多的时候,缓存都没有命中,于是都去请求了持久层数据库。这会给持久层数据库造成很大的压力,这时候就出现了缓存穿透。

解决方案

Ⅰ 布隆过滤器

布隆过滤器是一种数据结构,对所有可能查询的参数以 hash 形式存储,在控制层先进行校验,不符合则丢弃,从而避免了对底层存储系统的查询压力

Ⅱ 缓存空对象

当存储层不命中后,即使返回的空对象也将其缓存起来,同时会设置一个过期时间,之后再访问这个数据将会从缓存中获取,保护了后端数据源

但是这种方法会存在两个问题:

  • 如果空值能够被缓存起来,这就意味着缓存需要更多的空间存储更多的键,因为这当中可能会有很多的空值的键;

  • 即使对空值设置了过期时间,还是会存在缓存层和存储层的数据会有一段时间窗口的不一致,这对于需要保持一致性的业务会有影响。

缓存击穿(查的太多)

缓存击穿,是指一个 key 非常热点,在不停的扛着大并发,大并发集中。对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,导使数据库瞬间压力过大。

解决方案

Ⅰ 设置热点数据永不过期

从缓存层面来看,不设置过期时间,就不会出现热点 key 过期后产生的问题。

Ⅱ 加互斥锁

使用分布式锁,保证对于每个 key 同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的权限,因此只需要等待即可。这种方式将高并发的压力转移到了分布式锁,因此对分布式锁的考验很大。

缓存雪崩

缓存雪崩,是指在某一个时间段,缓存集中过期失效,Redis 宕机。

其实缓存集中过期,倒不是非常致命,比较致命的缓存雪崩,是缓存服务器某个节点宕机或断网。因为自然形成的缓存雪崩,一定是在某个时间段集中创建缓存,这个时候,数据库也是可以顶住压力的。无非就是对数据库产生周期性的压力而已。而缓存服务节点的宕机,对数据库服务器造成的压力是不可预知的,很有可能瞬间就把数据库压垮。

解决方案

Ⅰ Redis 高可用

多增设几台 redis,搭建redis集群(异地多活)。

Ⅱ 限流降级

在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个 key 只允许一个线程查询数据和写缓存,其他线程等待。

Ⅲ 数据预热

数据加热的含义就是在正式部署之前,我先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中。在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。

查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. Pytorch环境搭建

    Pytorch环境搭建 这里写目录标题Pytorch环境搭建机器环境搭建步骤1.下载Anaconda32.下载Pytorch测试结果机器环境 系统&#xff1a;win11 显卡&#xff1a;NVIDIA GeForce RTX 3060 Laptop CUDA&#xff1a;11.4&#xff08;显卡搭配&#xff09; 查看显卡型号&#xff1a;电…...

    2024/4/19 19:56:05
  2. C语言-循环语句的一二事

    声明&#xff1a;因本人为非科班转码&#xff0c;个人水平有限&#xff0c;博客中难免有不准确之处&#xff0c;如有读者发现&#xff0c;本人恳请大家积极指出&#xff0c;本人也想知道自己的错误在哪里。欢迎大家一起指正&#xff0c;共同进步。 联系方式&#xff1a;314636…...

    2024/4/7 15:28:44
  3. 《Linux C++通信架构实战》第3章

    《Linux C通信架构实战》第3章终端和进程的关系信号基本概念kill进程状态常用信号信号处理的相关动作UNIX/Linux体系结构UNIX/Linux操作系统体系结构signal函数信号编程信号集&#xff08;信号屏蔽字&#xff09;信号相关函数sigprocmask等信号函数fork函数简单认识进一步认识完…...

    2024/5/3 12:01:58
  4. 油溶性/水溶性上转换纳米颗粒之间可以转换

    油溶性上转换荧光纳米材料的表面修饰方法,其特征在于:将油溶性上转换荧光纳米材料均匀分散在有机溶剂中,然后加入mPEG2K-b-PEBEP6K并搅拌,再加热去除有机溶剂,离心分离得沉淀烘干,即完成油溶性上转换荧光纳米材料的表面修饰,获得水溶性的上转换荧光纳米材料。油溶性上转换纳米颗…...

    2024/4/14 10:06:12
  5. 打破程序员“中年危机”,如何在35岁以后依然被大厂抢着要?

    程序员圈子里一直流传着一个魔咒&#xff1a;35 岁的中年危机。 他们说&#xff0c;35 岁程序员的精力和体力会无法避免地下降; 他们说&#xff0c;35 岁程序员的丰富经验没有年轻员工的创新活力吃香; 他们说&#xff0c;35 岁程序员会面临越来越窄的职业上升通道和日益复杂的社…...

    2024/4/14 14:41:13
  6. react基础学习(002-react面向组件编程)

    面向组件编程1. 创建函数式组件2. 创建类式组件1. 创建函数式组件 // 1. 创建函数式组件 function MyComponent () {console.log(this) // 此处的this是undefined&#xff0c;因为babel编译后开启了严格模式return <h2>我是用函数定义的组件</h2> // 2. 渲染组件…...

    2024/4/14 10:06:37
  7. 【linux基操】命令行

    vim的操作 创建一个文件 因为我是用python写exp&#xff0c;这里创建一个python文件 touch filename.py 对刚创建的文件用vim编辑 vim用法&#xff1a;可以在命令行输入vim --help查看帮助 用vim打开文件vim filename.py 在进入命令模式后&#xff0c;按a, i, o等键进入插入…...

    2024/4/14 18:08:47
  8. CF1559C Mocha and Hiking(图,思维,构造)

    原题链接 题意 输入一个数 nnn &#xff0c;代表有 n1n 1n1 个点&#xff0c;然后 1−(n−1)1 - (n - 1)1−(n−1)&#xff0c;每个点都有一条边连向自己后面那条边。 然后对于点 1−n1 - n1−n&#xff0c;到点 n1n 1n1, 有 nnn 个数 如果 ai0a_i 0ai​0 &#xff0c;代…...

    2024/4/14 10:06:22
  9. Codeforces Round #766 (Div. 2)

    A 思路 不难想到&#xff0c;答案只能为0&#xff0c;1&#xff0c;2…或者直接-1 Code #include <iostream> #include <cstdio> #include <algorithm>using namespace std;typedef long long ll;const int N 1e2 10;char G[N][N];void solve(){int r,c…...

    2024/4/14 10:06:27
  10. 洛谷 1724

    题目描述 在幻想乡&#xff0c;东风谷早苗是以高达控闻名的高中生宅巫女。某一天&#xff0c;早苗终于入手了最新款的钢达姆模型。作为最新的钢达姆&#xff0c;当然有了与以往不同的功能了&#xff0c;那就是它能够自动行走&#xff0c;厉害吧&#xff08;好吧&#xff0c;我…...

    2024/4/18 9:25:46
  11. 写一篇最好懂的HTTPS讲解,移动端h5开发

    也就是说&#xff0c;使用http传输数据至少存在着数据被监听以及数据被篡改这两大风险&#xff0c;因此http是一种不安全的传输协议。 那么解决方案大家肯定都知道是使用https&#xff0c;但是我们先尝试着自己思考一下该如何保证http传输的安全性&#xff0c;进而也就能一步步…...

    2024/4/14 10:06:22
  12. 日积月累

    今日学习 冯.诺依曼体系结构 常用的DOS命令 1.dir &#xff08;directory&#xff09;查看某个盘或文件下的目录&#xff08;所以文件及文件夹&#xff09;; ​ 格式&#xff1a;dir [盘符:][目录路径] [/p] [/s] [/w] [/o]; ​ 解释&#xff1a;([/p]- 分页显示目录内容&…...

    2024/4/14 10:06:47
  13. 18. IAB Considerations【IAB 注意事项】

    原文链接&#xff1a;https://datatracker.ietf.org/doc/html/rfc8445#section-18 18. IAB Considerations【IAB 注意事项】 The IAB has studied the problem of “Unilateral Self-Address Fixing” (UNSAF), which is the general process by which an ICE agent attempts…...

    2024/4/20 6:37:31
  14. Fiddler(一)

    Fiddler 1、什么是 Fiddler? Fiddler 是一个 HTTP 协议调试代理工具&#xff0c;它能够记录并检查所有你的电脑和互联网之间的 HTTP 通讯。Fiddler 提供了电脑端、移动端的抓包、包括 http 协议和 https 协议都可以捕获到报文并进行分析&#xff1b;可以设置断点调试、截取报…...

    2024/4/19 15:39:56
  15. .NET MCV Swagger 安装和配置

    Web Api Swagger发布 1.安装包 右键项目管理NuGet包 安装 Swashbuckle 包 2.修改配置文件 App_Start/SwaggerConfig 行号106 c.IncludeXmlComments(GetXmlCommentsPath(thisAssembly.GetName().Name)); 添加方法 protected static string GetXmlCommentsPath(string name)…...

    2024/4/14 10:06:47
  16. Redis集群架构

    Redis集群架构 Redis集群与哨兵比较 哨兵模式 在redis3.0之前&#xff0c;redis实现集群一般是借助哨兵sentinel工具来监控master节点的状态。当master节点异常&#xff0c;则会主从切换&#xff0c;将某一台slave作为master。哨兵的配置略微复杂&#xff0c;并且性能和高可…...

    2024/4/17 21:16:23
  17. 【Course 2 改善深度神经网络】Week 2 优化算法

    神经网络的学习的目的是找到使损失函数的值尽可能小的参数。这是寻找最优参数的问题&#xff0c;解决这个问题的过程称为最优化(optimization)。Optimization Algorithms这一节主要讲SGD、Momentum、RMSprop和Adam算法的实现及其优化原理。在实现过程上&#xff0c;有三点可以区…...

    2024/4/14 10:07:08
  18. .net core 多项目中使用EFCore

    类库一级项目使用.net core 3.1 框架 其中EFCore是和数据库交互的 MultiCore 注入EFCore中的DBContext与数据库交互 主要为了解决多项目中数据库迁移失败问题 EFCore 工程安装如下包 <Project Sdk"Microsoft.NET.Sdk"><PropertyGroup><TargetFra…...

    2024/4/18 0:23:21
  19. 阿里电话一面

    做个自我介绍介绍下项目项目中让你有成就感的部分StringBuffer的底层讲一下String和StringBuffer的差别&#xff0c;StringBuffer和StringBuilder的优化垃圾回收器mysql索引底层笔试算法&#xff1a; a. 按照索引奇偶重构索引 输入: 1->2->3->4->5 输出: 1->3-&…...

    2024/4/14 10:07:33
  20. 游戏仍是“瘸腿”、发行版过多反成“噩梦”?2022 年还不是 Linux 元年

    近年来&#xff0c;开源热潮席卷全球&#xff0c;“得开源者得生态,得开源者得天下”逐渐成为了信息产业的一种流行趋势。为此&#xff0c;诸多互联网大厂争相拥抱开源&#xff0c;Linux 作为一款开源操作系统也愈发受开发者喜爱&#xff0c;他们坚信&#xff1a;距离“Linux 桌…...

    2024/4/14 10:07:28

最新文章

  1. 《有限元分析及应用》《有限元分析基础教程》-曾攀-清华大学|pdf电子书+有限元分析及应用视频教程(全85讲) 曾攀、雷丽萍 ​​​+课件PPT

    专栏导读 作者简介&#xff1a;工学博士&#xff0c;高级工程师&#xff0c;专注于工业软件算法研究本文已收录于专栏&#xff1a;《有限元编程从入门到精通》本专栏旨在提供 1.以案例的形式讲解各类有限元问题的程序实现&#xff0c;并提供所有案例完整源码&#xff1b;2.单元…...

    2024/5/3 18:06:51
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/3/20 10:50:27
  3. 方案分享 | 嵌入式指纹方案

    随着智能设备的持续发展&#xff0c;指纹识别技术成为了现在智能终端市场和移动支付市场中占有率最高的生物识别技术。凭借高识别率、短耗时等优势&#xff0c;被广泛地运用在智能门锁、智能手机、智能家居等设备上。 我们推荐的品牌早已在2015年进入指纹识别应用领域&#xff…...

    2024/5/1 13:00:02
  4. Java深度优先搜索DFS(含面试大厂题和源码)

    深度优先搜索&#xff08;Depth-First Search&#xff0c;简称DFS&#xff09;是一种用于遍历或搜索树或图的算法。DFS 通过沿着树的深度来遍历节点&#xff0c;尽可能深地搜索树的分支。当节点v的所在边都已被探寻过&#xff0c;搜索将回溯到发现节点v的那条边的起始节点。这个…...

    2024/5/3 10:02:23
  5. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/5/1 17:30:59
  6. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/5/2 16:16:39
  7. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/4/29 2:29:43
  8. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/5/2 9:28:15
  9. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/4/27 17:58:04
  10. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/4/27 14:22:49
  11. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/4/28 1:28:33
  12. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/4/30 9:43:09
  13. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/4/27 17:59:30
  14. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/5/2 15:04:34
  15. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/4/28 1:34:08
  16. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/4/26 19:03:37
  17. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/4/29 20:46:55
  18. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/4/30 22:21:04
  19. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/5/1 4:32:01
  20. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/4/27 23:24:42
  21. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/4/28 5:48:52
  22. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/4/30 9:42:22
  23. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/5/2 9:07:46
  24. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/4/30 9:42:49
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  26. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  28. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  29. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  30. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  31. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  32. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  35. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  36. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  37. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  38. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  39. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  40. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  41. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  42. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  43. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  44. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57