Redis入门笔记

Redis入门

2019年11月4日08:17:37

简介

Redis是一个开源的高性能的key-value式的存储系统,使用ANSI C编写,可基于内存,可持久化的日志型、key-value型的数据库。通常被称为数据结构服务器。

使用场景

1、性能:碰到耗时较久或结果不会频繁改动的sql就可以使用Redis,将Redis作为缓存使用,使用户的请求迅速响应。

2、并发:在高并发情况下,所有的请求都去访问数据库,数据库会因为连接过多而异常,所以这时候需要用Redis做一个缓冲的操作,让请求先到Redis。

支持的数据类型

相比于memcache,Redis支持的数据类型比它要多得多。Redis目前支持的数据类型有String(字符串),Hash(哈希),List(链表),Sets(集合),Zsets(有序集合)等。

优缺点

1、优点

快速:Redis的处理速度快,每秒能执行约11万集合,约80000多条记录。

多种数据类型:支持基本上所有的数据类型。

原子性:每个操作都是原子的,能够保证两个客户端同时访问Redis的时候能够取到最新的值。

多功能:可以用作缓存,消息中间件,队列。

2、缺点

单线程,耗内存。

安装Redis

1、编译

下载安装Redis的安装包

wget http://download.redis.io/releases/redis-5.0.6.tar.gz

解压安装包

tar zxf redis-5.0.6.tar.gz

进入到解压后的文件夹

Redis入门笔记

ps:编译前,服务器需要安装gcc

yum install gcc-c++

输入make命令即可编译,出现下图提示表示编译成功

Redis入门笔记

也可以指定编译位置

make install PREFIX=path

编译后的文件

Redis入门笔记

复制一份解压包里面的redis.conf到bin目录下。

2、启动

使用redis-server redis.conf即可启动Redis服务器。

[root@iz2zeaf5jdjve80rjlsjgnz bin]# ./redis-server redis.conf
8389:C 04 Nov 2019 08:35:37.743 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
8389:C 04 Nov 2019 08:35:37.743 # Redis version=5.0.6, bits=64, commit=00000000, modified=0, pid=8389, just started
8389:C 04 Nov 2019 08:35:37.743 # Configuration loaded
8389:M 04 Nov 2019 08:35:37.745 * No cluster configuration found, I'm 7e0945b7d634496ad0d57cdd7d53c0be4b39b10c
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 5.0.6 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                   
 (    '      ,       .-`  | `,    )     Running in cluster mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 7007
 |    `-._   `._    /     _.-'    |     PID: 8389
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           http://redis.io        
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               

8389:M 04 Nov 2019 08:35:37.748 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
8389:M 04 Nov 2019 08:35:37.748 # Server initialized
8389:M 04 Nov 2019 08:35:37.748 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
8389:M 04 Nov 2019 08:35:37.748 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.
8389:M 04 Nov 2019 08:35:37.748 * Ready to accept connections

默认是使用前台启动的,可以通过修改配置文件来改成后台启动。

将redis.conf里面的daemonize属性改为yes。

daemonize yes

启动成功后就可以开始进行一些简单的测试,使用命令redis-cli进入redis。

[root@iz2zeaf5jdjve80rjlsjgnz bin]# redis-cli
127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379> set str string
OK
127.0.0.1:6379> get str
"string"
127.0.0.1:6379> quit

数据类型详解

1、String

String是Redis最基本的数据类型,因为使用了SDS(简单动态字符串)的原因,它是二进制安全的。

String可以存储任何数据,包括图片等其他序列化后的对象。最大上限是1G。


set key value:设定key的值。

get key:获取key的值。

[root@iz2zeaf5jdjve80rjlsjgnz bin]# redis-cli -p 7007 -c
127.0.0.1:7007> set str string
OK
127.0.0.1:7007> keys *
1) "str"
127.0.0.1:7007> get str
"string"
127.0.0.1:7007> 

getset key value:将给定的key的值修改成value,并返回旧值。

127.0.0.1:7007> getset str rlxy93
"string"
127.0.0.1:7007> get str
"rlxy93"
127.0.0.1:7007> 

mget key[key]:获取一个或多个key值。

127.0.0.1:7007> mget str str1 str2
1) "rlxy93"
2) "str1"
3) "str2"
127.0.0.1:7007> 

setex key seconds value:设定key的值,并设置过期时间。

ttl key:查看key的过期时间,单位是秒。

127.0.0.1:7007> setex string 5 string
OK
127.0.0.1:7007> ttl string
(integer) 3
127.0.0.1:7007> keys *
1) "str1"
2) "str"
3) "str2"
127.0.0.1:7007> 

setnx key value:当key不存在时才设定key的值。

127.0.0.1:7007> keys *
1) "str1"
2) "str"
3) "str2"
127.0.0.1:7007> setnx str string
(integer) 0
127.0.0.1:7007> setnx string string
(integer) 1

strlen key:返回key值的长度。

127.0.0.1:7007> strlen string
(integer) 6

mset key value[key value]:同时设置一个或多个键值。

127.0.0.1:7007> mset str3 str3 str4 str4 str5 str5
OK
127.0.0.1:7007> mget str3 str4 str5
1) "str3"
2) "str4"
3) "str5"

msetnx key value[key value]:当所有key都不存在时同时设置一个或多个键值,如果有一个存在则添加key值不成功。

127.0.0.1:7007> msetnx str5 str5 str6 str6
(integer) 0
127.0.0.1:7007> keys *
1) "str5"
2) "str1"
3) "str2"
4) "string"
5) "str4"
6) "str"
7) "str3"
127.0.0.1:7007> msetnx str6 str6 str7 str7 str8 str8
(integer) 1
127.0.0.1:7007> keys *
 1) "str6"
 2) "str8"
 3) "str7"

psetex key milliseconds value:和setnx相似,但是它设置的是毫秒时间。

127.0.0.1:7007> psetex str9 5000 str9
OK
127.0.0.1:7007> ttl str9
(integer) 3

incr key:将key值存储的数字+1。

127.0.0.1:7007> set str10 10
OK
127.0.0.1:7007> get str10
"10"
127.0.0.1:7007> incr str10
(integer) 11

decr key:将key值存储的数字-1。

127.0.0.1:7007> decr str10
(integer) 10

decrby key decrement:key存储的值减去给定的值。

127.0.0.1:7007> decrby str10 10
(integer) 0

append key value:如果key存在并且是个字符串,该命令则在key的值后面追加value。

127.0.0.1:7007> append str5 str5
(integer) 8
127.0.0.1:7007> get str5
"str5str5"

2、Hash

Hash是一个String类型的field-value域值型的映射表。

Hash适合用来存储对象,和将对象的每个字段存成String类型,将对象存储在Hash类型中会占用更少的内存,并且可以很方便的存取整个对象。


hset key field value:将哈希表key中的字段field设置为value。

127.0.0.1:7007> hset hash str1 str1
(integer) 1
127.0.0.1:7007> hkeys hash
1) "str1"

hdel key field[field]:删除一个或多个哈希表字段。

127.0.0.1:7007> hdel hash str1
(integer) 1
127.0.0.1:7007> hkeys hash
(empty list or set)

hexists key field:查看指定的字段是否存在。

127.0.0.1:7007> hexists hash str1
(integer) 0
127.0.0.1:7007> hset hash str2 str2
(integer) 1
127.0.0.1:7007> hexists hash str2
(integer) 1

hget key field:获取指定字段的值。

127.0.0.1:7007> hget hash str2
"str2"

hgetall key:获取指定key中所有字段的值。

127.0.0.1:7007> hgetall hash
1) "str2"
2) "str2"
3) "str1"
4) "str1"

hincrby key field increment:给哈希表key中指定字段的整数值加上指定的值。

127.0.0.1:7007> hset hash age 20
(integer) 1
127.0.0.1:7007> hincrby hash age 10
(integer) 30
127.0.0.1:7007> hget hash age
"30"

hincrbyfloat key field increment:给哈希表key中指定字段的浮点数值加上指定的值。

127.0.0.1:7007> hincrbyfloat hash weight 0.5
"119.9"
127.0.0.1:7007> hget hash weight
"119.9"

hkeys key:获取所有哈希表中的字段。

127.0.0.1:7007> hkeys hash
1) "str2"
2) "str1"
3) "age"
4) "weight"

hlen key:获取哈希表中字段的数量。

127.0.0.1:7007> hlen hash
(integer) 4

hmget key field[field]:获取所有给定字段的值。

127.0.0.1:7007> hmget hash str1 weight
1) "str1"
2) "119.9"

hmset key field value[field value]:将一个或多个field-value域值对添加到哈希表的key中。

127.0.0.1:7007> hmset hash name rlxy93 address chongqing
OK
127.0.0.1:7007> hkeys hash
1) "str2"
2) "str1"
3) "age"
4) "weight"
5) "name"
6) "address"

hsetnx key field value:只有字段field不存在时,设置哈希表中字段的值。

127.0.0.1:7007> hsetnx hash str1 string
(integer) 0
127.0.0.1:7007> hsetnx hash id 1
(integer) 1

hvals key:获取哈希表中所有的值。

127.0.0.1:7007> hvals hash
1) "str2"
2) "str1"
3) "30"
4) "119.9"
5) "rlxy93"
6) "chongqing"
7) "1"

3、List

List类型其实是每个子元素都是String类型的双向链表,可以在链表的两头存取元素。


lpush key value[value]:将一个或多个值插入到key列表的头部。

lrange key start stop:获取列表指定范围内的元素。

127.0.0.1:7007> lpush list list1 list 2 list3
(integer) 4
127.0.0.1:7007> lrange list 0 -1
1) "list3"
2) "2"
3) "list"
4) "list1"

lpushx key value:将一个值插入到已存在的列表头部。

127.0.0.1:7007> lpushx list head
(integer) 5
127.0.0.1:7007> lrange list 0 -1
1) "head"
2) "list3"
3) "2"
4) "list"
5) "list1"


blpop key[key] timeout:移除并获取列表的第一个元素,如果列表没有元素会阻塞列表直到超时或发行可弹出元素为止。

127.0.0.1:7007> lrange list 0 -1
1) "head"
2) "list3"
3) "2"
4) "list"
5) "list1"
127.0.0.1:7007> blpop list 5000
1) "list"
2) "head"
127.0.0.1:7007> lrange list 0 -1
1) "list3"
2) "2"
3) "list"
4) "list1"


brpop key[key] timeout:移除并获取列表的最后一个元素,如果列表没有元素会阻塞列表直到超时或发行可弹出元素为止。

127.0.0.1:7007> lrange list 0 -1
1) "list3"
2) "2"
3) "list"
4) "list1"
127.0.0.1:7007> brpop list 5000
1) "list"
2) "list1"
127.0.0.1:7007> lrange list 0 -1
1) "list3"
2) "2"
3) "list"


lindex key index:根据索引获取列表中的元素。

127.0.0.1:7007> lindex list 2
"list"

linsert key before/after pivot value:在列表的元素前后插入元素。pivot是列表的某个元素。

127.0.0.1:7007> linsert list before 2 123
(integer) 4
127.0.0.1:7007> lrange list 0 -1
1) "list3"
2) "123"
3) "2"
4) "list"

llen key:获取列表长度。

127.0.0.1:7007> llen list
(integer) 4

lpop key:移除并获取列表的第一个元素。

127.0.0.1:7007> lrange list 0 -1
1) "list3"
2) "123"
3) "2"
4) "list"
127.0.0.1:7007> lpop list
"list3"
127.0.0.1:7007> lrange list 0 -1
1) "123"
2) "2"
3) "list"

lrem key count value:移除列表元素。

COUNT 的值可以是以下几种:
count > 0 : 从表头开始向表尾搜索,移除与 VALUE 相等的元素,数量为 COUNT 。
count < 0 : 从表尾开始向表头搜索,移除与 VALUE 相等的元素,数量为 COUNT 的绝对值。
count = 0 : 移除表中所有与 VALUE 相等的值。

127.0.0.1:7007> lrange list 0 -1
1) "123"
2) "2"
3) "list"
127.0.0.1:7007> lrem list 0 123
(integer) 1
127.0.0.1:7007> lrange list 0 -1
1) "2"
2) "list"

lset key index value:通过索引设置列表元素的值。

127.0.0.1:7007> lrange list 0 -1
1) "2"
2) "list"
127.0.0.1:7007> lset list 1 index
OK
127.0.0.1:7007> lrange list 0 -1
1) "2"
2) "index"

ltrim key start stop:保留指定区间内的元素。

127.0.0.1:7007> lrange list 0 -1
1) "g"
2) "f"
3) "e"
4) "d"
5) "c"
6) "b"
7) "a"
8) "2"
9) "index"
127.0.0.1:7007> ltrim list 4 8
OK
127.0.0.1:7007> lrange list 0 -1
1) "c"
2) "b"
3) "a"
4) "2"
5) "index"

rpop key:移除列表的最后一个元素,返回被移除的元素。

127.0.0.1:7007> lrange list 0 -1
1) "c"
2) "b"
3) "a"
4) "2"
5) "index"
127.0.0.1:7007> rpop list
"index"
127.0.0.1:7007> lrange list 0 -1
1) "c"
2) "b"
3) "a"
4) "2"

rpoplpush source destination:移除列表的最后一个元素,并将该元素添加到另一个列表并返回。

127.0.0.1:7007> lrange list 0 -1
1) "c"
2) "b"
3) "a"
4) "2"
127.0.0.1:7007> rpoplpush list list1
"2"
127.0.0.1:7007> lrange list1 0 -1
1) "2"

rpush key value[value]:在列表中添加一个或多个值。

127.0.0.1:7007> rpush list A
(integer) 4
127.0.0.1:7007> lrange list 0 -1
1) "c"
2) "b"
3) "a"
4) "A"

rpushx key value:为已存在的列表添加值。

127.0.0.1:7007> rpushx list Z
(integer) 5
127.0.0.1:7007> lrange list 0 -1
1) "c"
2) "b"
3) "a"
4) "A"
5) "Z"

4、Set

Set是一个String类型的无序集合,集合中的成员不能重复。


sadd key member[member]:向集合添加一个或多个成员。

smembers key:返回集合中的所有成员。

127.0.0.1:7007> sadd set a b c d e f g
(integer) 7
127.0.0.1:7007> smembers set
1) "d"
2) "c"
3) "g"
4) "b"
5) "e"
6) "a"
7) "f"

scard key:获取集合的成员数量。

127.0.0.1:7007> scard set
(integer) 7

sdiff key[key]:返回给定所有集合的差集。

127.0.0.1:7007> smembers set
1) "d"
2) "c"
3) "g"
4) "b"
5) "e"
6) "a"
7) "f"
127.0.0.1:7007> smembers set1
1) "e"
2) "g"
3) "h"
4) "i"
5) "f"
6) "j"
127.0.0.1:7007> sdiff set set1
1) "a"
2) "b"
3) "d"
4) "c"

sdiffstore destination key[key]:返回给定所有集合的差集并存储在另一个destination中。

127.0.0.1:7007> sdiffstore setdiff set set1
(integer) 4
127.0.0.1:7007> smembers setdiff
1) "a"
2) "b"
3) "d"
4) "c"

sinter key[key]:返回给定所有集合的交集。

127.0.0.1:7007> sinter set set1
1) "e"
2) "g"
3) "f"

sinterstore destination key[key]:返回给定所有集合的交集并存储在另一个set中。

127.0.0.1:7007> sinterstore setinter set set1
(integer) 6
127.0.0.1:7007> smembers setinter
1) "h"
2) "i"
3) "e"
4) "g"
5) "f"
6) "j"

sismember key member:判断member是否是集合key的成员。

127.0.0.1:7007> smembers set1
1) "e"
2) "g"
3) "h"
4) "i"
5) "f"
6) "j"
127.0.0.1:7007> sismember set1 j
(integer) 1
127.0.0.1:7007> sismember set1 k
(integer) 0

smove source destination member:将member从source集合移动到destination集合。

127.0.0.1:7007> smove set set1 a
(integer) 1
127.0.0.1:7007> smembers set1
1) "h"
2) "e"
3) "g"
4) "i"
5) "a"
6) "f"
7) "j"

spop key:移除并返回集合中的一个随机成员。

127.0.0.1:7007> smembers set1
1) "h"
2) "e"
3) "g"
4) "i"
5) "a"
6) "f"
7) "j"
127.0.0.1:7007> spop set1
"h"
127.0.0.1:7007> smembers set1
1) "e"
2) "g"
3) "i"
4) "a"
5) "f"
6) "j"

srandmember key [count]:返回一个或多个成员。

127.0.0.1:7007> smembers set1
1) "e"
2) "g"
3) "i"
4) "a"
5) "f"
6) "j"
127.0.0.1:7007> srandmember set1 3
1) "e"
2) "f"
3) "j"

srem key member[member]:移除集合中一个或多个成员。

127.0.0.1:7007> smembers set1
1) "e"
2) "g"
3) "i"
4) "a"
5) "f"
6) "j"
127.0.0.1:7007> srem set1 e j
(integer) 2
127.0.0.1:7007> smembers set1
1) "g"
2) "i"
3) "a"
4) "f"

sunion key[key]:返回给定集合的并集。

127.0.0.1:7007> sunion set set1
1) "g"
2) "b"
3) "e"
4) "i"
5) "a"
6) "f"
7) "d"
8) "c"

sunionstore destination key[key]:返回给定集合的并集并存储在destination中。

127.0.0.1:7007> sunionstore setunion set set1
(integer) 8
127.0.0.1:7007> smembers setunion
1) "g"
2) "b"
3) "e"
4) "i"
5) "a"
6) "f"
7) "d"
8) "c"

5、Zset

Zset和Set一样是一个String类型的集合,不同于Set的是,Zset会有一个Score值,用来排序,通过一些排序的方法,可以有序的获取集合中的成员。


zadd key score member[score member]:向有序集合添加一个或多个成员,或更新已存在成员的分数。

zcard key:获取有序集合的成员数。

zrange key start stop [with scores]:通过索引计算指定区间内的成员。

127.0.0.1:7007> zadd zset 19 xlr 20 yyy 21 lxy
(integer) 3
127.0.0.1:7007> zcard zset
(integer) 3
127.0.0.1:7007> zrange zset 0 -1
1) "xlr"
2) "yyy"
3) "lxy"

zcount key min max:获取指定区间分数的成员数。

127.0.0.1:7007> zcount zset 20 21
(integer) 2

zincrby key increment member:对指定成员的分数加上增量increment。

127.0.0.1:7007> zincrby zset 10 lxy
"31"

zinterstore destination numkeys key[key]:计算一个或多个有序集合的交集并存储到新的有序集合。numkeys 必须和有序集合个数对应。

127.0.0.1:7007> zinterstore zsetinter 2 zset zset1
(integer) 1
127.0.0.1:7007> zrange zsetinter 0 -1
1) "lxy"

zrangebyscore key min max [withscores] [limit]:通过分数区间返回有序集合的成员。不包含max。

127.0.0.1:7007> zrangebyscore zset 19 21
1) "xlr"
2) "yyy"

zrank key member:返回指定成员的索引。

127.0.0.1:7007> zrange zset 0 -1
1) "*"
2) "xlr"
3) "yyy"
4) "lxy"
127.0.0.1:7007> zrank zset lxy
(integer) 3

zrem key member[member]:移除有序集合的一个或多个成员。

127.0.0.1:7007> zrem zset *
(integer) 1

zremrangebyrank key start stop:移除给定的排名区间的所有成员。

127.0.0.1:7007> zremrangebyrank zset 2 3
(integer) 1
127.0.0.1:7007> zrange zset 0 -1
1) "xlr"
2) "yyy"

zremrangebyscore key min max:移除给定的分数区间的所有成员。

127.0.0.1:7007> zremrangebyscore zset 19 21
(integer) 2
127.0.0.1:7007> zrange zset 0 -1
(empty list or set)

zrevrange key start stop [withscores]:返回指定区间的成员,通过索引,分数从高到低。

127.0.0.1:7007> zrevrange zset 0 -1
1) "yc"
2) "lxy"
3) "yyy"
4) "xlr"

zrevrangebyscore key max min [withscores]:返回指定分数区间的成员,分数从高到低排序。

127.0.0.1:7007> zrevrangebyscore zset 100 0
1) "yc"
2) "lxy"
3) "yyy"
4) "xlr"

zrevrank key member:返回指定成员排名,按分数值从大到小排序。

127.0.0.1:7007> zrevrank zset yyy
(integer) 2

zscore key member:返回成员的分数值。

127.0.0.1:7007> zscore zset lxy
"21"

zunionstore destination numbers key[key]:计算一个或多个有序集合的并集,并存储在新的zset中。

127.0.0.1:7007> zunionstore zsetunion 2 zset zset1
(integer) 6
127.0.0.1:7007> zrange zsetunion 0 -1
1) "xlr"
2) "xhb"
3) "yyy"
4) "zhd"
5) "lxy"
6) "yc"

持久化模式

Redis提供了两种持久化模式RDB和AOF。

1、RDB(默认)

Redis每隔一段时间会将数据保存到硬盘上,生成一个RDB文件。Redis重启时会查看是否有RDB文件,有则加载进内存恢复数据,没有则数据丢失。

配置:

在配置文件中,有这样一段代码

#   save ""

save 900 1
save 300 10
save 60 10000

# 默认开启数据压缩,Redis使用LZF压缩。
rdbcompression yes

# RDB文件名
dbfilename dump.rdb

# RDB文件保存目录
dir ./

意思是900秒之内有1次更改,300秒内有10次更改,60秒内有10000次更改则保存成RDB文件。

如果不想服务器保存RDB文件,就把save ""的注释打开。

触发规则:

​ 1、在指定时间间隔内,执行指定次数的写操作。

​ 2、执行save命令(阻塞),或者bgsave命令(异步,不阻塞)。

​ 3、执行flushall命令,清空数据库所有数据。

​ 4、执行shutdown命令,保证服务器正常关闭而不丢失数据。

优点:

​ 1、适合大规模的数据恢复。

​ 2、如果业务对数据的完整性和一致性要求不高,可以使用RDB。

缺点:

​ 1、数据的完整性和一致性不高,因为RDB在进行最后一次备份时宕机了,导致最后一次的操作不能备份。

​ 2、备份时占用内存,RDB在备份时会单独创建一个进程,将数据写入一个临时文件中,再覆盖原来的备份文件。

ps:shutdown和flushall命令都会触发RDB。

2、AOF

AOF模式默认不开启,需要在redis.conf中开启,将appendonly改为yes。

appendonly yes

指定AOF持久化到硬盘的文件名

appendfilename "appendonly.aof"

指定日志更新条件:

​ 1、always:同步持久化,每次发生数据变化会立刻写入磁盘中,性能较差但保存的完整性较好。(慢,安全)

​ 2、everysec:默认值,每秒记录一次(异步)。

​ 3、no:不同步。

# appendfsync always
appendfsync everysec
# appendfsync no

AOF的重写机制:

​ 1、重写的原理:当AOF文件的大小达到设置的限制时,会对AOF文件的内容进行压缩。Redis会创建一个进程,去读取内存中的数据,并写入一个临时文件中,最后替换旧的AOF文件。

压缩的原理:
比如执行了
lpush a 1
lpush a 2
lpush a 3
lpush a 4
那么AOF文件中将会有4行信息记录a键的变化。
压缩主要是先从数据库读取a的值,然后用lpush a 1 2 3 4来保存写入操作,替换掉原来的4行。

​ 2、触发机制:当AOF文件大小是上次重写后的大小一倍且文件大小大于64M时触发。

auto-aof-rewrite-percentage 100//每个文件增长比例大小
auto-aof-rewrite-min-size 64mb//文件最大值

​ 3、如果在重写期间客户端更改了键值,那么AOF会将更改操作写入到AOF缓冲区和AOF重写缓冲区中,完成重写操作后,会将AOF重写缓冲区中的更改写入到AOF文件中,再用新的AOF文件替换旧的AOF文件。

优点:数据的完整性和一致性更高。

缺点:因为AOF文件的记录多,文件会越来越来大,数据恢复会变得慢。

事务

1、事务的概念

事务是一组命令的集合,在Mysql中,事务的执行是要么全部执行,要么全部不执行,遇到错误时会回滚。而Redis中,事务的执行在遇到错误时,还是会继续执行。事务不保证原子性,没有回滚。

Redis事务的三个阶段:

1、开启事务

2、命令进入队列

3、执行事务

2、事务的命令

watch key[key]:监视一个或多个key,执行事务之前,如果key被改动,则事务中断。

multi:开启事务。

exec:执行事务,执行时,会把watch取消。

discard:取消事务,删除队列中的所有命令。

unwatch:取消监视。

消息队列

publish/subscribe发布/订阅模式是消息通信的模式,订阅后可以监听某个消息,如果没有消息则一直阻塞,当有消息发布时,订阅者可以收到这个消息,类似于MQ。当发布了一个消息时,所有订阅这个消息的客户端都可以收到这个消息,一个客户端也可以订阅多个消息。

1、订阅频道

subscribe channel[channel]:订阅频道命令。

psubscribe pattern[pattern]:按通配符规则订阅一个或多个频道。

# subscribe
127.0.0.1:7007> subscribe message
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "message"
3) (integer) 1

#psubscribe
127.0.0.1:7007> psubscribe a* b? c?*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "a*"
3) (integer) 1
1) "psubscribe"
2) "b?"
3) (integer) 2
1) "psubscribe"
2) "c?*"
3) (integer) 3



使用后会一直监听某个频道,直到有消息发布。

2、发布消息

publish channel message:发布消息。

127.0.0.1:7007> publish message msg1
(integer) 1
#a*任意个字符(包括0)
127.0.0.1:7007> publish abcde abcde
(integer) 1
#b?一个字符
127.0.0.1:7007> publish bcd bcd
(integer) 0
127.0.0.1:7007> publish bc bc
(integer) 1
#c?*一个以上的字符
127.0.0.1:7007> publish cde cde
(integer) 1
127.0.0.1:7007> 


返回的1表示有一个客户端在监听频道。

发布后,订阅频道的会收到发送的消息。

127.0.0.1:7007> subscribe message
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "message"
3) (integer) 1
1) "message"
2) "message"
3) "msg1"

#psubscribe
127.0.0.1:7007> psubscribe a* b? c?*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "a*"
3) (integer) 1
1) "psubscribe"
2) "b?"
3) (integer) 2
1) "psubscribe"
2) "c?*"
3) (integer) 3
1) "pmessage"
2) "a*"
3) "abcde"
4) "abcde"
1) "pmessage"
2) "b?"
3) "bc"
4) "bc"
1) "pmessage"
2) "c?*"
3) "cde"
4) "cde"

失效策略

通过expire key ttl设置失效。

Redis采用的定期失效+惰性失效策略。


被动触发:使用get获取key value的时候检查是否失效。

主动触发:后台每100毫秒进行10次定时任务,可以通过修改redis.conf的hz参数来修改每秒进行多少次,默认为10,是随机抽取来检查,如果有过期的key,才失效,没有检查到的话就不会失效。如果随机抽取检查没有检查到某个key,也没有使用get来获取这个key,久而久之,Redis占用的的内存会越来越大。

那么应该采用内存淘汰机制

1)noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。应该没人用吧。

2)allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。推荐使用,目前项目在用这种。

3)allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key。应该也没人用吧,你不删最少使用Key,去随机删。

4)volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。这种情况一般是把redis既当缓存,又做持久化存储的时候才用。不推荐

5)volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。依然不推荐

6)volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。不推荐

ps:如果没有设置 expire 的key, 不满足先决条件(prerequisites); 那么 volatile-lru, volatile-random 和 volatile-ttl 策略的行为, 和 noeviction(不删除) 基本上一致。

高可用

在Redis中,实现高可用的技术主要包括持久化,复制,哨兵和集群。

​ 1、持久化:持久化是最简单的高可用方法,主要作用是数据备份,保证数据不会因为进程退出而丢失。

​ 2、复制:复制是高可用Redis的基础,哨兵和集群都是在复制的基础上实现高可用的。复制主要实现了数据的多机备份,以及对于读操作的负载均衡和简单的故障恢复。

​ 复制的缺陷:

​ 1、故障恢复无法自动化。

​ 2、写操作无法负载均衡。

​ 3、存储能力受单机的限制。

​ 3、哨兵:在复制的基础上,实现了自动化的故障恢复。

​ 哨兵的缺陷:

​ 1、写操作无法负载均衡。

​ 2、存储能力受单机的限制。

​ 4、集群:通过集群,能够解决写操作无法负载均衡以及存储能力受单机的限制的问题,是目前较为完善的高可用方案。

复制

1、主从复制

主从复制只能从主复制到从。

特点:

​ 1、数据冗余:实现了数据的热备份,主从的一致性高。

​ 2、故障恢复:主宕机后可以继续使用从继续提供服务,实现数据的快速恢复。

​ 3、负载均衡:使用读写分离,由主提供写服务,从提供读服务,从而分担服务器的负载,在读少写多的情况下,可以使用多个从节点,可以大幅度提高Redis的并发量。

​ 4、高可用基石:主从复制是哨兵和集群能够使用的基础。

2、全量复制和部分复制

全量复制:

​ 1、主节点在接收到了从节点发送的全量复制命令后,执行bgsave命令,单独进行一个进程生成RDB文件,并使用一个复制缓冲区记录从现在开始执行的所有写命令。

​ 2、主节点的bgsave命令完成后,把RDB文件发送给从节点,从节点首先清除自己的数据,然后载入接收到的RDB文件,将数据库更新至主节点执行bgsave命令时的状态。

​ 3、主节点将复制缓冲区的所有写命令发送给从节点,从节点执行这些写命令,将主从保持一致性。

​ 4、如果从节点开启了AOF,那么会执行bgrewriteaof命令,将所有写操作同步至AOF文件中。

ps:

​ 1、主节点执行bgsave命令fork一个子进程进行数据的持久化,该过程非常消耗CPU、内存和硬盘IO。

​ 2、主节点通过网络将RDB文件发送给从节点,该操作也十分消耗主从节点的带宽。

​ 3、从节点清空数据,载入RDB文件,这个操作是阻塞的,无法响应客户端的命令。

​ 4、从节点执行bgrewriteaof命令也十分消耗系统资源。


部分复制:

​ 1、复制偏移量

​ 主节点和从节点分别维护一个复制偏移量,这个值表示的是主节点向从节点传输的字节数,主节点向从节点发送了N个字节数时,主节点的offset+N,从节点接收到主节点发送的字节数,从节点的offset+N。

​ offset用来判断主从的数据库状态是否一致,如果二者的offset相同,则一致,反之不一致。不一致时可以根据两个offset找到从节点缺少的那部分数据,比如主节点的offset是1000,从节点的offset是500,那么部分复制就将offset为501-1000的数据发送给从节点,而501-1000的数据的存储位置就是复制积压缓冲区。

​ 2、复制积压缓冲区

​ 复制积压缓冲区是由主节点维护的,固定长度的,先进先出的队列,默认大小是1M。当主节点开始有从节点的时候就创建,其作用是备份主节点最近发送给从节点的数据。ps:无论主节点有一个还是多个从节点,都只需要一个复制积压缓冲区。

​ 在命令传播阶段,主节点除了将写命令发送给从节点,还会发送一份复制积压缓冲区,作为写命令的备份;除了存储写命令,复制积压缓冲区还会存储复制偏移量(offset),由于复制积压缓冲区的大小固定,且是先进先出的队列,所以它保存的是主节点最近的执行的写命令,时间较早的命令会被挤出队列。

​ 由于复制积压缓冲区的容量有限,因此它可以备份的写命令也有限,当主从节点的offset相差过大时,将无法进行部分复制,只能进行全量复制。因此,为了提高在网络中断时部分复制执行的效率,可以根据需要设置复制积压缓冲区的大小(repl-backlog-size)。比如,网络中断的平均时间是60s,而主节点每秒产生的写命令所占的大小是100kb,则复制积压缓冲区的大小应该设置为60*100,约等于6000kb(6M),保险起见,可以设置12M。

​ 从节点将它的offset发送给主节点之后,主节点根据从节点offset的值和缓冲区的大小来判断是否能执行部分复制。

​ 1、如果复制偏移量offset之后的数据还在复制积压缓冲区里,则执行部分复制。

​ 2、如果复制偏移量offset之后的数据已经被复制积压缓冲区挤出,则执行全量复制。


​ 3、服务器运行id

​ 每个主从节点在启动时都会生成一个随机ID(runid),由40位16进制字符组成,runid用来识别唯一一个Redis节点。

​ 主从节点初次复制时,主节点将自己的runid发送给从节点,从节点将这个runid保存起来,当断线重连时,从节点会把这个runid发送给主节点,主节点根据runid来判断能否部分复制。

​ 1、如果保存的runid和主节点的runid相同,说明主从之前已经同步过,是否再次使用部分复制取决于从节点的复制偏移量offset和复制积压缓冲区。

​ 2、如果保存的runid和主节点的runid不同,说明从节点断线前同步的主节点不是当前的主节点,只能进行全量复制。


PSYNC进行全量复制和部分复制可能会遇到的情况

Redis入门笔记

从服务器发送PING命令时可能遇到的情况

Redis入门笔记

从服务器进行身份验证时可能遇到的情况

Redis入门笔记

3、心跳检测

在命令传播阶段(将复制积压缓冲区的写命令发送到从服务器执行),从服务器会以每秒一次的频率,向主服务器发送命令(REPLCONF ACK offset)。

发送这个命令主要有三个作用:

​ 1、检测主从服务器的连接状态

​ 如果主服务器超过一秒钟没有接收到从服务器发来的REPLCONF ACK命令,那么主服务器就知道主从服务器之间的连接出了问题。

​ 2、辅助实现min-slaves选项

​ Redis的min-slaves-to-write和min-slaves-max-lag两个选项可以防止主服务器不安全的情况下执行写命令,比如我们向主服务器提供以下配置

min-slaves-to-write 3
min-slaves-max-lag 10

​ 那么在从服务器的数量小于3个,或者三个从服务器的延迟值都大于或等于10秒时,主服务器将拒绝执行写命令。

​ 3、检测命令丢失

​ 如果因为网络故障,主服务器传播给从服务器的写命令在半路丢失,那么从服务器向主服务器发送该命令时,主服务器会发觉从服务器当前的复制偏移量小于自己的复制偏移量,然后主服务器就会根据从服务器提交的复制偏移量,在复制积压缓冲区里找到从服务器丢失的数据,并将这些数据重新发送给从服务器。


哨兵(Sentinel)

Sentinel是Redis的高可用性的解决方案:由一个或多个Sentinel实例组成的Sentinel系统,可以监视任意多个主服务器,以及这些主服务器下面的所有从服务器。当监视的主服务器进入下线状态时并且下线时长超过用户设定的下线时长上限时,自动将下线的主服务器下面的某个从服务器升级为主服务器,当主服务器再次上线时,自动降级成它的从服务器。

集群

集群是一个可以再多个Redis节点之间进行数据共享的设施。

集群不支持同时处理多个键的Redis命令,因为执行这些命令会在多个Redis节点之间移动数据,在高负载的情况下,这些命令会降低集群的性能,并导致不可预知的错误。

集群通过分区来提供一定程度的可用性:即使集群有一部分节点失效,集群也可以处理这些请求。

集群的优点:

​ 1、将数据自动划分到多个节点。

​ 2、当集群中的一部分节点失效,仍然可以处理请求。

1、为什么要用16384个哈希槽

Redis入门笔记

2、分片

2.1、分片的作用

​ 1、允许使用更多的电脑的内存总和来支持更大的数据库,不使用分片则只能使用单机能支持的内存容量。

​ 2、允许将计算能力拓展到多台计算机上,并将网络带宽拓展到多台计算机和网络适配器上。

分片标准

比如,现在有4个Redis实例:r1,r2,r3,r4,代表用户键的名称:user:1,user:2。

​ 1、范围分片:让ID范围为0-1000的存储在r1上,1001-2000的存储在r2上,2001-3000的存储在r3上,3001-4000的存储在r4上。

​ 2、哈希分片:

​ 一个集群有16384个哈希槽,数据库的每个键都属于这16384个哈希槽的其中一个,集群利用公式CRC16(KEY) % 16384来计算当前key属于哪个槽,这个命令用于计算key的CRC16校验和。

​ 集群中的每个节点负责一部分哈希槽,比如,一个集群有三个哈希槽,其中:

​ 节点A处理0-5500

​ 节点B处理5501-11000

​ 节点C处理11001-16384

我现在想设置一个key,叫my_name:

set my_name zhangguoji

按照Redis Cluster的哈希槽算法,CRC16('my_name')%16384 = 2412 那么这个key就被分配到了节点A上
同样的,当我连接(A,B,C)的任意一个节点想获取my_name这个key,都会转到节点A上

2.2、分片标准的不同实现

1、客户端分片:由客户端选择正确的节点进行写操作或读取一个键。

2、代理辅助分片:客户端将请求发送给一个支持Redis协议的代理,而不是将请求发送给Redis实例。代理收到后将根据已经配置好的分区配置将请求发送给正确的Redis实例,之后将Redis实例的返回的值发送给客户端。

3、请求路由:将请求发送给任意一个Redis实例,这个实例确认后把这个请求发送给正确的Redis实例。

2.3、分片的缺点

1、涉及到多个键的命令不支持。因为执行这些命令会在多个Redis节点之间移动数据,在高负载的情况下,这些命令会降低集群的性能,并导致不可预知的错误。

2、无法使用涉及到多个键的事务。

3、分片的粒度是键,因此无法将一个很大的集合这样的大键分片到多个实例上。

4、使用了分片后,数据处理变得复杂,比如,不得不处理多个RDB/AOF文件,并且为了备份数据,需要将多个实例上的持久化文件进行组合。

版权声明:程序员胖胖胖虎阿 发表于 2022年8月29日 下午3:55。
转载请注明:Redis入门笔记 | 胖虎的工具箱-编程导航

相关文章

暂无评论

暂无评论...