NoSQL 概述
为什么会出现 NoSQL
单机 Mysql 的年代,网站大多是静态网页,动态交互性网站不多,一个网站的访问量不大,用单个数据库足以应付。这种单机架构的网站,数据存储的瓶颈分为:
数据量的总大小,一个机器放不下
一个机器的内存放不下数据库的索引
访问量一个数据库承受不了
解决这种问题的技术也随之发展,比如:
Memcached(缓存)+Mysql+ 垂直拆分
Mysql 主从读写分离
分表分库 + 水平拆分 +mysql 集群
Mysql 的扩展性瓶颈 Mysql 数据库经常存储一些大文本字段,导致数据库表非常大,在做数据恢复就会很慢,不容易快速恢复数据库。关系型数据库很强大,但是不能应对所有的应用场景。如今是大数据年代,大数据下 IO 压力大,表结构更改困难,因此需要引入 Nosql。
NoSQL 是什么
NoSQL (= Not Only SQL), 意为不仅仅是 SQL。泛指非关系型数据库。随着 web2.0 网站的兴起,传统数据库在应付 web2.0 网站,特别是超大规模和高并发的 SNS 类型的 web2.0 纯动态网站显得力不从心,而非关系型数据库则由于其本身的特点得到迅速地发展。NoSql 数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,尤其大数据应用难题等。
NoSql 特性
NoSQL 种类繁多,但都有一个特点就是去掉了关系型数据库的关系特性。
数据之间无关系,易扩展
读写性能高
多样灵活的数据模型,可以随时存储自定义的数据格式
分类
NoSql 四大分类,分为:
k- v 键值
文档型数据库
列存储数据库
图关系数据库
redis 入门介绍
简介
REmote DIctionary Server(Redis) 是一个由 Salvatore Sanfilippo 写的 key-value 存储系统。Redis 是一个开源的使用 ANSIC 语言编写、遵守 BSD 协议、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API。它通常被称为数据结构服务器,因为值(value)可以是 字符串 (String), 哈希(Hash), 列表(list), 集合(sets) 和 有序集合(sorted sets) 等类型。
特性
支持数据持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载使用;
读写性能高,支持多种数据类型;
支持弱事务,消息队列、消息订阅;
支持高可用,支持分布式分片集群。
安装
官网:https://redis.io 官方下载地址:http://download.redis.io/rele…redis 命令大全:http://redisdoc.com
系统环境
[root@moli_linux1 ~]# cat /etc/redhat-release
CentOS Linux release 7.5.1804 (Core)
[root@moli_linux1 ~]# uname -r
3.10.0-862.6.3.el7.x86_64
[root@moli_linux1 ~]# hostname -I
192.168.30.3
安装 redis
[root@moli_linux1 ~]# cd /usr/local/src/
[root@moli_linux1 src]# wget http://download.redis.io/releases/redis-4.0.10.tar.gz
[root@moli_linux1 src]# tar -zxvf redis-4.0.10.tar.gz
[root@moli_linux1 src]# mv redis-4.0.10 /usr/local/redis
[root@moli_linux1 src]# cd !$
cd /usr/local/redis
[root@moli_linux1 redis]# make
至此安装完毕。安装完的命令在 /usr/local/bin 下
[root@moli_linux1 redis]# ll /usr/local/bin/
总用量 67304
-rwxr-xr-x. 1 root root 14413648 5 月 19 2018 cmake
-rwxr-xr-x. 1 root root 15543936 5 月 19 2018 cpack
-rwxr-xr-x. 1 root root 16574056 5 月 19 2018 ctest
-rwxr-xr-x 1 root root 2451240 7 月 8 2018 redis-benchmark
-rwxr-xr-x 1 root root 5768672 7 月 8 2018 redis-check-aof
-rwxr-xr-x 1 root root 5768672 7 月 8 2018 redis-check-rdb
-rwxr-xr-x 1 root root 2617272 7 月 8 2018 redis-cli
lrwxrwxrwx 1 root root 12 7 月 8 2018 redis-sentinel -> redis-server
-rwxr-xr-x 1 root root 5768672 7 月 8 2018 redis-server
拷贝配置文件,并以新的配置文件启动 redis
[root@moli_linux1 redis]$ cp redis.conf /etc/
[root@moli_linux1 redis]$ vim /etc/redis.conf
# 修改 daemonize no 为 daemonize yes
[root@moli_linux1 redis]$ redis-server /etc/redis.conf # 启动 redis
[root@moli_linux1 redis]$ ps aux | grep redis #查看进程
[root@moli_linux1 redis]$ netstat -lntp | grep redis-server
客户端连接 redis
[root@moli_linux1 redis]$ redis-cli -p 6379
127.0.0.1:6379>
基础知识
单进程 redis 是使用单进程模型来处理客户端的请求,对读写等事件的响应是通过 linux 中 epoll 函数的包装来做到的。Redis 实际的处理速度完全依靠主进程的执行效率。Epoll 是 Linux 内核为了处理大批量文件描述符而作了改进的 epoll,是 linux 下多路复用 IO 接口 select/poll 的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统 CPU 利用率。
数据库 redis 默认有 16 个数据库,类似 pyhton 列表从零开始,初始默认使用 0 号数据库。可在 redis.conf 中的 database 16 中定义。
密码 redis16 个数据库都是使用同样的密码,要么都 ok,要么都连接失败, 设置密码在 redis.conf 中的 requirepass foobared 中设置,默认是注释掉的,即没有密码。去掉注释,密码默认就是 foobared,客户端连接时使用 auth foobared 进行认证。
端口 redis 默认的端口是 6379,可以在 redis.conf 中的 prot 6379 更改
索引 redis 索引是从 0 开始的
命令 select 命令:用于切换数据库,比如切换到 2 号数据库 select 2dbsize 命令:用于查看当前数据库的 key 的数量 flushdb 命令:清空当前库 flushall 命令:清空整个 Redis 服务器的数据(删除所有数据库的所有 key)。
redis 数据类型
键 key 的操作命令
列举下常用的 key 操作命令
命令
说明
keys pattern
查找符合 pattern(正则表达式)的 key,比如 keys * 列出所有 key
del key
删除存在的 key
exists key
判断 key 是否存在
expire key seconds
给指定的 key 设置过期时间,以秒为单位
ttl key
以秒为单位,返回给定 key 的剩余生存时间(TTL, time to live)。
type key
返回 key 所储存的值的类型
几个注意点:
为 key 设置过期时间, 当 key 过期后,是直接从内存中剔除,不是说只删除了 value 而 key 还存在。
已存在的 key,重新添加重名的 key,新添加 key 的 value 会覆盖旧的 value
ttl key 命令,返回 - 1 表示这个 key 永不过期,- 2 代表已过期
字符串 string
string 类型是 redis 最基本的数据类型,一个 key 对应一个 value,并且 string 类型是二进制安全的,它可以包含任何数据,比如图片或者序列化的对象,一个 string 类型的 value 最大能存储 512M 的数据。
string 命令介绍
命令
说明
示例
set key value
设置指定 key 的值
set k1 v1
get key
获取 key 的值
get k1
append key value
如果 key 已经存在并且是一个字符串,APPEND 命令将指定的 value 追加到该 key 原来值 value 的末尾
append k1 v1
strlen key
返回 key 所储存的字符串值的长度
strlen k1
incr key
将 key 中储存的数字值增一(要求 value 必须为数字)
incr k2
decr key
将 key 中储存的数字值减一(要求 value 必须为数字)
decr k2
incrby key increment
将 key 所储存的值加上给定的增量值
incrby k2 3
decrby key increment
将 key 所储存的值减上给定的增量值
decrby k2 3
getrange key start end
返回 key 中字符串值的子字符
getrange k3 0 -1
setrange key offset valye
用 value 参数覆写给定 key 所储存的字符串值,从偏移量 offset 开始
setrange k3 0 aaa
setex key seconds value
设置 key 的过期时间为 seconds
setex k4 10 hello
setnx key value
当 key 不存在时才设置 key 的值
set k5 v5
mset
同时设置一个或多个 key-value 对
mset k6 v6 k7 v7
mget
获取所有 (一个或多个) 给定 key 的值
mget k1 k2 k3
msetnx
同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在
msetnx k8 v8 k9 v9
getset
将给定 key 的值设为 value,并返回 key 的旧值(old value)
getset k1 v11
示例 1,set/get/append/strlen
127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> get k1
“v1”
127.0.0.1:6379> append k1 v11 # 在原先 k1 的值 v1 后添加 v11
(integer) 5
127.0.0.1:6379> get k1
“v1v11”
127.0.0.1:6379> strlen k1
(integer) 5 # v1v11 总共 5 个字符
127.0.0.1:6379>
示例 2,incr/decr/incrby/decrby
127.0.0.1:6379> set k2 1 # 设置 k2 的值为 1
OK
127.0.0.1:6379> get k2
“1”
127.0.0.1:6379> incr k2 # incr 命令使 key 的值加 1
(integer) 2
127.0.0.1:6379> get k2
“2” # 1+1 = 2
127.0.0.1:6379> decr k2 # decr 命令使 key 的值减 1
(integer) 1
127.0.0.1:6379> get k2
“1” # 变为 1
127.0.0.1:6379> incrby k2 5 # incrby 命令使 key 的值加上后面定义的数字
(integer) 6
127.0.0.1:6379> get k2
“6” # 1+5 = 6
127.0.0.1:6379> decrby k2 5 # decrby 与 incrby 相反
(integer) 1
127.0.0.1:6379> get k2
“1” # 6-5 = 1
示例 3,getrange/setrange
127.0.0.1:6379> set k3 abc123456
OK
127.0.0.1:6379> get k3
“abc123456”
127.0.0.1:6379> getrange k3 0 3 # 返回从 0 开始到 3 的字符串
“abc1”
127.0.0.1:6379> getrange k3 0 -1 # 返回整个字符串
“abc123456”
127.0.0.1:6379> setrange k3 0 qwe # 将从 0 开始的字符串覆盖为 qwe
(integer) 9
127.0.0.1:6379> get k3
“qwe123456”
127.0.0.1:6379> setrange k3 9 777
(integer) 12
127.0.0.1:6379> get k3
“qwe123456777”
127.0.0.1:6379>
示例 4,setex/setnx
127.0.0.1:6379> setex k4 10 v4 # 设置 k4 的过期时间为 10s
OK
127.0.0.1:6379> ttl k4 # 还有 7s 过期
(integer) 7
127.0.0.1:6379> get k4
“v4”
127.0.0.1:6379> ttl k4
(integer) -2 # 已过期
127.0.0.1:6379> get k4
(nil)
127.0.0.1:6379>
127.0.0.1:6379> get k1
“v1v11”
127.0.0.1:6379> setnx k1 hello # k1 已存在,这条命令不会改变 k1 的值
(integer) 0
127.0.0.1:6379> get k1
“v1v11”
127.0.0.1:6379> setnx k5 hello # k5 不存在,因此新建并赋值
(integer) 1
127.0.0.1:6379> get k5
“hello”
127.0.0.1:6379>
示例 5,mset/mget/msetnx
127.0.0.1:6379> mset a1 dog a2 cat # 批量设置 key-value
OK
127.0.0.1:6379> mget a1 a2 # 批量获取 value
1) “dog”
2) “cat”
127.0.0.1:6379> msetnx a1 panda a3 lion # 其中 a1 以存在,这条命令执行不成功
(integer) 0
127.0.0.1:6379> mget a1 a2 a3
1) “dog”
2) “cat”
3) (nil)
127.0.0.1:6379> msetnx a3 panda a4 lion # a3 和 a4 都不存在,因此设置 key-value 成功
(integer) 1
127.0.0.1:6379> mget a1 a2 a3 a4
1) “dog”
2) “cat”
3) “panda”
4) “lion”
127.0.0.1:6379>
哈希 hash
hash 类型是一个键值对的集合,可以将 hash 看做是 string key 和 string value 的映射表,它很适合存储对象。每一个 Hash 可以存储 995701749 个键值对。k- v 模式不变,但是 value 是键值对
常用命令 hset key field value: 将哈希表 key 中的字段 field 的值设为 valuehget key field:获取存储在哈希表中指定字段的值。hmset key field value:同时将多个键值对添加到 hash 表 key 中 hmget key field1 field2:获取给定字段的值 hgetall key:获取在哈希表中指定 key 的所有字段和值 hdel key field1 field2: 删除一个或多个哈希表字段
127.0.0.1:6379> hset k1 user id11 # 设置哈希表 k1 中 user 字段的值为 id11
(integer) 1
127.0.0.1:6379> hget k1 user # 获取哈希表 k1 中 user 字段的值
“id11”
127.0.0.1:6379> hmset k1 age 22 name laowan passwd 123456 # 批量设置哈希表中的字段与值
OK
127.0.0.1:6379> hmget k1 age name passwd # 批量获取
1) “22”
2) “laowan”
3) “123456”
127.0.0.1:6379> hgetall k1 # 批量获取键值对
1) “user”
2) “id11”
3) “age”
4) “22”
5) “name”
6) “laowan”
7) “passwd”
8) “123456”
127.0.0.1:6379> hdel k1 passwd # 删除哈希表中的字段 passwd
(integer) 1
127.0.0.1:6379> hgetall k1 # 已删除
1) “user”
2) “id11”
3) “age”
4) “22”
5) “name”
6) “laowan”
127.0.0.1:6379>
hlen key: 获取哈希表中字段的数量 hexists key field:查看哈希表中指定的字段是否存在 hkeys key:获取哈希表中所有字段 hvals key:获取哈希表中所有的值 hincrby key field increment: 为哈希表 key 中的指定字段的整数值加上增量 increment hincrbyfloat key field increment: 为哈希表 key 中的指定字段的浮点数值加上增量 increment。hsetnx key field value: 只有在字段 field 不存在时,设置哈希表字段的值。
列表 list
Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。
list 常用命令 lpush key value1 value2: 将一个或多个值插入列表头部 rpush key value1 value2: 在列表中添加一个或多个值 lrange key start end: 获取列表指定范围内的值
127.0.0.1:6379> lpush list1 1 2 3 4 5
(integer) 5
127.0.0.1:6379> lrange list1 0 -1
1) “5”
2) “4”
3) “3”
4) “2”
5) “1”
127.0.0.1:6379> rpush list2 1 2 3 4 5
(integer) 5
127.0.0.1:6379> lrange list2 0 -1
1) “1”
2) “2”
3) “3”
4) “4”
5) “5”
127.0.0.1:6379>
lpop key: 移出并获取列表的第一个元素 rpop key: 移出并获取列表的最后一个元素
127.0.0.1:6379> lpop list1
“5”
127.0.0.1:6379> lpop list2
“1”
127.0.0.1:6379> lrange list1 0 -1
1) “4”
2) “3”
3) “2”
4) “1”
127.0.0.1:6379> lrange list2 0 -1
1) “2”
2) “3”
3) “4”
4) “5”
127.0.0.1:6379> rpop list1
“1”
127.0.0.1:6379> rpop list2
“5”
127.0.0.1:6379>
lindex key index: 按照索引下标获取元素 llen key: 获取列表的长度
127.0.0.1:6379> lrange list1 0 -1
1) “4”
2) “3”
3) “2”
127.0.0.1:6379> lindex list1 0
“4”
127.0.0.1:6379> lindex list1 1
“3”
127.0.0.1:6379> lindex list1 2
“2”
127.0.0.1:6379> lindex list1 3
(nil)
127.0.0.1:6379> llen list1
(integer) 3
lrem key count value: 移除列表元素
127.0.0.1:6379> rpush list3 1 1 1 2 2 2 3 3 3 4 5 6
(integer) 12
127.0.0.1:6379> lrange list3 0 -1
1) “1”
2) “1”
3) “1”
4) “2”
5) “2”
6) “2”
7) “3”
8) “3”
9) “3”
10) “4”
11) “5”
12) “6”
127.0.0.1:6379> lrem list3 2 3 # 删除列表中 2 个 3
(integer) 2
127.0.0.1:6379> lrange list3 0 -1
1) “1”
2) “1”
3) “1”
4) “2”
5) “2”
6) “2”
7) “3” # 原先列表有 3 个 3,删除了 2 个
8) “4”
9) “5”
10) “6”
127.0.0.1:6379>
ltrim key start end: 截取列表指定范围的值后再赋值给列表
127.0.0.1:6379> lpush list4 1 2 3 4 5 6
(integer) 6
127.0.0.1:6379> lrange list4 0 -1
1) “6”
2) “5”
3) “4”
4) “3”
5) “2”
6) “1”
127.0.0.1:6379> ltrim list4 2 4 # 截取索引为 2 到 4 范围内的值(也就是 4,3,2),再赋值给 list4
OK
127.0.0.1:6379> lrange list4 0 -1 # 所以 list4 的值为 4,3,2
1) “4”
2) “3”
3) “2”
127.0.0.1:6379>
linsert key BEFORE|AFTER pivot value : 在列表的元素前或者元素后插入元素
127.0.0.1:6379> lrange list4 0 -1
1) “4”
2) “3”
3) “2”
127.0.0.1:6379> LINSERT list4 after 3 redis # 在列表的元素 3 后面插入元素 redis
(integer) 4
127.0.0.1:6379> lrange list4 0 -1
1) “4”
2) “3”
3) “redis”
4) “2”
127.0.0.1:6379> LINSERT list4 before 3 mongodb # 在列表元素 3 前面插入元素 mongodb
(integer) 5
127.0.0.1:6379> lrange list4 0 -1
1) “4”
2) “mongodb”
3) “3”
4) “redis”
5) “2”
127.0.0.1:6379>
关于 list 的小总结:
它是一个字符串链表,左右都可以插入添加
如果键不存在,则创建新的链表
如果键存在,就新增元素
如果值全部移除,对应的键也就消失
链表的操作无论是头部还是尾部效率都极高,但是对中间元素操作,效率就比较低。
集合 set
Redis 的 Set 是 string 类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。
set 常用命令 sadd key m1 m2: 向集合添加一个或多个成员 smembers key:返回集合中所有的成员 sismenber key member:判断 member 元素是否是集合 key 的成员 scard key:获取集合里面元素的数量 srem key value:删除集合中的元素
127.0.0.1:6379> sadd set01 a a b b c c # 因为不能出现重复数据,因此只有三个
(integer) 3
127.0.0.1:6379> smembers set01 # 顺序也不是 abc,因为是无序的
1) “a”
2) “c”
3) “b”
127.0.0.1:6379> SISMEMBER set01 a # a 元素存在集合 set01 里
(integer) 1
127.0.0.1:6379> SISMEMBER set01 d # d 元素不存在 set01 里
(integer) 0
127.0.0.1:6379> scard set01 # 获取集合 set01 的数量为 3 个
(integer) 3
127.0.0.1:6379> SREM set01 a # 删除集合 set01 中的元素 a
(integer) 1
127.0.0.1:6379> SMEMBERS set01 # a 元素没了
1) “c”
2) “b”
srandmember key number: 返回集合中 number 个随机数
127.0.0.1:6379> sadd set02 a b c d e f g # 集合中有 7 个元素
(integer) 7
127.0.0.1:6379> SRANDMEMBER set02 3 # 随机出 3 个元素
1) “g”
2) “d”
3) “b”
127.0.0.1:6379> SRANDMEMBER set02 3
1) “a”
2) “c”
3) “f”
127.0.0.1:6379> SRANDMEMBER set02 3
1) “c”
2) “b”
3) “f”
127.0.0.1:6379>
smove source destination member : 将 member 元素从 source 集合移动到 destination 集合
127.0.0.1:6379> sadd key3 ‘hello’ ‘world’ ‘foo’
(integer) 3
127.0.0.1:6379> sadd key4 ‘bar’
(integer) 1
127.0.0.1:6379> SMOVE key3 key4 ‘foo’
(integer) 1
127.0.0.1:6379> SMEMBERS key3
1) “world”
2) “hello”
127.0.0.1:6379> SMEMBERS key4
1) “foo”
2) “bar”
127.0.0.1:6379>
sdiff key1 key2: 差集, 返回给定集合之间的差集。不存在的集合 key 将视为空集。sinter key1 key2: 交集, 不存在的集合 key 被视为空集。当给定集合当中有一个空集时,结果也为空集 sunion key1 key2: 并集, 不存在的集合 key 被视为空集
127.0.0.1:6379> sadd key1 a b c
(integer) 3
127.0.0.1:6379> sadd key2 a d c
(integer) 3
127.0.0.1:6379> SDIFF key1 key2
1) “b”
127.0.0.1:6379> SDIFF key2 key1
1) “d”
127.0.0.1:6379> SINTER key1 key2
1) “a”
2) “c”
127.0.0.1:6379> SUNION key1 key2
1) “a”
2) “d”
3) “b”
4) “c”
有序集合 zset
Redis zset 和 set 一样也是 string 类型元素的集合, 且不允许重复的成员。不同的是每个元素都会关联一个 double 类型的分数。redis 正是通过分数来为集合中的成员进行从小到大的排序。zset 的成员是唯一的, 但分数 (score) 却可以重复。在 set 的基础上,加了一个 score 值,set 是 k1 v1 k2 v2,zset 是 k1 score1 v1 k2 score2 v2.
常用命令 zadd key score1 member1 [score2 member2]: 向有序集合添加一个或多个成员,或者更新已存在成员的分数 zrange key start stop [WITHSCORES]:通过索引区间返回有序集合成指定区间内的成员
127.0.0.1:6379> zadd k1 60 v1 70 v2 80 v3 90 v4 100 v5 # 创建有序集合 k1 和多个成员
(integer) 5
127.0.0.1:6379> zrange k1 0 -1 # 显示成员
1) “v1”
2) “v2”
3) “v3”
4) “v4”
5) “v5”
127.0.0.1:6379> zrange k1 0 -1 withscores # 显示分数与成员
1) “v1”
2) “60”
3) “v2”
4) “70”
5) “v3”
6) “80”
7) “v4”
8) “90”
9) “v5”
10) “100”
127.0.0.1:6379>
zrangebyscore key min max [WITHSCORES] [LIMIT] : 通过分数返回有序集合指定区间内的成员参数 withscore:显示分数(指不包含 limit:从索引值开始选取几个,比如 limit 2 2 指从下标为 2 的值开始选取 2 个值
127.0.0.1:6379> ZRANGEBYSCORE k1 60 90
1) “v1”
2) “v2”
3) “v3”
4) “v4”
127.0.0.1:6379> ZRANGEBYSCORE k1 60 (90 # 分数相当于选取 60<=x<90 中的 x 值
1) “v1”
2) “v2”
3) “v3”
127.0.0.1:6379> ZRANGEBYSCORE k1 (60 (90 # 分数大于 60,小于 90 之间的值
1) “v2”
2) “v3”
127.0.0.1:6379> ZRANGEBYSCORE k1 (60 90
1) “v2”
2) “v3”
3) “v4”
127.0.0.1:6379> ZRANGEBYSCORE k1 60 90 limit 2 2
1) “v3”
2) “v4”
ZREM key member [member …] : 移除有序集合中的一个或多个成员
127.0.0.1:6379> zrem k1 v5 # 删除有序集合 k1 中的值 v5
(integer) 1
127.0.0.1:6379> zrange k1 0 -1 withscores
1) “v1”
2) “60”
3) “v2”
4) “70”
5) “v3”
6) “80”
7) “v4”
8) “90”
127.0.0.1:6379>
zcard key: 获取有序集合的成员数 zcount key min max: 计算在有序集合中指定区间分数的成员数 zrank key member: 返回有序集合中指定成员的索引 zscore key member: 返回有序集中,成员的分数值
127.0.0.1:6379> zrange k1 0 -1 withscores
1) “v1”
2) “60”
3) “v2”
4) “70”
5) “v3”
6) “80”
7) “v4”
8) “90”
127.0.0.1:6379> ZCARD k1 # 有序集合 k1 中,有四个成员
(integer) 4
127.0.0.1:6379> zcount k1 60 80 # 成员在 60 分到 80 分之间的有三个值(包含 60 和 80)
(integer) 3
127.0.0.1:6379> zcount k1 60 70 # 成员在 60 到 70 之间的有 2 个
(integer) 2
127.0.0.1:6379> zrank k1 v4 # 成员 v4 在有序集合中索引值为 3
(integer) 3
127.0.0.1:6379> zscore k1 v4 # 成员 v4 在有序集合中的分数是 90
“90”
127.0.0.1:6379>
命令很多,多多练习,不必死记硬背。
redis.conf 配置文件解析
redis.conf 的路径通常在安装目录下。不过 linux 中通常不会直接修改本身的配置文件,而是拷贝一份进行配置。
reids 中常用的配置
是否后台运行,默认为 no
daemonize yes
设置 tcp 的 backlog,非高并发环境默认即可。
tcp-backlog 511
backlog 是一个连接队列,队列总和 = 未完成三次握手队列 + 已完成三次握手队列。高并发环境下需要一个高的 backlog 值来避免客户端连接过慢的问题。注意 Linux 内核会将这个值减小到 /proc/sys/net/core/somaxconn 的值,所以需要确认增大 somaxconn 和 tcp_max_syn_backlog 这两个值。
默认端口
port 6379
指定 IP 监听
bind 127.0.0.1 ip2 ip3 ip4
定义 redis 进程 PID 文件
pidfile /var/run/redis_6379.pid
设置 redis 日志级别
loglevel notice
Redis 默认有四种日志级别 debug (a lot of information, useful for development/testing)# 开发测试阶段使用 verbose (many rarely useful info, but not a mess like the debug level)# notice (moderately verbose, what you want in production probably) warning (only very important / critical messages are logged) #生产中使用
日志文件位置
logfile /var/log/redis.log
redis 数据库数量,默认 16 个,redis-cli 中使用 select 切换数据库
databases 16
设置密码, 默认是注释的即没密码,设置密码打开注释,修改 foobared 即可 redis-cli 中使用 auth password 进行认证
requirepass foobared
禁止 protected-mode
protected-mode yes/no(保护模式,是否只允许本地访问)
AOF 日志开关是否打开
appendonly no/yes
指定 AOF 日志的文件名
appendfilename appendonly.aof
指定更新日志的条件,有 3 个可选值
no: 表示等操作系统进行数据缓存同步到磁盘(快)
always: 表示每次更新操作后调用 fsync()将数据写到磁盘(慢,安全)
everysec: 表示每秒同步一次(默认值)
指定 Redis 最大内存限制,Redis 在启动时会把数据加载到内存中,达到最大内存后,Redis 会尝试清除已到期或者即将到期的 key。当此方法处理后,仍然达到最大内存设置,将无法进行写入操作,但可以进行读取操作。Redis 新的 vm 机制,会将 key 存放内存,value 存放 swap 区。
maxmemory <bytes>
当客户端闲置多长时间后关闭连接,如果指定为 0,表示关闭改功能
timeout 300
RDB 持久化策略
RDB 持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是 SNAPSHOT 快照,它恢复时是将快照文件直接读取到内存里。
优点 Redis 会单独创建 (fork) 一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上一次持久化好的文件。整个过程中,主进程不进行任何 IO 操作,确保了极高的性能。如果需要进行大规模数据的恢复,且对于数据恢复的完整性不那么敏感,那么 RDB 比 AOF 会高效许多。
缺点最后一次持久化的数据可能丢失。每次保存 RDB 的时候,Redis 都要 fork() 出一个子进程,并由子进程来进行实际的持久化工作。在数据集比较庞大时,fork() 可能会非常耗时,造成服务器在某某毫秒内停止处理客户端;如果数据集非常巨大,并且 CPU 时间非常紧张的话,那么这种停止时间甚至可能会长达整整一秒。虽然 AOF 重写也需要进行 fork(),但无论 AOF 重写的执行间隔有多长,数据的耐久性都不会有任何损失。
RDB 持久化配置修改 redis.conf 配置文件
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
dir /data/redis/
对应的含义是:
900 秒内有 1 次更改
300 秒内有 10 次更改
60 秒内有 10000 次更改
就将内存中的数据写入到 dump.rdb 文件中。
后台备份进程出错时, 主进程停不停止写入? 主进程不停止容易造成数据不一致
导出的 rdb 文件是否压缩 如果 rdb 的大小很大的话建议这么做
导入 rbd 恢复时数据时, 要不要检验 rdb 的完整性 验证版本是不是一致
导出来的 rdb 文件名
rdb 的放置路径
如果不设置 RDB 持久化只需要更改为 save “” 即可。注意:在 redis-cli 中执行 flushall 会立刻刷新 dump.rdb 文件。
如何恢复之前的数据假如已经保存了一个 dump.rdb 文件,但是 redis-cli 中执行了一次 flushall 命令,此时内存中已经没有任何数据了,但是又想恢复到原来的数据,应该怎么做?做个小测试:先修改 redis.conf 配置文件
save 30 5 # 即 30 秒内有 5 次更改就将内存的数据写入 dump.rdb
然后 redis-cli 里在 30s 内写入 5 个数据。
127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> set k2 v2
OK
127.0.0.1:6379> set k3 v3
OK
127.0.0.1:6379> set k4 v4
OK
127.0.0.1:6379> set k5 v5
OK
退出后可以看到新生成的 dump.rdb 文件
[root@moli_linux1 redis]# ll
总用量 4
-rw-r–r– 1 root root 133 3 月 27 14:52 dump.rdb # 注意此处的时间
[root@moli_linux1 redis]# cp dump.rdb dump.rdb.bak # 将这个文件备份
生产中应该讲备份文件放置在其他机器,测试所用就放在本地上了。再次进入 redis-cli 执行 flushall 命令
127.0.0.1:6379> flushall
OK
127.0.0.1:6379> exit
[root@moli_linux1 redis]# ll dump.rdb
-rw-r–r– 1 root root 93 3 月 27 14:56 dump.rdb # 立刻新生成的 dump.rdb 文件
此时重启 redis 服务,之前的持久化数据不会保存,因为重启加载的文件还是 flushall 后的 dump.rdb 文件。要想恢复之前的数据只要把保存的 dump.rdb 文件重新加载进去即可。
[root@moli_linux1 redis]# rm -f dump.rdb # 删除 flushall 保存的 dump.rdb
[root@moli_linux1 redis]# mv dump.rdb.bak dump.rdb # 把备份的文件更改为 redis 启动时读取的文件
[root@moli_linux1 redis]# redis-server /etc/redis.conf
9377:C 27 Mar 15:07:11.066 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
9377:C 27 Mar 15:07:11.066 # Redis version=4.0.10, bits=64, commit=00000000, modified=0, pid=9377, just started
9377:C 27 Mar 15:07:11.066 # Configuration loaded
[root@moli_linux1 redis]# redis-cli
127.0.0.1:6379> keys * # 数据恢复
1) “k5”
2) “k3”
3) “k2”
4) “k1”
5) “k4”
备份
创建一个定期任务(cron job),每小时将一个 RDB 文件备份到一个文件夹,并且每天将一个 RDB 文件备份到另一个文件夹。
确保快照的备份都带有相应的日期和时间信息,每次执行定期任务脚本时,使用 find 命令来删除过期的快照。
至少每天一次,将 RDB 备份到你的数据中心之外,或者至少是备份到你运行 Redis 服务器的物理机器之外。
AOF 持久化策略
AOF 以日志的形式来记录每个写操作,将 Redis 执行过的所有写命令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis 启动时会读取该文件重新构建数据,会将文件中的命令重新执行一遍以完成数据恢复。AOF 保存的是 appendonly.aof 文件。
AOF 配置
appendonly no
appendfilename “appendonly.aof”
appendfsync always
appendfsync everysec
appendfsync no
no-appendfsync-on-rewrite yes/no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
对于配置分别表示
是否开启 aof 日志功能
aof 文件名
同步持久化,每次发生数据变更就会被立刻记录到磁盘,性能较差但数据完整性
出厂默认配置,异步操作,每秒记录,如果一秒内宕机,有数据丢失
写入工作交给操作系统, 由操作系统判断缓冲区大小, 统一写入到 aof.
重写时是否可以运用 Appendfsync,用默认 no 即可,保证数据安全性
aof 文件大小比起上次重写时的大小, 增长率 100%(新 =2* 旧)时重写, 缺点: 业务开始的时候,会重复重写多次。
aof 文件, 至少超过 64M 时, 重写
关于 AOF 的重写 rewriteAOF 采用文件追加的方式,文件会越来越大,为避免出现这种情况,新增了重写机制,当 AOF 文件的大小超过所定的阈值,redis 就会启动 AOF 文件内容压缩,只保留可以恢复数据的最小指令集,可以使用命令 bgrewriteaof
AOF 的重写原理(减肥计划):AOF 文件持续增大而过大,会 fork 出一条新进程来将文件重写(先写临时文件最后再 rename),遍历新进程中的内存数据,每条记录有一条 set 语句。重写 aof 文件的操作,并没有读取旧的 aof 文件。而是将整个内存中的数据库内容用命令的方式重写一个新的 aof 文件。
AOF 的触发机制:reids 会记录上次重写 aof 文件的大小,默认配置是当 aof 文件大小是上次 rewrite 后大小的一倍且文件大小大于 64M 时触发。
AOF 优点使用 AOF 会让你的 Redis 更加耐久: 你可以使用不同的 fsync 策略:无 fsync, 每秒 fsync, 每次写的时候 fsync. 使用默认的每秒 fsync 策略,Redis 的性能依然很好(fsync 是由后台线程进行处理的, 主线程会尽力处理客户端请求), 一旦出现故障,你最多丢失 1 秒的数据.Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写:重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。整个重写操作是绝对安全的,因为 Redis 在创建新 AOF 文件的过程中,会继续将命令追加到现有的 AOF 文件里面,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。
一旦新 AOF 文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。
AOF 文件有序地保存了对数据库执行的所有写入操作,这些写入操作以 Redis 协议的格式保存,因此 AOF 文件的内容非常容易被人读懂,对文件进行分析(parse)也很轻松。导出(export)AOF 文件也非常简单:举个例子,如果你不小心执行了 FLUSHALL 命令,但只要 AOF 文件未被重写,那么只要停止服务器,移除 AOF 文件末尾的 FLUSHALL 命令,并重启 Redis,就可以将数据集恢复到 FLUSHALL 执行之前的状态。
AOF 缺点对于相同数据集的数据而言,AOF 文件要远大于 RDB 文件,恢复速度慢与 RDBAOF 运行效率要慢于 RDB,每秒同步策略较好,不同步效率和 RDB 相同。
如果 AOF 文件损坏了怎么办?生产环境下,如果因为断电而导致 AOF 写入的数据不完整,导致 AOF 文件出错,那么当 redis 重启的时候就会拒绝载入这个 AOF 文件,保证数据的一致性。如果发生这种情况可以使用 redis 自带的 redis-check-aof 进行修复。
为损坏的 AOF 文件进行备份
使用 redis-check-aof -fix appendonly.aof 命令进行修复
重启 redis 服务器,等待载入 aof 文件,完成数据恢复
dump.rdb 和 appendonly.aof 文件能否共存?先看下配置文件的注释
AOF and RDB persistence can be enabled at the same time without problems. If the AOF is enabled on startup Redis will load the AOF, that is the file with the better durability guarantees.
翻译过来大概是:RDB 和 AOF 持久化策略可以同时开启,如果 AOF 和 RDB 都开启,那么 redis 服务器每次重启时都会优先使用 AOF 文件恢复数据集。
如何选择 RDB 还是 AOF
redis 事务,主从,消息订阅,事务,慢日志后续再做笔记。秃头才能变强,加油!
参考资料
惨绿少年 @clsn.io:Redis 数据库
菜鸟教程:Redis 教程
Redis 官网:https://redis.io/