redis整理完成、数据库复习完成。

This commit is contained in:
Estom
2021-09-03 10:46:40 +08:00
parent fc481667cf
commit 62309f856a
12 changed files with 354 additions and 1021 deletions

View File

@@ -9,7 +9,13 @@ Redis 是速度非常快的非关系型NoSQL内存键值数据库可以
Redis 支持很多特性,例如将内存中的数据持久化到硬盘中,使用复制来扩展读性能,使用分片来扩展写性能。
### 优势
使用Redis有哪些好处
1. 速度快因为数据存在内存中类似于HashMapHashMap的优势就是查找和操作的时间复杂度都是O(1)
2. 支持丰富数据类型支持stringlistsetsorted sethash
3. 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行(4) 丰富的特性可用于缓存消息按key设置过期时间过期后将会自动删除
## 2 Redis 与 Memcached

View File

@@ -1,7 +1,10 @@
# 底层数据结构
> 参考文献
> * [底层数据结构](https://www.cnblogs.com/ysocean/p/9080942.html#_label1)
- [底层数据结构](#底层数据结构)
- [1 简单动态字符串SDS](#1-简单动态字符串sds)
- [1 动态字符串SDS](#1-动态字符串sds)
- [SDS 代码实现](#sds-代码实现)
- [SDS 特点与C字符串的不同](#sds-特点与c字符串的不同)
- [SDS API](#sds-api)
@@ -13,7 +16,7 @@
- [字典的实现](#字典的实现)
- [哈希算法](#哈希算法)
- [解决键冲突](#解决键冲突)
- [rehash](#rehash)
- [Rehash](#rehash)
- [哈希表的扩展与收缩](#哈希表的扩展与收缩)
- [渐进式rehash](#渐进式rehash)
- [字典API](#字典api)
@@ -34,7 +37,7 @@
- [压缩列表API](#压缩列表api)
## 1 简单动态字符串SDS
## 1 动态字符串SDS
Redis构建了 简单动态字符串simple dynamic stringSDS来表示字符串值。
SDS还被用作缓冲区AOF缓冲区客户端状态中的输入缓冲区。
@@ -283,7 +286,7 @@ Redis使用链地址法解决键冲突每个哈希表节点都有个next指
![collision](img/chap4/collision.png)
### rehash
### Rehash
随着操作的不断执行哈希表保存的键值对会增加或减少。为了让哈希表的负载因子维持在合理范围需要对哈希表的大小进行扩展或收缩即通过执行rehash重新散列来完成
@@ -344,13 +347,19 @@ Redis使用跳跃表作为**有序集合键的底层实现**之一,如果有
在集群节点中,**跳跃表也被Redis用作内部数据结构**。
### 跳表 代码实现
![](image/2021-09-02-23-20-54.png)
Redis的跳跃表由redis.h/zskiplistNode和redis.h/zskiplist两个结构定义其中zskiplistNode代表跳跃表节点zskiplist保存跳跃表节点的相关信息比如节点数量、以及指向表头/表尾结点的指针等。
![](image/2021-09-02-23-20-54.png)
1. 由很多层结构组成;
2. 每一层都是一个有序的链表排列顺序为由高层到底层都至少包含两个链表节点分别是前面的head节点和后面的nil节点
3. 最底层的链表包含了所有的元素;
4. 如果一个元素出现在某一层的链表中,那么在该层之下的链表也全都会出现(上一层的元素是当前层的元素的子集);
5. 链表中的每个节点都包含两个指针,一个指向同一层的下一个链表节点,另一个指向下一层的同一个链表节点;
跳跃表是基于多指针有序链表实现的,可以看成多个有序链表。
![skiplist](img/chap5/skiplist.png)
跳跃表是基于多指针有序链表实现的,可以看成多个有序链表。
```c
typedef struct zskiplist {

View File

@@ -52,3 +52,70 @@ Redis服务器采用单个线程来处理命令请求所以将AOF重写程序
1. 将AOF重写缓冲区的内容写入新AOF文件。
2. 对新的AOF文件改名覆盖现有的AOF文件。
## 3 RDB和AOF区别
* RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘实际操作过程是fork一个子进程先将数据集写入临时文件写入成功后再替换之前的文件用二进制压缩存储。
![](image/2021-04-07-23-24-02.png)
* AOF持久化以日志的形式记录服务器所处理的每一个写、删除操作查询操作不会记录以文本的方式记录可以打开文件看到详细的操作记录。
![](image/2021-04-07-23-24-24.png)
## 3 二者优缺点
### RDB存在哪些优势呢
1. 一旦采用该方式那么你的整个Redis数据库将只包含一个文件这对于文件备份而言是非常完美的。比如你可能打算每个小时归档一次最近24小时的数据同时还要每天归档一次最近30天的数据。通过这样的备份策略一旦系统出现灾难性故障我们可以非常容易的进行恢复。
2. 对于灾难恢复而言RDB是非常不错的选择。因为我们可以非常轻松的将一个单独的文件压缩后再转移到其它存储介质上。
3. 性能最大化。对于Redis的服务进程而言在开始持久化时它唯一需要做的只是fork出子进程之后再由子进程完成这些持久化的工作这样就可以极大的避免服务进程执行IO操作了。
4. 相比于AOF机制如果数据集很大RDB的启动效率会更高。
### RDB又存在哪些劣势呢
1. 如果你想保证数据的高可用性即最大限度的避免数据丢失那么RDB将不是一个很好的选择。因为系统一旦在定时持久化之前出现宕机现象此前没有来得及写入磁盘的数据都将丢失。
2. 由于RDB是通过fork子进程来协助完成数据持久化工作的因此如果当数据集较大时可能会导致整个服务器停止服务几百毫秒甚至是1秒钟。
### AOF的优势有哪些呢
1. 该机制可以带来更高的数据安全性即数据持久性。Redis中提供了3中同步策略即每秒同步、每修改同步和不同步。事实上每秒同步也是异步完成的其效率也是非常高的所差的是一旦系统出现宕机现象那么这一秒钟之内修改的数据将会丢失。而每修改同步我们可以将其视为同步持久化即每次发生的数据变化都会被立即记录到磁盘中。可以预见这种方式在效率上是最低的。至于无同步无需多言我想大家都能正确的理解它。
2. 由于该机制对日志文件的写入操作采用的是append模式因此在写入过程中即使出现宕机现象也不会破坏日志文件中已经存在的内容。然而如果我们本次操作只是写入了一半数据就出现了系统崩溃问题不用担心在Redis下一次启动之前我们可以通过redis-check-aof工具来帮助我们解决数据一致性的问题。
3. 如果日志过大Redis可以自动启用rewrite机制。即Redis以append模式不断的将修改数据写入到老的磁盘文件中同时Redis还会创建一个新的文件用于记录此期间有哪些修改命令被执行。因此在进行rewrite切换时可以更好的保证数据安全性。
4. AOF包含一个格式清晰、易于理解的日志文件用于记录所有的修改操作。事实上我们也可以通过该文件完成数据的重建。
### AOF的劣势有哪些呢
1. 对于相同数量的数据集而言AOF文件通常要大于RDB文件。RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。
2. 根据同步策略的不同AOF在运行效率上往往会慢于RDB。总之每秒同步策略的效率是比较高的同步禁用策略的效率和RDB一样高效。
### 二者选择的标准
就是看系统是愿意牺牲一些性能换取更高的缓存一致性aof还是愿意写操作频繁的时候不启用备份来换取更高的性能待手动运行save的时候再做备份rdb。rdb这个就更有些 eventually consistent的意思了。
## 4 常用配置
### RDB持久化配置
* Redis会将数据集的快照dump到dump.rdb文件中。此外我们也可以通过配置文件来修改Redis服务器dump快照的频率在打开6379.conf文件之后我们搜索save可以看到下面的配置信息
```
save 900 1 #在900秒(15分钟)之后如果至少有1个key发生变化则dump内存快照。
save 300 10 #在300秒(5分钟)之后如果至少有10个key发生变化则dump内存快照。
save 60 10000 #在60秒(1分钟)之后如果至少有10000个key发生变化则dump内存快照
```
### AOF持久化配置
* 在Redis的配置文件中存在三种同步方式它们分别是
```
appendfsync always #每次有数据修改发生时都会写入AOF文件。
appendfsync everysec #每秒钟同步一次该策略为AOF的缺省策略。
appendfsync no #从不同步。高效但是数据不会被持久化。
```

View File

@@ -116,6 +116,48 @@ typedef struct redisDb {
服务器收到`EXEC`命令后,根据这个客户端是否打开了`REDIS_DIRTY_CAS`标识来决定是否执行事务。
### 悲观锁与乐观锁
我正在买票`ticket -1 , money -100`而票只有1张, 如果在我multi之后,和exec之前, 票被别人买了,即ticket变成0了.我该如何观察这种情景,并不再提交
悲观的想法:
世界充满危险,肯定有人和我抢, 给 ticket上锁, 只有我能操作. [悲观锁]
乐观的想法:
没有那么人和我抢,因此,我只需要注意,
--有没有人更改ticket的值就可以了 [乐观锁]
Redis的事务中,启用的是乐观锁,只负责监测key没有被改动
```sh
具体的命令---- watch命令
redis 127.0.0.1:6379> watch ticket
OK
redis 127.0.0.1:6379> multi
OK
redis 127.0.0.1:6379> decr ticket
QUEUED
redis 127.0.0.1:6379> decrby money 100
QUEUED
redis 127.0.0.1:6379> exec
(nil) // 返回nil,说明监视的ticket已经改变了,事务就取消了.
redis 127.0.0.1:6379> get ticket
"0"
redis 127.0.0.1:6379> get money
"200"
watch key1 key2 ... keyN
作用:监听key1 key2..keyN有没有变化,如果有变, 则事务取消
unwatch
作用: 取消所有watch监听
```
## 3 事务的ACID性质
Redis的事务总是具有原子性atomicity、一致性consistency、隔离性isolation且当Redis运行在某种特定的持久化模式下事务也具有耐久性durability

View File

@@ -158,4 +158,68 @@ typdef struct redisClient {
随着负载不断上升,主服务器可能无法很快地更新所有从服务器,或者重新连接和重新同步从服务器将导致系统超载。为了解决这个问题,可以创建一个中间层来分担主服务器的复制工作。中间层的服务器是最上层服务器的从服务器,又是最下层服务器的主服务器。
![](image/2021-09-03-00-42-41.png)
![](image/2021-09-03-00-42-41.png)
## 5 docker部署主从服务器
> 参考文献
> * [docker实现redis主从](https://github.com/OMGZui/redis_m_s)
### 命令行模式
```bash
# 拉取redis
docker pull redis
# 主
docker run -v $(pwd)/master/redis.conf:/usr/local/etc/redis/redis.conf --name redis-master redis redis-server /usr/local/etc/redis/redis.conf
# 从1 --link redis-master:master master是别名
docker run -v $(pwd)/slave1/redis.conf:/usr/local/etc/redis/redis.conf --name redis-slave1 --link redis-master:master redis redis-server /usr/local/etc/redis/redis.conf
# 从2
docker run -v $(pwd)/slave2/redis.conf:/usr/local/etc/redis/redis.conf --name redis-slave2 --link redis-master:master redis redis-server /usr/local/etc/redis/redis.conf
```
### docker-compose模式 推荐
```bash
# 拉取redis
docker pull redis
# 目录
├── docker-compose.yml
├── master
│   ├── Dockerfile
│   └── redis.conf
├── redis.conf
├── slave1
│   ├── Dockerfile
│   └── redis.conf
└── slave2
├── Dockerfile
└── redis.conf
# 启动
docker-compose up -d master slave1 slave2
# 查看主容器
docker-compose exec master bash
root@cab5db8d544b:/data# redis-cli
127.0.0.1:6379> info Replication
# Replication
role:master
connected_slaves:2
slave0:ip=172.23.0.3,port=6379,state=online,offset=1043,lag=0
slave1:ip=172.23.0.4,port=6379,state=online,offset=1043,lag=0
master_replid:995257c6b5ac62f7908cc2c7bb770f2f17b60401
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1043
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:1043
```

View File

@@ -0,0 +1,58 @@
## 1 缓存使用
### 缓存预热
缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
解决方法:
1、直接写个缓存刷新页面上线时手工操作下
2、数据量不大可以在项目启动的时候自动进行加载
3、定时刷新缓存
### 缓存降级
当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。
降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。
在进行降级之前要对系统进行梳理,看看系统是不是可以丢卒保帅;从而梳理出哪些必须誓死保护,哪些可降级
## 2 Redis作为缓存可能会出现的问题
### 缓存雪崩
问题当我们的缓存失效或者redis挂了那么这个时候的请求都会直接走数据库就会给数据库造成极大的压力导致数据库也挂了
解决:
1. 对缓存设置不同的过期时间,这样就不会导致缓存同时失效
2. 建立redis集群保证服务的可靠性
### 缓存穿透
问题:当有大量用户不走我们设置的键值,就会直接走数据库,就会给数据库造成极大的压力,导致数据库也挂了
解决:
1. 参数过滤和提醒,引导用户走我们的设置的键值
2. 对不合法的参数进行空对象缓存,并设置较短的过期时间
### 缓存与数据库读写一致
问题:如果一直是读的话,是没问题的,但是更新操作会导致数据库已经更新了,缓存还是旧的数据
解决:
并发下解决数据库与缓存不一致的思路:将删除缓存、修改数据库、读取缓存等的操作积压到队列里边,实现串行化。
- 先删除缓存,再更新数据库。在高并发下表现不如意,在原子性被破坏时表现优异
- 先更新数据库,再删除缓存(Cache Aside Pattern设计模式)。在高并发下表现优异,在原子性被破坏时表现不如意

View File

@@ -1,795 +0,0 @@
# redis笔记
<!-- TOC -->
- [redis笔记](#redis笔记)
- [零、redis是什么](#零redis是什么)
- [一、redis与memcached比较](#一redis与memcached比较)
- [二、安装](#二安装)
- [三、配置](#三配置)
- [四、通用key操作](#四通用key操作)
- [五、redis中的5中数据结构](#五redis中的5中数据结构)
- [1. 字符串string](#1-字符串string)
- [2. 列表list链表支持 有序 可重复](#2-列表list链表支持-有序-可重复)
- [3. 集合set无序 不可重复](#3-集合set无序-不可重复)
- [4. 哈希hash键值对 key => value](#4-哈希hash键值对--key--value)
- [5. 有序集合zset键值对 成员 => 分值 成员必须唯一](#5-有序集合zset键值对--成员--分值-成员必须唯一)
- [六、Redis详细数据类型](#六redis详细数据类型)
- [1、string 字符串](#1string-字符串)
- [2、list 列表](#2list-列表)
- [3、set 集合](#3set-集合)
- [4、sorted set 有序集合](#4sorted-set-有序集合)
- [5、hash 哈希](#5hash-哈希)
- [6、bitmap 位图](#6bitmap-位图)
- [7、geo 地理位置类型](#7geo 地理位置类型)
- [8、hyperLogLog 基数统计](#8hyperloglog-基数统计)
- [七、redis事务](#七redis事务)
- [1、mysql事务与redis事务比较](#1mysql事务与redis事务比较)
- [2、悲观锁与乐观锁](#2悲观锁与乐观锁)
- [八、发布订阅](#八发布订阅)
- [九、持久化](#九持久化)
- [1、redis 快照rdb](#1redis-快照rdb)
- [2、redis 日志aof](#2redis-日志aof)
- [十、redis主从复制](#十redis主从复制)
- [十一、redis表设计](#十一redis表设计)
- [十二、面试](#十二面试)
- [1、缓存雪崩](#1缓存雪崩)
- [2、缓存穿透](#2缓存穿透)
- [3、缓存与数据库读写一致](#3缓存与数据库读写一致)
- [十三、docker实现redis主从](#十三docker实现redis主从)
- [1、命令行模式](#1命令行模式)
- [2、docker-compose模式 推荐](#2docker-compose模式-推荐)
- [十四、参考资料](#十四参考资料)
<!-- /TOC -->
## 零、redis是什么
redis是什么是一种非关系型数据库统称nosql。
## 一、redis与memcached比较
- 1、redis受益于“持久化”可以做存储(storge)memcached只能做缓存(cache)
- 2、redis有多种数据结构memcached只有一种类型`字符串(string)`
## 二、安装
安装最新稳定版
```sh
# 源码安装redis-4.0
# 下载
wget http://download.redis.io/releases/redis-4.0.1.tar.gz
# 解压
tar zxvf redis-4.0.1.tar.gz
cd redis-4.0.1
# 编译
make && make install
/usr/local/bin/redis-server -v
```
## 三、配置
- redis-benchmark redis性能测试工具
- redis-check-aof 检查aof日志的工具
- redis-check-rdb 检查rdb日志的工具
- redis-cli 连接用的客户端
- redis-server 服务进程
```sh
# 地址
bind 0.0.0.0
# 保护模式
protected-mode no
# 端口
port 6380
tcp-backlog 511
timeout 0
tcp-keepalive 300
# 守护进程模式
daemonize yes
supervised no
# 进程id文件
pidfile /usr/local/redis/run/redis.pid
# 日志等级
loglevel notice
# 日志位置
logfile /usr/local/redis/logs/redis.log
# 数据个数
databases 16
always-show-logo yes
# after 900 sec (15 min) if at least 1 key changed
# after 300 sec (5 min) if at least 10 keys changed
# after 60 sec if at least 10000 keys changed
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
# rdb开启
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
rdb-del-sync-files no
dir ./
# 主从
# +------------------+ +---------------+
# | Master | ---> | Replica |
# | (receive writes) | | (exact copy) |
# +------------------+ +---------------+
acllog-max-len 128
# 密码
requirepass omgzui
lazyfree-lazy-eviction no
lazyfree-lazy-expire no
lazyfree-lazy-server-del no
replica-lazy-flush no
lazyfree-lazy-user-del no
oom-score-adj no
oom-score-adj-values 0 200 800
# aof
appendonly yes
appendfilename "appendonly.aof"
# appendfsync always
appendfsync everysec
# appendfsync no
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
aof-use-rdb-preamble yes
lua-time-limit 5000
# 从服务器
# cluster-announce-ip 10.1.1.5
# cluster-announce-port 6379
# cluster-announce-bus-port 6380
slowlog-log-slower-than 10000
slowlog-max-len 128
latency-monitor-threshold 0
notify-keyspace-events ""
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-size -2
list-compress-depth 0
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
hll-sparse-max-bytes 3000
stream-node-max-bytes 4096
stream-node-max-entries 100
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
dynamic-hz yes
aof-rewrite-incremental-fsync yes
rdb-save-incremental-fsync yes
jemalloc-bg-thread yes
```
## 四、通用key操作
1. keys 查询
```sh
在redis里,允许模糊查询key
有3个通配符 - ? []
-: 通配任意多个字符
?: 通配单个字符
[]: 通配括号内的某1个字符
```
1. keys 查询
2. del 删除
3. rename 重命名
4. move 移到另外一个库
5. randomkey 随机
6. exists 存在
7. type 类型
8. ttl 剩余生命周期
9. expire 设置生命周期
10. persist 永久有效
11. flushdb 清空
## 五、redis中的5中数据结构
### 1. 字符串string
- set
- set name shengj -> OK
- get
- get name -> "shengj"
- del
- del name -> (integer) 1
- get name -> (nil)
- mset
- mset name shengj age 23 sex male -> OK
- mget
- mget age sex
```sh
1) "23"
2) "male"
```
- setrange
- setrange sex 2 1 将sex的第3个字符改成1 -> (integer) 4
- get sex -> "ma1e"
- append
- append name GG -> (integer) 8
- get name -> "shengjGG"
- getrange
- getrange name 1 2 -> "he"
- incr 自增
- incrby 自增一个量级
- incrbyfloat 自增一个浮点数
- decr 递减
- decrby 递减一个量级
- decrbyfloat 递减一个浮点数
- setbit 设置二进制位数
- getbit 获取二进制表示
- bitop 位操作
---
### 2. 列表list链表支持 有序 可重复
- rpush 右边插入
- rpush list item1 -> (integer) 1
- rpush list item2 -> (integer) 2
- rpush list item3 -> (integer) 3
- lrange 列出链表值
- lrange list 0 -1
```sh
1) "item1"
2) "item2"
3) "item3"
```
- lindex
- lindex list 1 -> "item2"
- lpop
- lpop list -> "item1"
- lrange list 0 -1
```sh
1) "item2"
2) "item3"
```
- ltrim
- ltrim list 3 0 -> OK
- lrange list 0 -1 -> (empty list or set)
- lpush 左边插入
- rpop 右边删除
- lrem
---
### 3. 集合set无序 不可重复
- sadd 增加
- sadd set item1 -> (integer) 1
- sadd set item2 -> (integer) 1
- sadd set item3 -> (integer) 1
- sadd set item1 -> (integer) 0 已存在
- smembers 所有集合元素
- smembers set
```sh
1) "item3"
2) "item2"
3) "item1"
```
- sismember 存不存在
- sismember set item1 -> (integer) 1
- sismember set item -> (integer) 0 不存在
- srem 移除元素
- srem set item1 -> (integer) 1
- smembers set
```sh
1) "item3"
2) "item2"
```
- spop 随机删除一个元素
- srandmember 随机获取一个元素 -> 抽奖
- scard 多少个元素
- smove 移动
- sinter 交集
- sinterstore 交集并赋值
- suion 并集
- sdiff 差集
---
### 4. 哈希hash键值对 key => value
- hset 设置一个
- hset hash key1 value1 -> (integer) 1
- hset hash key2 value2 -> (integer) 1
- hset hash key3 value3 -> (integer) 1
- hset hash key1 value1 -> (integer) 0 已存在
- hgetall 获取全部
- hgetall hash
```sh
1) "key1"
2) "value1"
3) "key2"
4) "value2"
5) "key3"
6) "value3"
```
- hget 获取一个
- hget hash key1 -> "value1"
- hdel 删除
- hdel hash key1 -> (integer) 1
- hgetall hash
```sh
1) "key2"
2) "value2"
3) "key3"
4) "value3"
```
- hmset 设置多个
- hmget 获取多个
- hlen 个数
- hexists 是否存在增长
- hinrby 增长
- hkeys 所有的key
- hvals 所有的值
---
### 5. 有序集合zset键值对 成员 => 分值 成员必须唯一
- zadd 增加
- zadd zset 100 item1 -> (integer) 1
- zadd zset 200 item2 -> (integer) 1
- zadd zset 300 item3 -> (integer) 1
- zadd zset 100 item1 -> (integer) 0 已存在
- zrange 按分值排序
- zrange zset 0 -1 withscores
```sh
1) "item1"
2) "100"
3) "item2"
4) "200"
5) "item3"
6) "300"
```
- zrangebyscore 按分值的一部分排序
- zrangebyscore zset 0 200 withscores
```sh
1) "item1"
2) "100"
3) "item2"
4) "200"
```
- zrem 删除
- zrem zset item1 -> (integer) 1
- zrange zset 0 -1 withscores
```sh
1) "item2"
2) "200"
3) "item3"
4) "300"
```
- zrank 排名升序
- zremrangebyscore 按分值删除一部分
- zremrangebyrank 按排名删除一部分
- zcard 个数
## 六、Redis详细数据类型
首先来看一下 Redis 的核心数据类型。Redis  8 种核心数据类型分别是 
- string 字符串类型
- list 列表类型
- set 集合类型
- sorted set 有序集合类型
- hash 类型
- bitmap 位图类型
- geo 地理位置类型
- HyperLogLog 基数统计类型。
### 1、string 字符串
string  Redis 的最基本数据类型。可以把它理解为 Mc  key 对应的 value 类型。string 类型是二进制安全的 string 中可以包含任何数据。
Redis 中的普通 string 采用 raw encoding 即原始编码方式该编码方式会动态扩容并通过提前预分配冗余空间来减少内存频繁分配的开销。
在字符串长度小于 1MB 按所需长度的 2 倍来分配超过 1MB则按照每次额外增加 1MB 的容量来预分配。
Redis 中的数字也存为 string 类型但编码方式跟普通 string 不同数字采用整型编码字符串内容直接设为整数值的二进制字节序列。
在存储普通字符串序列化对象以及计数器等场景时都可以使用 Redis 的字符串类型字符串数据类型对应使用的指令包括 set、get、mset、incr、decr 等。
### 2、list 列表
Redis  list 列表是一个快速双向链表存储了一系列的 string 类型的字串值。list 中的元素按照插入顺序排列。插入元素的方式可以通过 lpush 将一个或多个元素插入到列表的头部也可以通过 rpush 将一个或多个元素插入到队列尾部还可以通过 lset、linsert 将元素插入到指定位置或指定元素的前后。
list 列表的获取可以通过 lpop、rpop 从对头或队尾弹出元素如果队列为空则返回 nil。还可以通过 Blpop、Brpop 从队头/队尾阻塞式弹出元素如果 list 列表为空没有元素可供弹出则持续阻塞直到有其他 client 插入新的元素。这里阻塞弹出元素可以设置过期时间避免无限期等待。最后list 列表还可以通过 LrangeR 获取队列内指定范围内的所有元素。Redis list 列表的偏移位置都是基于 0 的下标即列表第一个元素的下标是 0第二个是 1。偏移量也可以是负数倒数第一个是 -1倒数第二个是 -2依次类推。
list 列表对于常规的 pop、push 元素性能很高时间复杂度为 O(1),因为是列表直接追加或弹出。但对于通过随机插入、随机删除,以及随机范围获取,需要轮询列表确定位置,性能就比较低下了。
feed timeline 存储时由于 feed id 一般是递增的可以直接存为 list用户发表新 feed就直接追加到队尾。另外消息队列、热门 feed 等业务场景都可以使用 list 数据结构。
操作 list 列表时可以用 lpush、lpop、rpush、rpop、lrange 来进行常规的队列进出及范围获取操作在某些特殊场景下也可以用 lset、linsert 进行随机插入操作 lrem 进行指定元素删除操作最后在消息列表的消费时还可以用 Blpop、Brpop 进行阻塞式获取从而在列表暂时没有元素时可以安静的等待新元素的插入而不需要额外持续的查询。
### 3、set 集合
set  string 类型的无序集合set 中的元素是唯一的 set 中不会出现重复的元素。Redis 中的集合一般是通过 dict 哈希表实现的所以插入、删除以及查询元素可以根据元素 hash 值直接定位时间复杂度为 O(1)。
 set 类型数据的操作除了常规的添加、删除、查找元素外还可以用以下指令对 set 进行操作。
sismember 指令判断该 key 对应的 set 数据结构中是否存在某个元素如果存在返回 1否则返回 0
sdiff 指令来对多个 set 集合执行差集
sinter 指令对多个集合执行交集
sunion 指令对多个集合执行并集
spop 指令弹出一个随机元素
srandmember 指令返回一个或多个随机元素。
set 集合的特点是查找、插入、删除特别高效时间复杂度为 O(1)所以在社交系统中可以用于存储关注的好友列表用来判断是否关注还可以用来做好友推荐使用。另外还可以利用 set 的唯一性来对服务的来源业务、来源 IP 进行精确统计。
### 4、sorted set 有序集合
Redis 中的 sorted set 有序集合也称为 zset有序集合同 set 集合类似也是 string 类型元素的集合且所有元素不允许重复。
但有序集合中每个元素都会关联一个 double 类型的 score 分数值。有序集合通过这个 score 值进行由小到大的排序。有序集合中元素不允许重复 score 分数值却允许重复。
有序集合除了常规的添加、删除、查找元素外,还可以通过以下指令对 sorted set 进行操作。
zscan 指令按顺序获取有序集合中的元素
zscore 指令获取元素的 score 
zrange指令通过指定 score 返回指定 score 范围内的元素;
在某个元素的 score 值发生变更时还可以通过 zincrby 指令对该元素的 score 值进行加减。
通过 zinterstore、zunionstore 指令对多个有序集合进行取交集和并集然后将新的有序集合存到一个新的 key 如果有重复元素重复元素的 score 进行相加然后作为新集合中该元素的 score 值。
sorted set 有序集合的特点是
所有元素按 score 排序而且不重复
查找、插入、删除非常高效时间复杂度为 O(1)。
因此可以用有序集合来统计排行榜实时刷新榜单还可以用来记录学生成绩从而轻松获取某个成绩范围内的学生名单还可以用来对系统统计增加权重值从而在 dashboard 实时展示。
### 5、hash 哈希
Redis 中的哈希实际是 field  value 的一个映射表。
hash 数据结构的特点是在单个 key 对应的哈希结构内部可以记录多个键值对 field  value value 可以是任何字符串。而且这些键值对查询和修改很高效。
所以可以用 hash 来存储具有多个元素的复杂对象然后分别修改或获取这些元素。hash 结构中的一些重要指令包括hmset、hmget、hexists、hgetall、hincrby 等。
hmset 指令批量插入多个 field、value 映射;
hmget 指令获取多个 field 对应的 value 值;
hexists 指令判断某个 field 是否存在;
如果 field 对应的 value 是整数,还可以用 hincrby 来对该 value 进行修改。
### 6、bitmap 位图
Redis 中的 bitmap 位图是一串连续的二进制数字底层实际是基于 string 进行封装存储的 bit 位进行指令操作的。bitmap 中每一 bit 位所在的位置就是 offset 偏移可以用 setbit、bitfield 对 bitmap 中每个 bit 进行置 0 或置 1 操作也可以用 bitcount 来统计 bitmap 中的被置 1  bit 还可以用 bitop 来对多个 bitmap 进行求与、或、异或等操作。
bitmap 位图的特点是按位设置、求与、求或等操作很高效而且存储成本非常低用来存对象标签属性的话一个 bit 即可存一个标签。可以用 bitmap存用户最近 N 天的登录情况每天用 1 bit登录则置 1。个性推荐在社交应用中非常重要可以对新闻、feed 设置一系列标签如军事、娱乐、视频、图片、文字等 bitmap 来存储这些标签在对应标签 bit 位上置 1。对用户也可以采用类似方式记录用户的多种属性并可以很方便的根据标签来进行多维度统计。bitmap 位图的重要指令包括setbit、 getbit、bitcount、bitfield、 bitop、bitpos 等。
### 7、geo 地理位置类型
在移动社交时代LBS 应用越来越多比如微信、陌陌中附近的人美团、大众点评中附近的美食、电影院滴滴、优步中附近的专车等。要实现这些功能就得使用地理位置信息进行搜索。地球的地理位置是使用二维的经纬度进行表示的我们只要确定一个点的经纬度就可以确认它在地球的位置。
Redis  3.2 版本之后增加了对 GEO 地理位置的处理功能。Redis  GEO 地理位置本质上是基于 sorted set 封装实现的。在存储分类 key 下的地理位置信息时需要对该分类 key 构建一个 sorted set 作为内部存储结构用于存储一系列位置点。
在存储某个位置点时首先利用 Geohash 算法将该位置二维的经纬度映射编码成一维的 52 位整数值将位置名称、经纬度编码 score 作为键值对存储到分类 key 对应的 sorted set 中。
需要计算某个位置点 A 附近的人时首先以指定位置 A 为中心点以距离作为半径算出 GEO 哈希 8 个方位的范围 然后依次轮询方位范围内的所有位置点只要这些位置点到中心位置 A 的距离在要求距离范围内就是目标位置点。轮询完所有范围内的位置点后重新排序即得到位置点 A 附近的所有目标。
使用 geoadd将位置名称如人、车辆、店名与对应的地理位置信息添加到指定的位置分类 key 
使用 geopos 方便地查询某个名称所在的位置信息
使用 georadius 获取指定位置附近不超过指定距离的所有元素
使用 geodist 来获取指定的两个位置之间的距离。
这样,是不是就可以实现,找到附近的餐厅,算出当前位置到对应餐厅的距离,这样的功能了?
Redis GEO 地理位置利用 Geohash 将大量的二维经纬度转一维的整数值这样可以方便的对地理位置进行查询、距离测量、范围搜索。但由于地理位置点非常多一个地理分类 key 下可能会有大量元素 GEO 设计时需要提前进行规划避免单 key 过度膨胀。
Redis  GEO 地理位置数据结构应用场景很多比如查询某个地方的具体位置查当前位置到目的地的距离查附近的人、餐厅、电影院等。GEO 地理位置数据结构中重要指令包括 geoadd、geopos、geodist、georadius、georadiusbymember 等。
### 8、hyperLogLog 基数统计
Redis  hyperLogLog 是用来做基数统计的数据类型当输入巨大数量的元素做统计时只需要很小的内存即可完成。HyperLogLog 不保存元数据只记录待统计元素的估算数量这个估算数量是一个带有 0.81% 标准差的近似值在大多数业务场景对海量数据不足 1% 的误差是可以接受的。
Redis  HyperLogLog 在统计时如果计数数量不大采用稀疏矩阵存储随着计数的增加稀疏矩阵占用的空间也会逐渐增加当超过阀值后则改为稠密矩阵稠密矩阵占用的空间是固定的约为12KB字节。
通过 hyperLoglog 数据类型你可以利用 pfadd 向基数统计中增加新的元素可以用 pfcount 获得 hyperLogLog 结构中存储的近似基数数量还可以用 hypermerge 将多个 hyperLogLog 合并为一个 hyperLogLog 结构从而可以方便的获取合并后的基数数量。
hyperLogLog 的特点是统计过程不记录独立元素占用内存非常少非常适合统计海量数据。在大中型系统中统计每日、每月的 UV 即独立访客数或者统计海量用户搜索的独立词条数都可以用 hyperLogLog 数据类型来进行处理。
## 七、redis事务
### 1、mysql事务与redis事务比较
|比较|mysql|redis|
|---|---|---|
|开启|start transaction|multi|
|语句|普通sql语句|普通redis命令|
|失败|rollback|discard|
|成功|commit|exec|
如果已经成功执行了2条语句, 第3条语句出错.
rollback后,前2条的语句影响消失.
discard只是结束本次事务,前2条语句造成的影响仍然还在
### 2、悲观锁与乐观锁
我正在买票`ticket -1 , money -100`而票只有1张, 如果在我multi之后,和exec之前, 票被别人买了,即ticket变成0了.我该如何观察这种情景,并不再提交
悲观的想法:
世界充满危险,肯定有人和我抢, 给 ticket上锁, 只有我能操作. [悲观锁]
乐观的想法:
没有那么人和我抢,因此,我只需要注意,
--有没有人更改ticket的值就可以了 [乐观锁]
Redis的事务中,启用的是乐观锁,只负责监测key没有被改动
```sh
具体的命令---- watch命令
redis 127.0.0.1:6379> watch ticket
OK
redis 127.0.0.1:6379> multi
OK
redis 127.0.0.1:6379> decr ticket
QUEUED
redis 127.0.0.1:6379> decrby money 100
QUEUED
redis 127.0.0.1:6379> exec
(nil) // 返回nil,说明监视的ticket已经改变了,事务就取消了.
redis 127.0.0.1:6379> get ticket
"0"
redis 127.0.0.1:6379> get money
"200"
watch key1 key2 ... keyN
作用:监听key1 key2..keyN有没有变化,如果有变, 则事务取消
unwatch
作用: 取消所有watch监听
```
## 八、发布订阅
订阅端: subscribe 频道名称
发布端: publish 频道名称 发布内容
## 九、持久化
### 1、redis 快照rdb
有限制,还是容易数据丢失,恢复快
```sh
save 900 1 # 900内,有1条写入,则产生快照
save 300 1000 # 如果300秒内有1000次写入,则产生快照
save 60 10000 # 如果60秒内有10000次写入,则产生快照
(这3个选项都屏蔽,则rdb禁用)
stop-writes-on-bgsave-error yes # 后台备份进程出错时,主进程停不停止写入?
rdbcompression yes # 导出的rdb文件是否压缩
Rdbchecksum yes # 导入rbd恢复时数据时,要不要检验rdb的完整性
dbfilename dump.rdb # 导出来的rdb文件名
dir ./ //rdb的放置路径
```
### 2、redis 日志aof
```sh
appendonly no # 是否打开 aof日志功能
appendfsync always # 每1个命令,都立即同步到aof. 安全,速度慢
appendfsync everysec # 折衷方案,每秒写1次
appendfsync no # 写入工作交给操作系统,由操作系统判断缓冲区大小,统一写入到aof. 同步频率低,速度快,
no-appendfsync-on-rewrite yes: # 正在导出rdb快照的过程中,要不要停止同步aof
auto-aof-rewrite-percentage 100 #aof文件大小比起上次重写时的大小,增长率100%时,重写
auto-aof-rewrite-min-size 64mb #aof文件,至少超过64M时,重写
```
注: 在dump rdb过程中,aof如果停止同步,会不会丢失?
答: 不会,所有的操作缓存在内存的队列里, dump完成后,统一操作.
注: aof重写是指什么?
答: aof重写是指把内存中的数据,逆化成命令,写入到.aof日志里.以解决 aof日志过大的问题.
问: 如果rdb文件,和aof文件都存在,优先用谁来恢复数据?
答: aof
问: 2种是否可以同时用?
答: 可以,而且推荐这么做
问: 恢复时rdb和aof哪个恢复的快
答: rdb快,因为其是数据的内存映射,直 接载入到内存,而aof是命令,需要逐条执行
## 十、redis主从复制
```sh
Master配置:
1:关闭rdb快照(备份工作交给slave)
2:可以开启aof
slave配置:
1: 声明slave-of
2: 配置密码[如果master有密码]
3: [某1个]slave打开 rdb快照功能
4: 配置是否只读[slave-read-only]
```
## 十一、redis表设计
主键表
|列名|操作|备注|
|--|--|--|
|global:user_id|incr|全局user_id|
|global:post_id|incr|全局post_id|
---
mysql用户表
|列名|操作|备注||
|--|--|--|--|
|user_id|user_name|password|authsecret|
|1|shengj|123456|,./!@#|
redis用户表
|列名|操作|备注||
|--|--|--|--|
|user:user_id|user:user_id:*:user_name|user:user_id:*:password|user:user_id:*:authsecret|
|1|shengj|123456|,./!@#|
---
mysql发送表
|列名|操作|备注|||
|--|--|--|--|--|
|post_id|user_id|user_name|time|content|
|1|1|shengj|1370987654|测试内容|
redis发送表
|列名|操作|备注|||
|--|--|--|--|--|
|post:post_id|post:post_id:*:user_id|post:post_id:*:user_name|post:post_id:*:time|post:post_id:*:content|
|1|1|shengj|1370987654|测试内容|
---
关注表following -> set user_id
粉丝表follower -> set user_id
推送表receivepost -> list user_ids
拉取表pullpost -> zset user_ids
## 十二、面试
### 1、缓存雪崩
问题当我们的缓存失效或者redis挂了那么这个时候的请求都会直接走数据库就会给数据库造成极大的压力导致数据库也挂了
解决:
1. 对缓存设置不同的过期时间,这样就不会导致缓存同时失效
2. 建立redis集群保证服务的可靠性
### 2、缓存穿透
问题:当有大量用户不走我们设置的键值,就会直接走数据库,就会给数据库造成极大的压力,导致数据库也挂了
解决:
1. 参数过滤和提醒,引导用户走我们的设置的键值
2. 对不合法的参数进行空对象缓存,并设置较短的过期时间
### 3、缓存与数据库读写一致
问题:如果一直是读的话,是没问题的,但是更新操作会导致数据库已经更新了,缓存还是旧的数据
解决:
并发下解决数据库与缓存不一致的思路:将删除缓存、修改数据库、读取缓存等的操作积压到队列里边,实现串行化。
- 先删除缓存,再更新数据库
在高并发下表现不如意,在原子性被破坏时表现优异
- 先更新数据库,再删除缓存(Cache Aside Pattern设计模式)
在高并发下表现优异,在原子性被破坏时表现不如意
## 十三、docker实现redis主从
[docker实现redis主从](https://github.com/OMGZui/redis_m_s)
### 1、命令行模式
```bash
# 拉取redis
docker pull redis
# 主
docker run -v $(pwd)/master/redis.conf:/usr/local/etc/redis/redis.conf --name redis-master redis redis-server /usr/local/etc/redis/redis.conf
# 从1 --link redis-master:master master是别名
docker run -v $(pwd)/slave1/redis.conf:/usr/local/etc/redis/redis.conf --name redis-slave1 --link redis-master:master redis redis-server /usr/local/etc/redis/redis.conf
# 从2
docker run -v $(pwd)/slave2/redis.conf:/usr/local/etc/redis/redis.conf --name redis-slave2 --link redis-master:master redis redis-server /usr/local/etc/redis/redis.conf
```
### 2、docker-compose模式 推荐
```bash
# 拉取redis
docker pull redis
# 目录
├── docker-compose.yml
├── master
│   ├── Dockerfile
│   └── redis.conf
├── redis.conf
├── slave1
│   ├── Dockerfile
│   └── redis.conf
└── slave2
├── Dockerfile
└── redis.conf
# 启动
docker-compose up -d master slave1 slave2
# 查看主容器
docker-compose exec master bash
root@cab5db8d544b:/data# redis-cli
127.0.0.1:6379> info Replication
# Replication
role:master
connected_slaves:2
slave0:ip=172.23.0.3,port=6379,state=online,offset=1043,lag=0
slave1:ip=172.23.0.4,port=6379,state=online,offset=1043,lag=0
master_replid:995257c6b5ac62f7908cc2c7bb770f2f17b60401
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1043
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:1043
```
## 十四、参考资料
- [redis](https://redis.io/)
- [Docker创建Redis集群](https://lw900925.github.io/docker/docker-redis-cluster.html)

View File

@@ -0,0 +1,101 @@
### 1 redis相比memcached有哪些优势
1. Redis不仅仅支持简单的k/v类型的数据同时还提供listsetzsethash等数据结构的存储。
2. Redis支持数据的备份即master-slave模式的数据备份。
3. Redis支持数据的持久化可以将内存中的数据保持在磁盘中重启的时候可以再次加载进行使用。
### 3 redis常见性能问题和解决方案
(1) Master最好不要做任何持久化工作如RDB内存快照和AOF日志文件
(2) 如果数据比较重要某个Slave开启AOF备份数据策略设置为每秒同步一次
(3) 为了主从复制的速度和连接的稳定性Master和Slave最好在同一个局域网内
(4) 尽量避免在压力很大的主库上增加从库
(5) 主从复制不要用图状结构用单向链表结构更为稳定Master <- Slave1 <- Slave2 <- Slave3...
### 4 MySQL里有2000w数据redis中只存20w的数据如何保证redis中的数据都是热点数据
相关知识redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略。
redis 提供 6种数据淘汰策略
voltile-lru从已设置过期时间的数据集server.db[i].expires中挑选最近最少使用的数据淘汰
volatile-ttl从已设置过期时间的数据集server.db[i].expires中挑选将要过期的数据淘汰
volatile-random从已设置过期时间的数据集server.db[i].expires中任意选择数据淘汰
allkeys-lru从数据集server.db[i].dict中挑选最近最少使用的数据淘汰
allkeys-random从数据集server.db[i].dict中任意选择数据淘汰
no-enviction驱逐禁止驱逐数据
### 6. Redis 常见的性能问题都有哪些?如何解决?
1).Master写内存快照save命令调度rdbSave函数会阻塞主线程的工作当快照比较大时对性能影响是非常大的会间断性暂停服务所以Master最好不要写内存快照。
2).Master AOF持久化如果不重写AOF文件这个持久化方式对性能的影响是最小的但是AOF文件会不断增大AOF文件过大会影响Master重启的恢复速度。Master最好不要做任何持久化工作包括内存快照和AOF日志文件特别是不要启用内存快照做持久化,如果数据比较关键某个Slave开启AOF备份数据策略为每秒同步一次。
3).Master调用BGREWRITEAOF重写AOF文件AOF在重写的时候会占大量的CPU和内存资源导致服务load过高出现短暂服务暂停现象。
4). Redis主从复制的性能问题为了主从复制的速度和连接的稳定性Slave和Master最好在同一个局域网内
### 7 redis 最适合的场景
Redis最适合所有数据in-momory的场景虽然Redis也提供持久化功能但实际更多的是一个disk-backed的功能跟传统意义上的持久化有比较大的差别那么可能大家就会有疑问似乎Redis更像一个加强版的Memcached那么何时使用Memcached,何时使用Redis呢?
1. 会话缓存Session Cache
最常用的一种使用Redis的情景是会话缓存session cache。用Redis缓存会话比其他存储如Memcached的优势在于Redis提供持久化。当维护一个不是严格要求一致性的缓存时如果用户的购物车信息全部丢失大部分人都会不高兴的现在他们还会这样吗
幸运的是,随着 Redis 这些年的改进很容易找到怎么恰当的使用Redis来缓存会话的文档。甚至广为人知的商业平台Magento也提供Redis的插件。
2. 全页缓存FPC
除基本的会话token之外Redis还提供很简便的FPC平台。回到一致性问题即使重启了Redis实例因为有磁盘的持久化用户也不会看到页面加载速度的下降这是一个极大改进类似PHP本地FPC。
再次以Magento为例Magento提供一个插件来使用Redis作为全页缓存后端。
此外对WordPress的用户来说Pantheon有一个非常好的插件 wp-redis这个插件能帮助你以最快速度加载你曾浏览过的页面。
3. 队列
Reids在内存存储引擎领域的一大优点是提供 list 和 set 操作这使得Redis能作为一个很好的消息队列平台来使用。Redis作为队列使用的操作就类似于本地程序语言如Python对 list 的 push/pop 操作。
如果你快速的在Google中搜索“Redis queues”你马上就能找到大量的开源项目这些项目的目的就是利用Redis创建非常好的后端工具以满足各种队列需求。例如Celery有一个后台就是使用Redis作为broker你可以从这里去查看。
4. 排行榜/计数器
Redis在内存中对数字进行递增或递减的操作实现的非常好。集合Set和有序集合Sorted Set也使得我们在执行这些操作的时候变的非常简单Redis只是正好提供了这两种数据结构。所以我们要从排序集合中获取到排名最靠前的10个用户我们称之为“user_scores”我们只需要像下面一样执行即可
当然,这是假定你是根据你用户的分数做递增的排序。如果你想返回用户及用户的分数,你需要这样执行:
ZRANGE user_scores 0 10 WITHSCORES
Agora Games就是一个很好的例子用Ruby实现的它的排行榜就是使用Redis来存储数据的你可以在这里看到。
5. 发布/订阅
最后但肯定不是最不重要的是Redis的发布/订阅功能。发布/订阅的使用场景确实非常多。我已看见人们在社交网络连接中使用,还可作为基于发布/订阅的脚本触发器甚至用Redis的发布/订阅功能来建立聊天系统!(不,这是真的,你可以去核实)。
Redis提供的所有特性中我感觉这个是喜欢的人最少的一个虽然它为用户提供如果此多功能。
### 8 Redis 常见性能问题和解决方案?
(1) Master 最好不要做任何持久化工作,如 RDB 内存快照和 AOF 日志文件
(2) 如果数据比较重要,某个 Slave 开启 AOF 备份数据,策略设置为每秒同步一次
(3) 为了主从复制的速度和连接的稳定性, Master 和 Slave 最好在同一个局域网内
(4) 尽量避免在压力很大的主库上增加从库
(5) 主从复制不要用图状结构,用单向链表结构更为稳定,即: Master <- Slave1 <- Slave2 <-Slave3…
### 9 Redis怎么保证原子性的
对于Redis而言命令的原子性指的是一个操作的不可以再分操作要么执行要么不执行。
Redis的操作之所以是原子性的是因为Redis是单线程的。Redis本身提供的所有API单个命令都是原子操作Redis中的事务其实是要保证批量操作的原子性。
多个命令在并发中也是原子性的吗?
不一定, 举例1、将get和set改成单命令操作2、incr
如何解决使用Redis的事务或者使用Redis+Lua的方式实现

View File

@@ -1,77 +0,0 @@
# Redis持久化
> 参考文献
> * [https://www.cnblogs.com/shizhengwen/p/9283824.html](https://www.cnblogs.com/shizhengwen/p/9283824.html)
## 概述
* Redis是一种高级key-value数据库。它跟memcached类似不过数据可以持久化而且支持的数据类型很丰富。有字符串链表集 合和有序集合。支持在服务器端计算集合的并,交和补集(difference)等还支持多种排序功能。所以Redis也可以被看成是一个数据结构服务 器。
* Redis的所有数据都是保存在内存中然后不定期的通过异步方式保存到磁盘上(这称为“半持久化模式”)也可以把每一次数据变化都写入到一个append only file(aof)里面(这称为“全持久化模式”)。
* 由于Redis的数据都存放在内存中如果没有配置持久化redis重启后数据就全丢失了于是需要开启redis的持久化功能将数据保存到磁盘上当redis重启后可以从磁盘中恢复数据。redis提供两种方式进行持久化
* 一种是RDB持久化原理是将Reids在内存中的数据库记录定时dump到磁盘上的RDB持久化
* 另外一种是AOFappend only file持久化原理是将Reids的操作日志以追加的方式写入文件
* 那么这两种持久化方式有什么区别呢,改如何选择呢?网上看了大多数都是介绍这两种方式怎么配置,怎么使用,就是没有介绍二者的区别,在什么应用场景下使用。
## 2 二者的区别
* RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘实际操作过程是fork一个子进程先将数据集写入临时文件写入成功后再替换之前的文件用二进制压缩存储。
![](image/2021-04-07-23-24-02.png)
* AOF持久化以日志的形式记录服务器所处理的每一个写、删除操作查询操作不会记录以文本的方式记录可以打开文件看到详细的操作记录。
![](image/2021-04-07-23-24-24.png)
## 3 二者优缺点
### RDB存在哪些优势呢
1. 一旦采用该方式那么你的整个Redis数据库将只包含一个文件这对于文件备份而言是非常完美的。比如你可能打算每个小时归档一次最近24小时的数据同时还要每天归档一次最近30天的数据。通过这样的备份策略一旦系统出现灾难性故障我们可以非常容易的进行恢复。
2. 对于灾难恢复而言RDB是非常不错的选择。因为我们可以非常轻松的将一个单独的文件压缩后再转移到其它存储介质上。
3. 性能最大化。对于Redis的服务进程而言在开始持久化时它唯一需要做的只是fork出子进程之后再由子进程完成这些持久化的工作这样就可以极大的避免服务进程执行IO操作了。
4. 相比于AOF机制如果数据集很大RDB的启动效率会更高。
### RDB又存在哪些劣势呢
1. 如果你想保证数据的高可用性即最大限度的避免数据丢失那么RDB将不是一个很好的选择。因为系统一旦在定时持久化之前出现宕机现象此前没有来得及写入磁盘的数据都将丢失。
2. 由于RDB是通过fork子进程来协助完成数据持久化工作的因此如果当数据集较大时可能会导致整个服务器停止服务几百毫秒甚至是1秒钟。
### AOF的优势有哪些呢
1. 该机制可以带来更高的数据安全性即数据持久性。Redis中提供了3中同步策略即每秒同步、每修改同步和不同步。事实上每秒同步也是异步完成的其效率也是非常高的所差的是一旦系统出现宕机现象那么这一秒钟之内修改的数据将会丢失。而每修改同步我们可以将其视为同步持久化即每次发生的数据变化都会被立即记录到磁盘中。可以预见这种方式在效率上是最低的。至于无同步无需多言我想大家都能正确的理解它。
2. 由于该机制对日志文件的写入操作采用的是append模式因此在写入过程中即使出现宕机现象也不会破坏日志文件中已经存在的内容。然而如果我们本次操作只是写入了一半数据就出现了系统崩溃问题不用担心在Redis下一次启动之前我们可以通过redis-check-aof工具来帮助我们解决数据一致性的问题。
3. 如果日志过大Redis可以自动启用rewrite机制。即Redis以append模式不断的将修改数据写入到老的磁盘文件中同时Redis还会创建一个新的文件用于记录此期间有哪些修改命令被执行。因此在进行rewrite切换时可以更好的保证数据安全性。
4. AOF包含一个格式清晰、易于理解的日志文件用于记录所有的修改操作。事实上我们也可以通过该文件完成数据的重建。
### AOF的劣势有哪些呢
1. 对于相同数量的数据集而言AOF文件通常要大于RDB文件。RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。
2. 根据同步策略的不同AOF在运行效率上往往会慢于RDB。总之每秒同步策略的效率是比较高的同步禁用策略的效率和RDB一样高效。
### 二者选择的标准
就是看系统是愿意牺牲一些性能换取更高的缓存一致性aof还是愿意写操作频繁的时候不启用备份来换取更高的性能待手动运行save的时候再做备份rdb。rdb这个就更有些 eventually consistent的意思了。
## 4 常用配置
### RDB持久化配置
* Redis会将数据集的快照dump到dump.rdb文件中。此外我们也可以通过配置文件来修改Redis服务器dump快照的频率在打开6379.conf文件之后我们搜索save可以看到下面的配置信息
```
save 900 1 #在900秒(15分钟)之后如果至少有1个key发生变化则dump内存快照。
save 300 10 #在300秒(5分钟)之后如果至少有10个key发生变化则dump内存快照。
save 60 10000 #在60秒(1分钟)之后如果至少有10000个key发生变化则dump内存快照
```
### AOF持久化配置
* 在Redis的配置文件中存在三种同步方式它们分别是
```
appendfsync always #每次有数据修改发生时都会写入AOF文件。
appendfsync everysec #每秒钟同步一次该策略为AOF的缺省策略。
appendfsync no #从不同步。高效但是数据不会被持久化。
```

View File

@@ -1 +0,0 @@
https://www.cnblogs.com/ysocean/p/9080942.html#_label1

View File

@@ -1,141 +0,0 @@
https://zhuanlan.zhihu.com/p/65180667
我们在碰到需要执行耗时特别久,且结果不频繁变动的 SQL就特别适合将运行结果放入缓存。这样后面的请求就去缓存中读取使得请求能够迅速响应。
![](image/2021-08-24-16-39-38.png)
在大并发的情况下,所有的请求直接访问数据库,数据库会出现连接异常。这个时候,就需要使用 Redis 做一个缓冲操作,让请求先访问到 Redis而不是直接访问数据库。
![](image/2021-08-24-16-39-58.png)
1. 使用Redis有哪些好处
(1) 速度快因为数据存在内存中类似于HashMapHashMap的优势就是查找和操作的时间复杂度都是O(1)
(2) 支持丰富数据类型支持stringlistsetsorted sethash
(3) 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行(4) 丰富的特性可用于缓存消息按key设置过期时间过期后将会自动删除
2. redis相比memcached有哪些优势
(1) memcached所有的值均是简单的字符串redis作为其替代者支持更为丰富的数据类型
(2) redis的速度比memcached快很多
(3) redis可以持久化其数据
3.redis常见性能问题和解决方案
(1) Master最好不要做任何持久化工作如RDB内存快照和AOF日志文件
(2) 如果数据比较重要某个Slave开启AOF备份数据策略设置为每秒同步一次
(3) 为了主从复制的速度和连接的稳定性Master和Slave最好在同一个局域网内
(4) 尽量避免在压力很大的主库上增加从库
(5) 主从复制不要用图状结构用单向链表结构更为稳定Master <- Slave1 <- Slave2 <- Slave3...
4. MySQL里有2000w数据redis中只存20w的数据如何保证redis中的数据都是热点数据
相关知识redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略。
redis 提供 6种数据淘汰策略
voltile-lru从已设置过期时间的数据集server.db[i].expires中挑选最近最少使用的数据淘汰
volatile-ttl从已设置过期时间的数据集server.db[i].expires中挑选将要过期的数据淘汰
volatile-random从已设置过期时间的数据集server.db[i].expires中任意选择数据淘汰
allkeys-lru从数据集server.db[i].dict中挑选最近最少使用的数据淘汰
allkeys-random从数据集server.db[i].dict中任意选择数据淘汰
no-enviction驱逐禁止驱逐数据
6. Redis 常见的性能问题都有哪些?如何解决?
1).Master写内存快照save命令调度rdbSave函数会阻塞主线程的工作当快照比较大时对性能影响是非常大的会间断性暂停服务所以Master最好不要写内存快照。
2).Master AOF持久化如果不重写AOF文件这个持久化方式对性能的影响是最小的但是AOF文件会不断增大AOF文件过大会影响Master重启的恢复速度。Master最好不要做任何持久化工作包括内存快照和AOF日志文件特别是不要启用内存快照做持久化,如果数据比较关键某个Slave开启AOF备份数据策略为每秒同步一次。
3).Master调用BGREWRITEAOF重写AOF文件AOF在重写的时候会占大量的CPU和内存资源导致服务load过高出现短暂服务暂停现象。
4). Redis主从复制的性能问题为了主从复制的速度和连接的稳定性Slave和Master最好在同一个局域网内
7, redis 最适合的场景
Redis最适合所有数据in-momory的场景虽然Redis也提供持久化功能但实际更多的是一个disk-backed的功能跟传统意义上的持久化有比较大的差别那么可能大家就会有疑问似乎Redis更像一个加强版的Memcached那么何时使用Memcached,何时使用Redis呢?
如果简单地比较Redis与Memcached的区别大多数都会得到以下观点
1 、Redis不仅仅支持简单的k/v类型的数据同时还提供listsetzsethash等数据结构的存储。
2 、Redis支持数据的备份即master-slave模式的数据备份。
3 、Redis支持数据的持久化可以将内存中的数据保持在磁盘中重启的时候可以再次加载进行使用。
1、会话缓存Session Cache
最常用的一种使用Redis的情景是会话缓存session cache。用Redis缓存会话比其他存储如Memcached的优势在于Redis提供持久化。当维护一个不是严格要求一致性的缓存时如果用户的购物车信息全部丢失大部分人都会不高兴的现在他们还会这样吗
幸运的是,随着 Redis 这些年的改进很容易找到怎么恰当的使用Redis来缓存会话的文档。甚至广为人知的商业平台Magento也提供Redis的插件。
2、全页缓存FPC
除基本的会话token之外Redis还提供很简便的FPC平台。回到一致性问题即使重启了Redis实例因为有磁盘的持久化用户也不会看到页面加载速度的下降这是一个极大改进类似PHP本地FPC。
再次以Magento为例Magento提供一个插件来使用Redis作为全页缓存后端。
此外对WordPress的用户来说Pantheon有一个非常好的插件 wp-redis这个插件能帮助你以最快速度加载你曾浏览过的页面。
3、队列
Reids在内存存储引擎领域的一大优点是提供 list 和 set 操作这使得Redis能作为一个很好的消息队列平台来使用。Redis作为队列使用的操作就类似于本地程序语言如Python对 list 的 push/pop 操作。
如果你快速的在Google中搜索“Redis queues”你马上就能找到大量的开源项目这些项目的目的就是利用Redis创建非常好的后端工具以满足各种队列需求。例如Celery有一个后台就是使用Redis作为broker你可以从这里去查看。
4、排行榜/计数器
Redis在内存中对数字进行递增或递减的操作实现的非常好。集合Set和有序集合Sorted Set也使得我们在执行这些操作的时候变的非常简单Redis只是正好提供了这两种数据结构。所以我们要从排序集合中获取到排名最靠前的10个用户我们称之为“user_scores”我们只需要像下面一样执行即可
当然,这是假定你是根据你用户的分数做递增的排序。如果你想返回用户及用户的分数,你需要这样执行:
ZRANGE user_scores 0 10 WITHSCORES
Agora Games就是一个很好的例子用Ruby实现的它的排行榜就是使用Redis来存储数据的你可以在这里看到。
5、发布/订阅
最后但肯定不是最不重要的是Redis的发布/订阅功能。发布/订阅的使用场景确实非常多。我已看见人们在社交网络连接中使用,还可作为基于发布/订阅的脚本触发器甚至用Redis的发布/订阅功能来建立聊天系统!(不,这是真的,你可以去核实)。
Redis提供的所有特性中我感觉这个是喜欢的人最少的一个虽然它为用户提供如果此多功能。
7.4、缓存预热
缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
解决方法:
1、直接写个缓存刷新页面上线时手工操作下
2、数据量不大可以在项目启动的时候自动进行加载
3、定时刷新缓存
7.5、缓存降级
当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。
降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。
在进行降级之前要对系统进行梳理,看看系统是不是可以丢卒保帅;从而梳理出哪些必须誓死保护,哪些可降级
8、Redis 常见性能问题和解决方案?
(1) Master 最好不要做任何持久化工作,如 RDB 内存快照和 AOF 日志文件
(2) 如果数据比较重要,某个 Slave 开启 AOF 备份数据,策略设置为每秒同步一次
(3) 为了主从复制的速度和连接的稳定性, Master 和 Slave 最好在同一个局域网内
(4) 尽量避免在压力很大的主库上增加从库
(5) 主从复制不要用图状结构,用单向链表结构更为稳定,即: Master <- Slave1 <- Slave2 <-Slave3…
9、为什么Redis的操作是原子性的怎么保证原子性的
对于Redis而言命令的原子性指的是一个操作的不可以再分操作要么执行要么不执行。
Redis的操作之所以是原子性的是因为Redis是单线程的。
Redis本身提供的所有API单个命令都是原子操作Redis中的事务其实是要保证批量操作的原子性。
多个命令在并发中也是原子性的吗?
不一定, 举例1、将get和set改成单命令操作2、incr
如何解决使用Redis的事务或者使用Redis+Lua的方式实现
————————————————
版权声明本文为CSDN博主「波波的小书房」的原创文章遵循CC 4.0 BY-SA版权协议转载请附上原文出处链接及本声明。
原文链接https://blog.csdn.net/xl_1803/article/details/107901645