Redis

Redis

Redis是一个高性能的key-value数据库,是非关系型数据库

Redis的特点

1.redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用

2.redis支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储

3.redis支持数据的备份,即master-slave模式的数据备份

redis的优势

1.性能极高 --- Redis能读的速度是110000次/s,写的速度是81000次/s

2.丰富的数据类型 --- Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作

3.原子 --- Redis的所有操作都是原子性,意思就是要么成功执行,要么失败完全不执行,单个操作是原子性的,多个操作也支持事务,即原子性,通过MULTII和EXEC指令包起来

4.丰富的特征 --- Redis还支持 publish/subscribe, 通知, key 过期等等特性

Redis与其他key-value存储有什么不同

1.Redis有更复杂的数据结构,并且提供对他们的原子性操作

2.Redis运行在内存中但是支持持久化到磁盘中

关系型数据库和非关系型数据库

关系型数据库:存核心的数据,最典型的数据结构是表

优点:

1.操作简单:数据都是在表中,使用的spl语句也是一样的,也可以用于复杂查询

2.复杂操作:支持spl,可以进行多表查询及别的复杂的查询

缺点:

1.读写性能差:当数据多的时候对表的读写效率低

2.扩展性差:表的结构固定,不易于扩展

3.高并发行:对数据库进行查询是I/O操作

非关系型数据库:按照键值对的形式存储

优点:

1.扩展性强:没有固定的结构,可以存储多种样式的数据形式

2.速度快:非关系型数据库是以内存作为载体,关系型数据库是以硬盘做载体

3.成本低:基本都是开源软件

缺点:

1.不支持sql

2.没有事务处理

3.数据结构象对复杂 ,复杂查询方面欠缺

redis介绍

redis安装
"""
1、官网下载:安装包或是绿色面安装
2、安装并配置环境变量
"""
redis VS mysql
"""
redis: 内存数据库(读写快)、非关系型(操作数据方便、数据固定)
mysql: 硬盘数据库(数据持久化)、关系型(操作数据间关系、可以不同组合)

大量访问的临时数据,才有redis数据库更优
"""
redis VS memcache
"""
redis: 操作字符串、列表、字典、无序集合、有序集合 | 支持数据持久化(数据丢失可以找回(默认持久化,主动持久化save)、可以将数据同步给mysql) | 高并发支持
memcache: 操作字符串 | 不支持数据持久化 | 并发量小
"""

Redis操作

启动服务
"""
前提:前往一个方便管理redis持久化文件的逻辑再启动服务:dump.rdb
1)前台启动服务,当cmd页面关闭的时候就不能再进行连接了
>: redis-server
2)后台启动服务
>: redis-server --service-start
3)配置文件启动服务
>: redis-server 配置文件的绝对路径
>: redis-server --service-start 配置文件的绝对路径
eg>: redis-server --service-start D:/redis/redis.conf
"""
密码管理
"""
1)提倡在配置文件中配置,采用配置文件启动
requirepass 密码

2)当服务启动后,并且连入数据库,可以再改当前服务的密码(服务重启,密码重置)
config set requirepass 新密码

3)连入数据库,查看当前服务密码密码
config get requirepass

4)使用密码登录
redis-cli -h 127.0.0.1 -p 6380 -a 123456
"""
连接数据库
"""
1)默认连接:-h默认127.0.0.1,-p默认6379,-n默认0,-a默认无
>: redis-cli

2)完整连接:
>: redis-cli -h ip地址 -p 端口号 -n 数据库编号 -a 密码

3)先连接,后输入密码
>: redis-cli -h ip地址 -p 端口号 -n 数据库编号
>: auth 密码
"""
关闭服务
"""
1)在没有连接进数据库时执行
>: redis-cli shutdown

2)连接进数据库后执行
>: shutdown
"""
切换数据库
"""
1)在连入数据库后执行
>: select 数据库编号
"""
数据持久化

Redis的所有数据都是保存在内存中,然后不定期的通过异步方式保存到磁盘上(这称为“半持久化模式”);也可以把每一次数据变化都写入到一个append only file(aof)里面(这称为“全持久化模式”)。

由于Redis的数据都存放在内存中,如果没有配置持久化,redis重启后数据就全丢失了,于是需要开启redis的持久化功能,将数据保存到磁 盘上,当redis重启后,可以从磁盘中恢复数据。redis提供两种方式进行持久化,一种是RDB持久化(原理是将Reids在内存中的数据库记录定时 dump到磁盘上的RDB持久化),另外一种是AOF(append only file)持久化(原理是将Reids的操作日志以追加的方式写入文件)

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的意思了。

"""
RDB持久化配置
1)配置文件默认配置
save 900 1  # 超过900秒有1个键值对操作,会自动调用save完成数据持久化
save 300 10  # 超过300秒有10个键值对操作,会自动调用save完成数据持久化
save 60 10000  # 超过60秒有10000个键值对操作,会自动调用save完成数据持久化

2)安全机制
# 当redis服务不可控宕机,会默认调用一下save完成数据持久化

3)主动持久化
>: save  # 连入数据库时,主动调用save完成数据持久化

注:数据持久化默认保存文件 dump.rdb,保存路径默认为启动redis服务的当前路径
"""
"""
AOF持久化配置
在Redis的配置文件中存在三种同步方式,它们分别是:

appendfsync always     #每次有数据修改发生时都会写入AOF文件。

appendfsync everysec  #每秒钟同步一次,该策略为AOF的缺省策略。

appendfsync no          #从不同步。高效但是数据不会被持久化。
"""

Redis数据类型

数据操作:字符串、列表、哈希(字典)、无序集合、有序(排序)集合
有序集合:游戏排行榜

字符串

"""
# 增:
SET key value   设置指定 key 的值  单增
MSET key value [key value ...]      同时设置一个或多个 key-value 对  多增(key存在时)
MSETNX key value [key value ...]    同时设置一个或多个 key-value 对   多增(key不存在时)
APPEND key value    在末尾新增

# 删
del key [key ...]   单删或多删

# 改
GETSET key value       将给定 key 的值设为 value ,并返回 key 的旧值(old value)  单改
mset k1 v1 k2 v2 ...   同时设置一个或多个 key-value 对。
incrby key increment   将 key 所储存的值加上给定的增量值(increment)

# 查
mget k1 k2 ...    群查
get k1            单查

# 设置时间
setex key exp value    当exp时间后不能在查询到信息,到期自动删除
"""

列表

"""
rpush key value1 value2 ...    在列表中添加一个或多个值
lpush key value1 value2 ...    将一个或多个值插入到列表头部

lrange key bindex eindex       获取列表指定范围内的元素
lindex key index               通过索引获取列表中的元素

lpop key                       移出并获取列表的第一个元素  
rpop key                       移除列表的最后一个元素,并将该元素添加到另一个列表并返回

linsert key before|after old_value new_value  在列表的元素前或者后插入元素
"""

哈希

"""
hset key field value            新增
hmset key field1 value1 field2 value2 ...  多增
hget key field                  单查
hmget key field1 field2         多查
hkeys key                       查询key中所有的key
hvals key                       查询key中所有的value
hdel key field                  删除key中的field    

key:redis数据库中的keu值    field:value中的key值   value:value中的key对应的值
"""

集合

"""
sadd key member1 member2 ...    新增一个集合且可以想已有集合添加成员
sdiff key1 key2 ...             返回给定所有集合的差集
sdiffstore newkey key1 key2 ... 返回给定所有集合的差集并存储在 destination 中
sinter key1 key2 ...            返回给定所有集合的交集
sunion key1 key2 ...            返回所有给定集合的并集       
smembers key                    返回集合中的所有成员
spop key                        移除并返回集合中的一个随机元素
"""

有序集合

"""
zadd key grade1 member1 grade2 member2 ...   向有序集合添加一个或多个成员,或者更新已存在成员的分数
zincrby key grade member        有序集合中对指定成员的分数加上增量 increment
zrange key start end            通过索引区间返回有序集合指定区间内的成员
zrevrange key start end         移除有序集合中给定的字典区间的所有成员
"""

python使用redis

依赖
>: pip3 install redis

直接使用
import redis
r = redis.Redis(host='127.0.0.1', port=6379, db=1, password=None, decode_responses=True)   # 将取出来的内容进行转义

连接池使用
import redis
pool = redis.ConnectionPool(host='127.0.0.1', port=6379, db=1, max_connections=100, password=None, decode_responses=True)
r = redis.Redis(connection_pool=pool)

缓存使用:要额外安装 django-redis
# 1.将缓存存储位置配置到redis中:settings.py
CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/0",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
            "CONNECTION_POOL_KWARGS": {"max_connections": 100},
            "DECODE_RESPONSES": True,
            "PASSWORD": "",
        }
    }
}

# 2.操作cache模块直接操作缓存:views.py
from django.core.cache import cache  # 结合配置文件实现插拔式
# 存放token,可以直接设置过期时间
cache.set('token', 'header.payload.signature', 300)
# 取出token
token = cache.get('token')

接口缓存

按后端不分离有三种缓存,

后台接口提供数据库数据的,IO操作慢,可以将数据存储在缓存中,接口数据从缓存中调

一般将大量访问(数据时效性要求不是很苛刻)的接口建立缓存

接口缓存思想:数据先走缓存,有直接返回,没有走数据库(同步到缓存)

Memcached缓存

Memcached是基于内存的缓存,是Django原生支持的最快最有效的缓存系统,Django默认走的是memcached缓存,但是Memcached不会持久化,重启会将缓存清除掉,所以我们要使用redis来对缓存进行存储,保证了在重启后还能将数据恢复

cache语法

from django.core.cache import cache
#存入内存
cache.set("aaa",123)
#从内存中获取
cache.get("aa")

设置缓存时效

#只在内存中存五秒钟
cache.set("aaa",123,10)

接口缓存
1) 优先从缓存中取数据,如果没有,再从数据库中取(同步到缓存)
def list(self, request, *args, **kwargs):
    data = cache.get('banner_cache')
    if not data:
        print('走了数据库')
        response = super().list(request, *args, **kwargs)
        # 不设置过期时间,缓存的更新在后台异步更新(celery异步框架)
        cache.set('banner_cache', response.data)  
        return response
     return Response(data)

Redis高级

常见的关系型数据库在数据量过大的时候需要分库分表,把数据表拆到不同的库中,postgresql和MySQL操作是一样的,是关系型数据库

非关系型数据库:reids,mongodb

redis大部分时间用来做缓存,因为缓存块(内存数据库,单线程,单进程)

五大数据类型可以实现的功能

做缓存:string类型最多,其次是hash,values值是json格式

计数:(自增)

消息队列:列表类型,现金新阿初,或者先进后出,blpop

发布订阅:观察者模式

排行榜:有序集合

分布式锁:

做附近的人

布隆过滤器

统计在线人数

geo实现地图定位

持久化

主从复制

主从复制可以解决问题:机器故障,容量瓶颈,QPS瓶颈

一主一从,一主多从

做读写分离

做数据副本

扩展数据性能

一个maskter可以有多个slabe

一个slabeke只能有一个master

数据流向是单项的,从master到slave

可以从主redis中存放数据,然后在从redis中取出来数据

操作流程:

1.使用docker启动三个redis容器

docker run -id --name=redis6380 -p 6380:6379 -v /root/test/redis/conf/redis.conf:/etc/redis/redis6380.conf -v /root/test/redis/data:/etc/redis/data redis

docker run -id --name=redis6381 -p 6381:6379 -v /root/test/redis/conf/redis6381.conf:/etc/redis/redis6381.conf -v /root/test/redis/data:/etc/redis/data redis

docker run -id --name=redis6382 -p 6382:6379 -v /root/test/redis/conf/redis6382.conf:/etc/redis/redis6382.conf -v /root/test/redis/data:/etc/redis/data redis

2.分别启动三个容器

3.进入三个容器,并使用三个容器所对应的IP进行登录,

主:redis-cli -h 172.18.0.9

从:redis-cli -h 172.18.0.11
从:redis-cli -h 172.18.0.12

4.在 从容器 中执行以下命令

slaveof 172.18.0.9 6379

5.在主容器中查看

172.18.0.9:6379> info replication   # 查看指令
# Replication
role:master
connected_slaves:2
slave0:ip=172.18.0.11,port=6379,state=online,offset=70,lag=1   对应的 从容器1
slave1:ip=172.18.0.12,port=6379,state=online,offset=70,lag=0   对应的 从容器2
master_replid:3154a1a9afe50413e919a9f21ed059ac451bf91a
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:70
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:70

其他操作命令

slaveof no one #取消复制,不会把之前的数据清除

使用配置文件进行配置

slaveof ip port #配置从节点ip和端口
slave-read-only yes #从节点只读,因为可读可写,数据会乱

'''
mkdir -p redis1/conf redis1/data redis2/conf redis2/data redis3/conf redis3/data
vim redis.conf

daemonize no
pidfile redis.pid
bind 0.0.0.0
protected-mode no
port 6379
timeout 0
logfile redis.log
dbfilename dump.rdb
dir /data
slaveof 10.0.0.101 6379
slave-read-only yes


cp redis.conf /home/redis2/conf/


docker run -p 6379:6379 --name redis_6379 -v /home/redis1/conf/redis.conf:/etc/redis/redis.conf -v /home/redis1/data:/data -d redis redis-server /etc/redis/redis.conf

docker run -p 6378:6379 --name redis_6378 -v /home/redis2/conf/redis.conf:/etc/redis/redis.conf -v /home/redis2/data:/data -d redis redis-server /etc/redis/redis.conf

docker run -p 6377:6379 --name redis_6377 -v /home/redis3/conf/redis.conf:/etc/redis/redis.conf -v /home/redis3/data:/data -d redis redis-server /etc/redis/redis.conf

info replication

'''

常见问题

1 读写分离

读流量分摊到从节点

可能遇到问题:复制数据延迟,读到过期数据,从节点故障

2 主从配置不一致

maxmemory不一致:丢失数据

数据结构优化参数:主节点做了优化,从节点没有设置优化,会出现一些问题

3 规避全量复制

第一次全量复制,不可避免:小主节点,低峰(夜间)

节点运行id不匹配:主节点重启(运行id变化)

复制挤压缓冲区不足:增大复制缓冲区大小,rel_backlog_size

4 规避复制风暴

单主节点复制风暴,主节点重启,所有从节点复制

redis哨兵

redis可以做故障判断,故障转移,通知客户端,客户端连接sentinel的地址

具体实现流程:

1.多个sentinel发现并确认master有问题

2.选举出一个sentinel作为领导

3.选举一个slave作为新的master

4.通知其余slave成为新的master的slave

5.通知客户端主从变化

6.等待老的master复活成为新的master的slave

安装配置

#配置开启sentinel监控主节点
mkdir -p redis4/conf redis4/data redis5/conf redis5/data redis6/data redis6/conf

vi sentinel.conf


port 26379
daemonize no
dir /data
protected-mode no
bind 0.0.0.0
logfile "redis_sentinel.log"
sentinel monitor mymaster 10.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000


docker run -p 26379:26379 --name redis_26379 -v /home/redis4/conf/sentinel.conf:/etc/redis/sentinel.conf -v /home/redis4/data:/data -d redis redis-sentinel /etc/redis/sentinel.conf

docker run -p 26378:26379 --name redis_26378 -v /home/redis5/conf/sentinel.conf:/etc/redis/sentinel.conf -v /home/redis5/data:/data -d redis redis-sentinel /etc/redis/sentinel.conf

docker run -p 26377:26379 --name redis_26377 -v /home/redis6/conf/sentinel.conf:/etc/redis/sentinel.conf -v /home/redis6/data:/data -d redis redis-sentinel /etc/redis/sentinel.conf



redis-sentinel sentinel.conf

info
配置会重写,自动发现slave

客户端连接

import redis
from redis.sentinel import Sentinel

# 连接哨兵服务器(主机名也可以用域名)
# 10.0.0.101:26379
sentinel = Sentinel([('10.0.0.101', 26379),
                     ('10.0.0.101', 26378),
                     ('10.0.0.101', 26377)
		     ],
                    socket_timeout=5)


print(sentinel)
# 获取主服务器地址
master = sentinel.discover_master('mymaster')
print(master)


# 获取从服务器地址
slave = sentinel.discover_slaves('mymaster')
print(slave)


# 获取主服务器进行写入
# master = sentinel.master_for('mymaster', socket_timeout=0.5)
# w_ret = master.set('foo', 'bar')
#
#
#
#
# slave = sentinel.slave_for('mymaster', socket_timeout=0.5)
# r_ret = slave.get('foo')
# print(r_ret)

原文地址:https://www.cnblogs.com/whkzm/p/12177886.html