Skip to content

商务合作:vTikTok


公众号:



Redis介绍

Redis是一种基于键值对的NoSQL数据库,它属于非关系型数据库。

Redis有以下主要特征:

  1. 基于内存,性能极高,读写速度达每秒十万级,支持数据持久化。
  2. 支持丰富的数据结构,如字符串,散列表,列表,集合,有序集合,bitmaps,hyperloglogs等。
  3. 支持主从复制,可以做高可用部署。
  4. 支持持久化,可以在重启时再次加载数据。Redis是一种内存键值对存储数据库,数据存放在服务器内存中。但是也支持持久化到磁盘。
  5. 支持LUA脚本,可以做一定的业务逻辑处理。
  6. 简单稳定,对象均为二进制安全,可以保存任何数据。
  7. 开源,支持很多不同语言的API。

MAC上面安装Redis

  1. brew --version
  2. brew install redis

从前台启动和停止Redis(生命周期是命令行是否关闭)

启动:redis-server 停止:Ctrl + C

从后台启动和停止Redis(类似于启动MySQL或者SSH服务,但还未连接)

启动:brew services start redis 停止:brew services stop redis

查看启动状态:brew services info redis

打开Redis终端(类似于ssh -p)

redis-cli(注意,要在另外一个终端打开,相当于服务启动相当于,服务器启动),而redis-cli则相当于连接到这台服务器上面的一个客户端。

查看Redis配置

Redis 的配置文件位于 Redis 安装目录下,文件名为 redis.conf

redis 127.0.0.1:6379> CONFIG GET XXX

如:redis 127.0.0.1:6379> CONFIG GET loglevel

结果:

1) "loglevel"
2) "notice"
1) "loglevel"
2) "notice"

查看所有配置项:CONFIG GET *

编辑Redis配置

redis 127.0.0.1:6379> CONFIG SET XXX XXX_VALUE

如:redis 127.0.0.1:6379> CONFIG SET loglevel "notice"

常用配置:

daemonize no	Redis 默认不是以守护进程的方式运行,可以通过该配置项修改,使用 yes 启用守护进程(Windows 不支持守护线程的配置为 no )
port 6379	指定 Redis 监听端口,默认端口为 6379。
bind 127.0.0.1	绑定的主机地址
timeout 300	当客户端闲置多长秒后关闭连接,如果指定为 0 ,表示关闭该功能
loglevel notice	指定日志记录级别,Redis 总共支持四个级别:debug、verbose、notice、warning,默认为 notice
databases 16	设置数据库的数量,默认数据库为0,可以使用SELECT 命令在连接上指定数据库id
requirepass foobared	设置 Redis 连接密码,如果配置了连接密码,客户端在连接 Redis 时需要通过 AUTH <password> 命令提供密码,默认关闭
maxclients 128	设置同一时间最大客户端连接数,默认无限制,Redis 可以同时打开的客户端连接数为 Redis 进程可以打开的最大文件描述符数,如果设置 maxclients 0,表示不作限制。当客户端连接数到达限制时,Redis 会关闭新的连接并向客户端返回 max number of clients reached 错误信息
daemonize no	Redis 默认不是以守护进程的方式运行,可以通过该配置项修改,使用 yes 启用守护进程(Windows 不支持守护线程的配置为 no )
port 6379	指定 Redis 监听端口,默认端口为 6379。
bind 127.0.0.1	绑定的主机地址
timeout 300	当客户端闲置多长秒后关闭连接,如果指定为 0 ,表示关闭该功能
loglevel notice	指定日志记录级别,Redis 总共支持四个级别:debug、verbose、notice、warning,默认为 notice
databases 16	设置数据库的数量,默认数据库为0,可以使用SELECT 命令在连接上指定数据库id
requirepass foobared	设置 Redis 连接密码,如果配置了连接密码,客户端在连接 Redis 时需要通过 AUTH <password> 命令提供密码,默认关闭
maxclients 128	设置同一时间最大客户端连接数,默认无限制,Redis 可以同时打开的客户端连接数为 Redis 进程可以打开的最大文件描述符数,如果设置 maxclients 0,表示不作限制。当客户端连接数到达限制时,Redis 会关闭新的连接并向客户端返回 max number of clients reached 错误信息

Redis数据类型

Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。

String类型

string 是 redis 最基本的类型,你可以理解成与 Memcached 一模一样的类型,一个 key 对应一个 value。

string 类型是二进制安全的。意思是 redis 的 string 可以包含任何数据。比如jpg图片或者序列化的对象。

string 类型是 Redis 最基本的数据类型,string 类型的值最大能存储 512MB。

redis 127.0.0.1:6379> DEL runoob
redis 127.0.0.1:6379> SET runoob "菜鸟教程"
OK
redis 127.0.0.1:6379> GET runoob
"菜鸟教程"
redis 127.0.0.1:6379> DEL runoob
redis 127.0.0.1:6379> SET runoob "菜鸟教程"
OK
redis 127.0.0.1:6379> GET runoob
"菜鸟教程"

Hash类型

使用场景:存储、读取、修改用户属性

redis 127.0.0.1:6379> DEL runoob
redis 127.0.0.1:6379> HMSET runoob field1 "Hello" field2 "World"
"OK"
redis 127.0.0.1:6379> HGET runoob field1
"Hello"
redis 127.0.0.1:6379> HGET runoob field2
"World"
redis 127.0.0.1:6379> DEL runoob
redis 127.0.0.1:6379> HMSET runoob field1 "Hello" field2 "World"
"OK"
redis 127.0.0.1:6379> HGET runoob field1
"Hello"
redis 127.0.0.1:6379> HGET runoob field2
"World"

列表

使用场景:1、最新消息排行等功能(比如朋友圈的时间线) 2、消息队列

列表最多可存储 232 - 1 元素 (4294967295, 每个列表可存储40多亿)。

redis 127.0.0.1:6379> DEL runoob
redis 127.0.0.1:6379> lpush runoob redis
(integer) 1
redis 127.0.0.1:6379> lpush runoob mongodb
(integer) 2
redis 127.0.0.1:6379> lpush runoob rabbitmq
(integer) 3
redis 127.0.0.1:6379> lrange runoob 0 10
1) "rabbitmq"
2) "mongodb"
3) "redis"
redis 127.0.0.1:6379> DEL runoob
redis 127.0.0.1:6379> lpush runoob redis
(integer) 1
redis 127.0.0.1:6379> lpush runoob mongodb
(integer) 2
redis 127.0.0.1:6379> lpush runoob rabbitmq
(integer) 3
redis 127.0.0.1:6379> lrange runoob 0 10
1) "rabbitmq"
2) "mongodb"
3) "redis"

Set(集合)

使用场景:1、共同好友 2、利用唯一性,统计访问网站的所有独立ip 3、好友推荐时,根据tag求交集,大于某个阈值就可以推荐

Redis 的 Set 是 string 类型的无序集合。 集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。

添加一个 string 元素到 key 对应的 set 集合中,成功返回 1,如果元素已经在集合中返回 0。

redis 127.0.0.1:6379> DEL runoob
redis 127.0.0.1:6379> sadd runoob redis
(integer) 1
redis 127.0.0.1:6379> sadd runoob mongodb
(integer) 1
redis 127.0.0.1:6379> sadd runoob rabbitmq
(integer) 1
redis 127.0.0.1:6379> sadd runoob rabbitmq
(integer) 0
redis 127.0.0.1:6379> smembers runoob

1) "redis"
2) "rabbitmq"
3) "mongodb"
redis 127.0.0.1:6379> DEL runoob
redis 127.0.0.1:6379> sadd runoob redis
(integer) 1
redis 127.0.0.1:6379> sadd runoob mongodb
(integer) 1
redis 127.0.0.1:6379> sadd runoob rabbitmq
(integer) 1
redis 127.0.0.1:6379> sadd runoob rabbitmq
(integer) 0
redis 127.0.0.1:6379> smembers runoob

1) "redis"
2) "rabbitmq"
3) "mongodb"

注意:以上实例中 rabbitmq 添加了两次,但根据集合内元素的唯一性,第二次插入的元素将被忽略。

集合中最大的成员数为 232 - 1(4294967295, 每个集合可存储40多亿个成员)。

zset(sorted set:有序集合)

使用场景:1、排行榜 2、带权重的消息队列

Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。 不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。

zset的成员是唯一的,但分数(score)却可以重复。

redis 127.0.0.1:6379> DEL runoob
redis 127.0.0.1:6379> zadd runoob 0 redis
(integer) 1
redis 127.0.0.1:6379> zadd runoob 0 mongodb
(integer) 1
redis 127.0.0.1:6379> zadd runoob 0 rabbitmq
(integer) 1
redis 127.0.0.1:6379> zadd runoob 0 rabbitmq
(integer) 0
redis 127.0.0.1:6379> ZRANGEBYSCORE runoob 0 1000
1) "mongodb"
2) "rabbitmq"
3) "redis"
redis 127.0.0.1:6379> DEL runoob
redis 127.0.0.1:6379> zadd runoob 0 redis
(integer) 1
redis 127.0.0.1:6379> zadd runoob 0 mongodb
(integer) 1
redis 127.0.0.1:6379> zadd runoob 0 rabbitmq
(integer) 1
redis 127.0.0.1:6379> zadd runoob 0 rabbitmq
(integer) 0
redis 127.0.0.1:6379> ZRANGEBYSCORE runoob 0 1000
1) "mongodb"
2) "rabbitmq"
3) "redis"

Redis实例的含义

通过redis-server启动服务,就等于启动了一个Redis实例,一个实例对应一个端口,就相当于我们的Tomcat服务器,运行start.sh启动一个Tomcat实例。

  1. 指定不同的端口号

如果希望在同一个服务器上面启动多个Redis实例,那么就需要指定不同的端口,如果不指定,多次启动会报错(因为端口已使用),如:

redis-server --port 6379 # 启动一个实例,端口6379 redis-server --port 6380 # 启动另一个实例,端口6380

  1. 指定不同的PID文件

使用--pidfile参数为每个实例指定不同的PID文件,否则ONLYONE实例保护机制会阻止多实例启动。 例如:

redis-server --port 6379 --pidfile redis_6379.pid redis-server --port 6380 --pidfile redis_6380.pid

  1. 指定不同的工作目录

redis-server --port 6379 --dir ./redis_6379/ redis-server --port 6380 --dir ./redis_6380/

  1. 配置不同的实例配置文件

你可以为每个实例准备一个独立的配置文件,通过--config-file参数指定,而后启动实例。这是一种比较规范的方式。 例如:

redis_6379.conf:

port 6379
pidfile redis_6379.pid
dir ./redis_6379
port 6379
pidfile redis_6379.pid
dir ./redis_6379

redis_6380.conf:

port 6380 
pidfile redis_6380.pid
dir ./redis_6380
port 6380 
pidfile redis_6380.pid
dir ./redis_6380

然后:

redis-server --config-file redis_6379.conf 
redis-server --config-file redis_6380.conf
redis-server --config-file redis_6379.conf 
redis-server --config-file redis_6380.conf

以上4种方式都可以用于在一台服务器启动多个Redis实例。只需要确保每个实例的端口号、PID文件、工作目录等不冲突即可。

Select->选择数据库(编号)

注意:Redis的数据库更像是一种命名空间,且都用同一个密码访问,不同App使用不能用Redis的数据库概念来隔离app的数据,而应该用不同的Redis实例做数据隔离。

每个数据库对外都是一个从0开始的递增数字命名,Redis默认支持16个数据库(可以通过配置文件支持更多,无上限),可以通过配置databases来修改这一数字。客户端与Redis建立连接后会自动选择0号数据库,不过可以随时使用SELECT命令更换数据库,如要选择1号数据库:

redis> SELECT 1
OK
redis [1] > GET foo
(nil)
redis> SELECT 1
OK
redis [1] > GET foo
(nil)

然而这些以数字命名的数据库又与我们理解的数据库有所区别。首先Redis不支持自定义数据库的名字,每个数据库都以编号命名,开发者必须自己记录哪些数据库存储了哪些数据。另外Redis也不支持为每个数据库设置不同的访问密码,所以一个客户端要么可以访问全部数据库,要么连一个数据库也没有权限访问。最重要的一点是多个数据库之间并不是完全隔离的,比如FLUSHALL命令可以清空一个Redis实例中所有数据库中的数据。综上所述,这些数据库更像是一种命名空间,而不适宜存储不同应用程序的数据。

比如可以使用0号数据库存储某个应用生产环境中的数据,使用1号数据库存储测试环境中的数据,但不适宜使用0号数据库存储A应用的数据而使用1号数据库B应用的数据,不同的应用应该使用不同的Redis实例存储数据。

由于Redis非常轻量级,一个空Redis实例占用的内存只有1M左右,所以不用担心多个Redis实例会额外占用很多内存。

本地执行(客户端)

$ redis-cli
redis 127.0.0.1:6379>
redis 127.0.0.1:6379> PING
$ redis-cli
redis 127.0.0.1:6379>
redis 127.0.0.1:6379> PING

远程执行(客户端)

$redis-cli -h 127.0.0.1 -p 6379 -a "mypass"
redis 127.0.0.1:6379>
redis 127.0.0.1:6379> PING
$redis-cli -h 127.0.0.1 -p 6379 -a "mypass"
redis 127.0.0.1:6379>
redis 127.0.0.1:6379> PING

Redis持久化问题

作为内存数据库,如果不进行数据持久化,那么一旦服务重启,数据将全部丢失。所以Redis支持RDB和AOF两种持久化方式。

  • RDB将数据定期序列化为dump.rdb文件。重启后直接加载该文件恢复数据。
  • AOF记录服务器执行的所有写操作命令,重启后重新执行这些命令来恢复数据。

Redis为单线程,如果启用了AOF,由于Redis本身对写操作做了缓存,如果在缓冲区数据还未刷新到磁盘时Redis异常重启,那么可能丢失几秒内的数据。

Redis使用fsync=always进行持久化时(将每次修改同步写入磁盘),可以保证数据不丢失,但会严重影响性能。

所以,总结来说:

如果不启用任何持久化,那么Redis重启会丢失全部数据。 如果启用RDB或AOF持久化,那么丢失的数据窗口可以控制在几秒内。 如果使用fsync=always,那么可以保证不丢失数据,但性能会大幅下降。 在实际使用中,需要根据业务需求选择合适的持久化策略与fsync频率,在性能和数据安全间取得平衡。

与Berkeley DB(BDB)对比

Berkeley DB是一个嵌入式数据库系统,将其归类到内存数据库范畴没有问题;

重建内存数据库 BDB用4s,Redis 20s; 更新内存数据库,BDB和Redis的实验结果都比较理想 查询记录时,BDB比Redis基本快一个数量级;

在存取速度上,Berkerley DB比关系型数据库,比redis都快不少,在批量查询及数据导入操作上甚至快上一个数量级。这主要是因为:

  1. 全内存操作;数据都在内存中;
  2. DB与应用在同一个进程地址空间中;这样,就没有额外的网络开销。

当然,我们会意识到使用Berkerley DB就必须在每个应用物理节点上都开辟一大块内存,来存放Berkerley DB的数据。这方面,分布式的Redis则显得更为实

Python使用Redis

pip3 install redis

基本案例:

# -*- coding: UTF-8 -*-
import logging

from flask import Blueprint
from flask import Flask

from ResultEntity import ResultEntity

import redis   # 导入redis 模块

route_bp = Blueprint('', __name__)

# redis-py 使用 connection pool 来管理对一个 redis server 的所有连接,避免每次建立、释放连接的开销。
# 默认,每个Redis实例都会维护一个自己的连接池。可以直接建立一个连接池,然后作为参数 Redis,这样就可以实现多个 Redis 实例共享一个连接池。
r = redis.StrictRedis(host='localhost', port=6379, decode_responses=True)

@route_bp.route('/')
def index():
    username = r.get('k1')
    print(type(username))  # 查看类型
    # redis 提供两个类 Redis 和 StrictRedis, StrictRedis 用于实现大部分官方的命令,Redis 是 StrictRedis 的子类,用于向后兼用旧版本。
    return ResultEntity.success(body=username), 200, {'Content-Type': 'application/json', 'charset': 'utf-8'}

app = Flask(__name__, static_folder="static", static_url_path="/static")
app.config['SESSION_TYPE'] = 'filesystem'
app.config['SECRET_KEY'] = b'axx1ab234szf_007'

app.register_blueprint(route_bp)

app.logger.setLevel(logging.INFO)
handler = logging.StreamHandler()
handler.setLevel(logging.INFO)
app.logger.addHandler(handler)

if __name__ == '__main__':
    # 初始化Redis
    # redis 基本命令 String
    # set(name, value, ex=None, px=None, nx=False, xx=False)
    r.set('name', 'axx1ab234szf_007')  # 设置 name 对应的值
    r.set('name', 'axx1ab234szf_007', ex=3)  # 设置过期时间为 3 秒
    r.set('name', 'axx1ab234szf_007', px=1000)  # 设置过期时间为 1000 毫秒
    r.set('name', 'axx1ab234szf_007', nx=True)  # 只有 name 不存在时,设置 name 的值
    r.set('name', 'axx1ab234szf_007', xx=True)  # 只有 name 存在时,设置 name 的值
    r.mset({'k1': '小K的名字', 'k2': '小李的名字'})  # 批量设置值

    app.run(host="0.0.0.0", port=8000)
# -*- coding: UTF-8 -*-
import logging

from flask import Blueprint
from flask import Flask

from ResultEntity import ResultEntity

import redis   # 导入redis 模块

route_bp = Blueprint('', __name__)

# redis-py 使用 connection pool 来管理对一个 redis server 的所有连接,避免每次建立、释放连接的开销。
# 默认,每个Redis实例都会维护一个自己的连接池。可以直接建立一个连接池,然后作为参数 Redis,这样就可以实现多个 Redis 实例共享一个连接池。
r = redis.StrictRedis(host='localhost', port=6379, decode_responses=True)

@route_bp.route('/')
def index():
    username = r.get('k1')
    print(type(username))  # 查看类型
    # redis 提供两个类 Redis 和 StrictRedis, StrictRedis 用于实现大部分官方的命令,Redis 是 StrictRedis 的子类,用于向后兼用旧版本。
    return ResultEntity.success(body=username), 200, {'Content-Type': 'application/json', 'charset': 'utf-8'}

app = Flask(__name__, static_folder="static", static_url_path="/static")
app.config['SESSION_TYPE'] = 'filesystem'
app.config['SECRET_KEY'] = b'axx1ab234szf_007'

app.register_blueprint(route_bp)

app.logger.setLevel(logging.INFO)
handler = logging.StreamHandler()
handler.setLevel(logging.INFO)
app.logger.addHandler(handler)

if __name__ == '__main__':
    # 初始化Redis
    # redis 基本命令 String
    # set(name, value, ex=None, px=None, nx=False, xx=False)
    r.set('name', 'axx1ab234szf_007')  # 设置 name 对应的值
    r.set('name', 'axx1ab234szf_007', ex=3)  # 设置过期时间为 3 秒
    r.set('name', 'axx1ab234szf_007', px=1000)  # 设置过期时间为 1000 毫秒
    r.set('name', 'axx1ab234szf_007', nx=True)  # 只有 name 不存在时,设置 name 的值
    r.set('name', 'axx1ab234szf_007', xx=True)  # 只有 name 存在时,设置 name 的值
    r.mset({'k1': '小K的名字', 'k2': '小李的名字'})  # 批量设置值

    app.run(host="0.0.0.0", port=8000)