1

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四大分类,分为:

  1. k-v键值
  2. 文档型数据库
  3. 列存储数据库
  4. 图关系数据库

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 2
dbsize命令:用于查看当前数据库的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 的值设为 value
hget 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"

备份

  1. 创建一个定期任务(cron job), 每小时将一个 RDB 文件备份到一个文件夹, 并且每天将一个 RDB 文件备份到另一个文件夹。
  2. 确保快照的备份都带有相应的日期和时间信息,每次执行定期任务脚本时,使用 find 命令来删除过期的快照。
  3. 至少每天一次,将 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的重写rewrite
AOF采用文件追加的方式,文件会越来越大,为避免出现这种情况,新增了重写机制,当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文件,恢复速度慢与RDB
AOF运行效率要慢于RDB,每秒同步策略较好,不同步效率和RDB相同。

如果AOF文件损坏了怎么办?
生产环境下,如果因为断电而导致AOF写入的数据不完整, 导致AOF文件出错,那么当redis重启的时候就会拒绝载入这个AOF文件,保证数据的一致性。如果发生这种情况可以使用redis自带的redis-check-aof进行修复。

  1. 为损坏的AOF文件进行备份
  2. 使用redis-check-aof -fix appendonly.aof命令进行修复
  3. 重启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事务,主从,消息订阅,事务,慢日志后续再做笔记。秃头才能变强,加油!

参考资料

  1. 惨绿少年@clsn.io:Redis数据库
  2. 菜鸟教程:Redis教程
  3. Redis 官网:https://redis.io/

syushin
948 声望316 粉丝