redis全面解析

Redis调研及使用文档

1 引言
随着业务的增长和产品的完善,急速增长的数据给Oracle数据库带来了很大的压力,而随着我们对产品服务质量要求的提高,传统的数据查询方式已无法满足我们需求。为此我们需要寻找另外一种模式来提高数据查询效率。NoSQL内存数据库是最近兴起的新型数据库,它的特点就是把数据放在内存中操作,数据处理速度相对于磁盘提高了好几个量级,因此,通过把经常访问的数据转移到内存数据库中,不但可以缓解Oracle的访问压力,而且可以极大提高数据的访问速度,提高用户体验。

2 概述
Redis是一个开源的,先进的key-value持久化产品。它通常被称为数据结构服务器,它的值可以是字符串(String)、哈希(Map)、列表(List)、集合(Sets)和有序集合(Sorted sets)等类型。可以在这些类型上面做一些原子操作,如:字符串追加、增加Hash里面的值、添加元素到列表、计算集合的交集,并集和差集;或者区有序集合中排名最高的成员。为了取得好的性能,Redis是一个内存型数据库。不限于此,Redis也可以把数据持久化到磁盘中,或者把数据操作指令追加了一个日志文件,把它用于持久化。也可以用Redis容易的搭建master-slave架构用于数据复制。其它让它像缓存的特性包括,简单的check-and-set机制,pub/sub和配置设置。Redis可以用大部分程序语言来操作:C、C++、C#、Java、Node.js、php、ruby等等。Redis是用ANSIC写的,可以运行在多数POSIX系统,如:Linux,*BSD,OS X和Soloris等。官方版本不支持Windows下构建,可以选择一些修改过的版本,照样可以使用Redis。

3 Redis介绍
3.1 五种数据类型
3.1.1 String类型
String是最基本的类型,而且string 类型是二进制安全的。意思是 redis 的 string 可以包含任何数据。比如 jpg 图片或者序列化的对象。从内部实现来看其实 string 可以看作 byte 数组,最大上限是 1G 字节。

string类型数据操作指令简介

1. set keyvalue设置key对应string类型的值,返回1表示成功,0失败。

2. setnx keyvalue如果key不存在,设置key对应string类型的值。如果key已经存在,返回0。

3. get key获取key对应的string值,如果key不存在返回nil

4. getsetkey value先获取key的值,再设置key的值。如果key不存在返回nil。

5. mget key1key2 ...... keyN一次获取多个key的值,如果对应key不存在,则对应返回nil

6. mset key1value1 ...... keyN valueN一次设置多个key的值,成功返回1表示所有的值都设置了,失败返回0表示没有任何值被设置。

7. msetnxkey1 value1 ...... keyN valueN一次设置多个key的值,但是不会覆盖已经存在的key incr key 对key的值做++操作,并返回新的值。注意incr一个不是int的value会返回错误,incr一个不存在的key,则设置key值为1。

8. decr key: 对key的值做--操作,decr一个不存在key,则设置key值为-1。

9. incrbykey integer对key加上指定值,key不存在时候会设置key,并认为原来的value 是0。

10. decrbykey integer对key减去指定值。decrby完全是为了可读性,我们完全可以通过incrby一个负值来实现同样效果,反之一样。

3.1.2 hash类型
hash是一个string类型的field和value的映射表。添加,删除操作都是O(1)(平均)。 hash特别适合用于存储对象。相对于将对象的每个字段存成单个string类型。将一个对象存储在hash类型中会占用更少的内存,并且可以更方便的存取整个对象。省内存的原因是新建一个hash对象时开始是用 zipmap(又称为 small hash)来存储的。这个 zipmap 其实并不是hashtable,但是zipmap相比正常的hash实现可以节省不少hash本身需要的一些元数据存储开销。尽管zipmap的添加,删除,查找都是 O(n),但是由于一般对象的field 数量都不太多。所以使用zipmap也是很快的,也就是说添加删除平均还是O(1)。如果field 或者 value的大小超出一定限制后,redis会在内部自动将zipmap替换成正常的hash实现.这个限制可以在配置文件中指定。

hash类型数据操作指令简介

1. hset keyfield value 设置hash field为指定值,如果key不存在,则创建。

2. hget keyfield 获取指定的hash field。

3. hmget keyfiled1....fieldN 获取全部指定的hash filed。

4. hmset keyfiled1 value1 ...... filedN valueN 同时设置hash的多个field。

5. hincrbykey field integer 将指定的hashfiled 加上指定值。成功返回hashfiled变更后的值。

6. hexistskey field 检测指定field是否存在。 hdelkey field 删除指定的hash field。

7. hlen key 返回指定hash的field数量。 hkeys key 返回hash的所有field。

8. hvals key 返回hash的所有value。

9. hgetall 返回hash的所有filed和value。

3.1.3 List类型
list是一个链表结构,可以理解为一个每个子元素都是 string 类型的双向链表。主要功

能是push、pop、获取一个范围的所有值等。操作中key理解为链表的名字。

List类型数据操作指令简介

1. lpush keystring 在key对应list的头部添加字符串元素,返回1表示成功,0表示key存在且不是list类型。

2. rpush keystring 在key对应list的尾部添加字符串元素。

3. llen key 返回key对应list的长度,如果key不存在返回0,如果key对应类型不是list 返回错误。

4. lrangekey start end 返回指定区间内的元素,下标从0开始,负值表示从后面计算,-1表示倒数第一个元素 ,key不存在返回空列表。

5. ltrim keystart end 截取list指定区间内元素,成功返回1,key不存在返回错误。

6. lset keyindex value 设置list中指定下标的元素值,成功返回1,key或者下标不存在返回错误。

7. lrem keycount value 从 List 的头部(count正数)或尾部(count负数)删除一定数量(count)匹配value的元素,返回删除的元素数量。count为0时候删除全部。

8. lpop key 从list的头部删除并返回删除元素。如果key对应list不存在或者是空返回nil,如果key对应值不是list返回错误。

9. rpop key从list的尾部删除并返回删除元素。

10. blpopkey1 ...... keyN timeout 从左到右扫描,返回对第一个非空list进行lpop操作并返回,比如blpop list1 list2 list3 0 ,如果list不存在list2,list3都是非空则对list2做 lpop并返回从list2中删除的元素。如果所有的list都是空或不存在,则会阻塞timeout秒,timeout为0表示一直阻塞。当阻塞时,如果有client对key1...keyN中的任意key 进行push操作,则第一在这个key上被阻塞的client会立即返回。如果超时发生,则返回 nil。有点像unix的select或者poll。

11. brpop 同blpop,一个是从头部删除一个是从尾部删除。

3.1.4 Set类型
set是无序集合,最大可以包含(2的 32 次方-1)个元素。set 的是通过 hash table 实现的,所以添加,删除,查找的复杂度都是 O(1)。hash table 会随着添加或者删除自动的调整大小。需要注意的是调整 hash table 大小时候需要同步(获取写锁)会阻塞其他读写操作。可能不久后就会改用跳表(skip list)来实现。跳表已经在 sorted sets 中使用了。关于 set 集合类型除了基本的添加删除操作,其它有用的操作还包含集合的取并集(union),交集(intersection),差集(difference)。通过这些操作可以很容易的实现 SNS 中的好友推荐和 blog 的 tag 功能。

set类型数据操作指令简介

1. sadd keymember 添加一个string元素到key对应set集合中,成功返回1,如果元素以及在集合中则返回0,key对应的set不存在则返回错误。

2. srem keymember 从key对应set中移除指定元素,成功返回1,如果member在集合中不存在或者key不存在返回0,如果key对应的不是set类型的值返回错误。

3. spop key 删除并返回key对应set中随机的一个元素,如果set是空或者key不存在返回 nil。

4. srandmemberkey 同spop,随机取set中的一个元素,但是不删除元素。

5. smovesrckey dstkey member 从srckey对应set中移除member并添加到dstkey对应set中,整个操作是原子的。成功返回1,如果member在srckey中不存在返回0,如果key不是set 类型返回错误。

6. scard key 返回set的元素个数,如果set是空或者key不存在返回0。

7. sismemberkey member 判断member是否在set中,存在返回1,0表示不存在或者key不存在。

8. sinterkey1 key2 …… keyN 返回所有给定key的交集。

9. sinterstoredstkey key1 ....... keyN 返回所有给定key的交集,并保存交集存到dstkey下。

10. sunionkey1 key2 ...... keyN 返回所有给定key的并集。

11. sunionstoredstkey key1 ...... keyN 返回所有给定key的并集,并保存并集到dstkey下。

12. sdiffkey1 key2 ...... keyN 返回所有给定key的差集。

13. sdiffstoredstkey key1 ...... keyN 返回所有给定key的差集,并保存差集到dstkey下。

14. smemberskey 返回key对应set的所有元素,结果是无序的。

3.1.5 Sorted Set
sorted set是有序集合,它在set的基础上增加了一个顺序属性,这一属性在添加修改元素的时候可以指定,每次指定后,会自动重新按新的值调整顺序。可以理解了有两列的 mysql表,一列存value,一列存顺序。操作中key理解为sorted set的名字。

Sorted Set类型数据操作指令简介

1. add keyscore member 添加元素到集合,元素在集合中存在则更新对应score。

2. zrem keymember 删除指定元素,1 表示成功,如果元素不存在返回 0。

3. zincrbykey incr member 增加对应 member 的 score 值,然后移动元素并保持skip list 保持有序。返回更新后的 score 值。

4. zrank keymember 返回指定元素在集合中的排名(下标),集合中元素是按 score 从小到大排序的。

5. zrevrankkey member 同上,但是集合中元素是按score 从大到小排序。

6. zrangekey start end 类似 lrange 操作从集合中去指定区间的元素。返回的是有序结果

7. zrevrangekey start end 同上,返回结果是按 score 逆序的。

8. zrangebyscorekey min max 返回集合中 score 在给定区间的元素。

9. zcountkey min max 返回集合中 score 在给定区间的数量。

10. zcard key 返回集合中元素个数。

11. zscorekey element 返回给定元素对应的 score。

12. zremrangebyrankkey min max 删除集合中排名在给定区间的元素。

13. zremrangebyscorekey min max 删除集合中 score 在给定区间的元素。

3.2 Redis主从复制
3.2.1 主从复制介绍
Redis支持将数据同步到多台从库上,这种特性对提高读取性能非常有益。master可以有多个slave。除了多个slave连到相同的master外,slave也可以连接其它slave形成图状结构。主从复制不会阻塞master。也就是说当一个或多个slave与master进行初次同步数据时,master可以继续处理客户端发来的请求。相反slave在初次同步数据时则会阻塞不能处理客户端的请求。

主从复制可以用来提高系统的可伸缩性,我们可以用多个slave 专门用于客户端的读请求,比如sort操作可以使用slave来处理。也可以用来做简单的数据冗余。可以在 master 禁用数据持久化,只需要注释掉 master 配置文件中的所有 save 配置,然后只在 slave 上配置数据持久化。

3.2.2 主从复制过程
当设置好 slave 服务器后,slave 会建立和 master 的连接,然后发送 sync命令。无论是第一次同步建立的连接还是连接断开后的重新连接,master 都会启动一个后台进程,将数据库快照保存到文件中,同时 master 主进程会开始收集新的写命令并缓存起来。后台进程完成写文件后,master 就发送文件给 slave,slave 将文件保存到磁盘上,然后加载到内存恢复数据库快照到 slave 上。接着 master 就会把缓存的命令转发给 slave。而且后续 master 收到的写命令都会通过开始建立的连接发送给slave。从master到slave的同步数据的命令和从客户端发送的命令使用相同的协议格式。当 master 和 slave 的连接断开时 slave 可以自动重新建立连接。如果 master 同时收到多个 slave 发来的同步连接命令,只会启动一个进程来写数据库镜像,然后发送给所有 slave。

配置 slave服务器很简单,只需要在配置文件中加入如下配置

slaveof 192.168.1.1 6379 #指定 master的 ip 和端口。

3.3 Redis持久化
通常Redis将数据存储在内存中或虚拟内存中,但它提供了数据持久化功能可以把内存中的数据持久化到磁盘。持久化有什么好处呢?比如可以保证断电后数据不会丢失,升级服务器也会变得更加方便。Redis提供了两种数据持久化的方式。

3.3.1 RDB Snapshotting方式持久化(默认方式)
这种方式就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为 dump.rdb。客户端也可以使用save或者bgsave命令通知redis做一次快照持久化。save操作是在主线程中保存快照的,由于redis是用一个主线程来处理所有客户端的请求,这种方式会阻塞所有客户端请求。所以不推荐使用。另一点需要注意的是,每次快照持久化都是将内存数据完整写入到磁盘一次,并不是增量的只同步增量数据。如果数据量大的话,写操作会比较多,必然会引起大量的磁盘IO操作,可能会严重影响性能。

这种方式的缺点也是显而易见的,由于快照方式是在一定间隔时间做一次的,所以如果 redis 意外当机的话,就会丢失最后一次快照后的所有数据修改。

3.3.2 AOF方式持久化
这种方式 redis 会将每一个收到的写命令都通过 write 函数追加到文件中(默认 appendonly.aof)。当redis重启时会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。当然由于操作系统会在内核中缓存write做的修改,所以可能不是立即写到磁盘上。这样的持久化还是有可能会丢失部分修改。不过我们可以通过配置文件告诉 redis我们想要通过fsync函数强制操作系统写入到磁盘的时机。有三种方式如下(默认是:每秒fsync一次)

appendonly yes //启用日志追加持久化方式

1. appendfsync always //每次收到写命令就立即强制写入磁盘,最慢的,但是保证完全持久化,不推荐使用。

2. appendfsync everysec //每秒钟强制写入磁盘一次,在性能和持久化方面做了很好的折中,推荐使用。

3. appendfsync no //完全依赖操作系统,性能最好,持久化没保证。

日志追加方式同时带来了另一个问题。持久化文件会变的越来越大。例如我们调用 incr test 命令 100 次,文件中必须保存全部 100 条命令,其实有 99 条都是多余的。因为要恢复数据库状态其实文件中保存一条 set test 100 就够了。为了压缩这种持久化方式的日志文件。 redis 提供了 bgrewriteaof 命令。收到此命令 redis 将使用与快照类似的方式将内存中的数据以命令的方式保存到临时文件中,最后替换原来的持久化日志文件。

3.4 Redis虚拟内存
3.4.1 虚拟内存介绍
首先说明下redis的虚拟内存与操作系统虚拟内存不是一码事,但是思路和目的都是相同的。就是暂时把不经常访问的数据从内存交换到磁盘中,从而腾出宝贵的内存空间。对于 redis这样的内存数据库,内存总是不够用的。除了可以将数据分割到多个redis 服务器以外。另外的能够提高数据库容量的办法就是使用虚拟内存技术把那些不经常访问的数据交换到磁盘上。如果我们存储的数据总是有少部分数据被经常访问,大部分数据很少被访问,对于网站来说确实总是只有少量用户经常活跃。当少量数据被经常访问时,使用虚拟内存不但能提高单台redis数据库服务器的容量,而且也不会对性能造成太多影响。

redis没有使用操作系统提供的虚拟内存机制而是自己在用户态实现了自己的虚拟内

存机制。主要的理由有以下两点,操作系统的虚拟内存是以4k/页为最小单位进行交换的。而redis的大多数对象都远小于4k,所以一个操作系统页上可能有多个redis对象。另外redis的集合对象类型如 list,set可能存在于多个操作系统页上。最终可能造成只有10%的key被经常访问,但是所有操作系统页都会被操作系统认为是活跃的,这样只有内存真正耗尽时操作系统才会进行页的交换。相比操作系统的交换方式。redis可以将被交换到磁盘的对象进行压缩,保存到磁盘的对象可以去除指针和对象元数据信息。一般压缩后的对象会比内存中的对象小 10倍。这样redis的虚拟内存会比操作系统的虚拟内存少做很多IO操作。

3.4.1 虚拟内存相关设置
vm-enabled yes 开启虚拟内存功能

vm-swap-file /tmp/redis.swap 交换出来value保存的文件路径/tmp/redis.swapvm-max-memory268435456 redis使用的最大内存上限(256MB),超过上限后 redis开始交换value到磁盘swap文件中。建议设置为系统空闲内存的60 %-80%

vm-page-size 32 每个redis页的大小32个字节

vm-pages 134217728 最多在文件中使用多少个页,交换文件的大小 =(vm-page-size* vm-pages)4 GB

vm-max-threads 8 用于执行value对象换入换出的工作线程数量。0 表示不使用工作线程

3.5 Redis集群
Redis最新稳定版还不支持集群,会在以后的V3.0提供集群功能,现在进度是V3.0已经发布Beta-7版,计划今年年底正式推出稳定版。Redis提供了Partitioning(类似于Memcached在客户端进行Hash映射)机制以支持多实例,下面介绍下Partitioning目标:

1. 突破了单一计算机内存限制,可以利用到多个计算机的内存和

2. 通过配置多个计算机来拓展计算能力,解决网络、网卡瓶颈

Partitioning支持:

1. 通过Client端实现Partitioning,即Client端在读写数据的时候根据自己的选择算法自动选择合适的Redis实例

2. 代理Partitioning,即客户端把读写请求发到代理,由来自选择合适的实例,twitter为Redis和Memcached专门写了一个代理 Twemproxy

3. 查询时自动routing:客户端随机发送命令到任何一个实例,这个实例会把请求转发给合适的实例,这个功能现在还不支持,3.0版本实现的集群功能支持

3.6 其它参考资料
所有特性请参考:http://redis.io/documentation

所有命令请参考:http://redis.io/commands#sorted_set

4 环境安装
4.1 下载、解压、安装
$ wget http://download.redis.io/releases/redis-2.8.13.tar.gz
$ tar xzf redis-2.8.13.tar.gz
$ cd redis-2.8.13
$ make

4.2 启动Redis实例
$ src/redis-server

4.3 通过内置客户端进行测试
$ src/redis-cli
redis> set foo bar
OK
redis> get foo
"bar"
具体请参考:http://redis.io/download
5 配置文件
1. Redis默认不是以守护进程的方式运行,可以通过该配置项修改,使用yes启用守护进程

daemonize yes

2. 当Redis以守护进程方式运行时,Redis默认会把pid写入/var/run/redis.pid文件,可以通过pidfile指定

pidfile/usr/local/redis/var/redis.pid

3. 指定Redis监听端口,默认端口为6379,作者在自己的一篇博文中解释了为什么选用6379作为默认端口,因为6379在手机按键上MERZ对应的号码,而MERZ取自意大利歌女Alessia Merz的名字。

port 6379

4. 绑定的主机地址

bind 127.0.0.1

5. 当客户端闲置多长时间后关闭连接,如果指定为0,表示关闭该功能

timeout 0

6. 对客户端发送ACK信息,linux中单位为秒

tcp-keepalive 0

7. 指定日志记录级别,Redis总共支持四个级别:debug、verbose、notice、warning,默认为notice

loglevel notice

8. 日志记录位置,默认为标准输出

logfile/usr/local/redis/var/redis.log

9. 设置数据库的数量,默认数据库为0,可以使用SELECT 命令在连接上指定数据库id

databases 16

10. 指定在多长时间内,有多少次更新操作,就将数据同步到数据文件,可以多个条件配合

Save分别表示900秒(15分钟)内有1个更改,300秒(5分钟)内有10个更改以及60秒内有10000个更改。

Redis默认配置文件中提供了三个条件:

save 900 1

save 300 10

save 60 10000

11. 持久化失败以后,redis是否停止

stop-writes-on-bgsave-erroryes

12. 指定存储至本地数据库时是否压缩数据,默认为yes,Redis采用LZF压缩,如果为了节省CPU时间,可以关闭该选项,但会导致数据库文件变的巨大

rdbcompression yes

rdbchecksum yes

13. 指定本地数据库文件名,默认值为dump.rdb

dbfilename dump.rdb

14. 指定本地数据库存放目录

dir /usr/local/redis/var

 

15. 设置当本机为slave服务时,设置master服务的IP地址及端口,在Redis启动时,它会自动从master进行数据同步

slaveof

16. 当master服务设置了密码保护时,slave服务连接master的密码

masterauth

17. 设置Redis连接密码,如果配置了连接密码,客户端在连接Redis时需要通过AUTH 命令提供密码,默认关闭

requirepass foobared

18. 设置同一时间最大客户端连接数,在 Redis2.4中,最大连接数是被直接硬编码在代码里面的,而在2.6版本中这个值变成可配置的。maxclients 的默认值是 10000,你也可以在 redis.conf 中对这个值进行修改。当然,这个值只是 Redis 一厢情愿的值,Redis 还会照顾到系统本身对进程使用的文件描述符数量的限制。在启动时 Redis 会检查系统的 soft limit,以查看打开文件描述符的个数上限。如果系统设置的数字,小于咱们希望的最大连接数加32,那么这个 maxclients 的设置将不起作用,Redis 会按系统要求的来设置这个值。(加32是因为 Redis 内部会使用最多32个文件描述符,所以连接能使用的相当于所有能用的描述符号减32)。当上面说的这种情况发生时(maxclients 设置后不起作用的情况),Redis 的启动过程中将会有相应的日志记录。比如下面命令希望设置最大客户端数量为10000,所以 Redis 需要 10000+32 个文件描述符,而系统的最大文件描述符号设置为10144,所以 Redis 只能将maxclients 设置为 10144 – 32 = 10112。

maxclients 10000

19. 指定Redis最大内存限制,Redis在启动时会把数据加载到内存中,达到最大内存后,Redis会先尝试清除已到期或即将到期的Key,当此方法处理后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作。Redis新的vm机制,会把Key存放内存,Value会存放在swap区

maxmemory

slave-serve-stale-data yes

slave-read-only yes

repl-disable-tcp-nodelay no

slave-priority 100

20. 指定是否在每次更新操作后进行日志记录,Redis在默认情况下是异步的把数据写入磁盘,如果不开启,可能会在断电时导致一段时间内的数据丢失。因为Redis本身同步数据文件是按上面slave条件来同步的,所以有的数据会在一段时间内只存在于内存中。默认为no

appendonly no

21. 指定更新日志文件名,默认为appendonly.aof

appendfilename appendonly.aof

22. 指定更新日志条件,共有3个可选值:

no:表示等操作系统进行数据缓存同步到磁盘(快)

always:表示每次更新操作后手动调用fsync()将数据写到磁盘(慢,安全)

everysec:表示每秒同步一次(折衷,默认值)

appendfsync everysec

23. 指定是否启用虚拟内存机制,默认值为no,简单的介绍一下,VM机制将数据分页存放,由Redis将访问量较少的页即冷数据swap到磁盘上,访问多的页面由磁盘自动换出到内存中(在后面的文章我会仔细分析Redis的VM机制)

vm-enabled no

24. 虚拟内存文件路径,默认值为/tmp/redis.swap,不可多个Redis实例共享

vm-swap-file /tmp/redis.swap

25. 将所有大于vm-max-memory的数据存入虚拟内存,无论vm-max-memory设置多小,所有索引数据都是内存存储的(Redis的索引数据就是keys),也就是说,当vm-max-memory设置为0的时候,其实是所有value都存在于磁盘。

vm-max-memory 0

26. Redis swap文件分成了很多的page,一个对象可以保存在多个page上面,但一个page上不能被多个对象共享,vm-page-size是要根据存储的 数据大小来设定的,作者建议如果存储很多小对象,page大小最好设置为32或者64bytes;如果存储很大大对象,则可以使用更大的page,如果不确定,就使用默认值

vm-page-size32

27. 设置swap文件中的page数量,由于页表(一种表示页面空闲或使用的bitmap)是在放在内存中的,,在磁盘上每8个pages将消耗1byte的内存。

vm-pages 134217728

28. 设置访问swap文件的线程数,最好不要超过机器的核数,如果设置为0,那么所有对swap文件的操作都是串行的,可能会造成比较长时间的延迟。默认值为4

vm-max-threads 4

no-appendfsync-on-rewrite no

auto-aof-rewrite-percentage 100

auto-aof-rewrite-min-size 64mb

lua-time-limit 5000

slowlog-log-slower-than 10000

slowlog-max-len 128

29. 指定在超过一定的数量或者最大的元素超过某一临界值时,采用一种特殊的哈希算法

hash-max-ziplist-entries 512

hash-max-ziplist-value 64

list-max-ziplist-entries 512

list-max-ziplist-value 64

set-max-intset-entries 512

zset-max-ziplist-entries 128

zset-max-ziplist-value 64

30. 指定是否激活重置哈希,默认为开启(后面在介绍Redis的哈希算法时具体介绍)

activerehashing yes

client-output-buffer-limit normal 0 00

client-output-buffer-limit slave256mb 64mb 60

client-output-buffer-limit pubsub32mb 8mb 60

hz 10

31. 指定包含其它的配置文件,可以在同一主机上多个Redis实例之间使用同一份配置文件,而同时各个实例又拥有自己的特定配置文件

具体请参考:https://raw.githubusercontent.com/antirez/redis/2.6/redis.conf

 

6 示例代码
我们选择的Redis客户端实现是Jedis,这是比较成熟的Redis客户端,也是官网力荐的。因为Redis本身不支持集群功能,我们的思路是在集群中启动多个独立的实例,在客户端通过“一致性hash”技术来实现对集群中各个Redis实例进行读写,Jedis通过ShardJedis对象对”一致性hash”提供支持。

 

6.1 定义一个ShardJedisPool
static ShardedJedisPoolpool;

static {
//配置

JedisPoolConfig config = new JedisPoolConfig(); config.setMaxTotal(1024);

config.setMaxIdle(5);

config.setMaxWaitMillis(1000*2);

//创建ShardJedis对象

String masterA = "10.10.0.3";

String masterB = "10.10.0.131";

int port = 6379;

List<JedisShardInfo> jdsInfoList =new ArrayList<JedisShardInfo>(2);

JedisShardInfo infoA = new JedisShardInfo(masterA, port);

JedisShardInfo infoB = new JedisShardInfo(masterB, port);

jdsInfoList.add(infoA);

jdsInfoList.add(infoB);

//创建ShardJedisPool

pool = new ShardedJedisPool(config, jdsInfoList, Hashing.MURMUR_HASH, Sharded.DEFAULT_KEY_TAG_PATTERN);

}

 

6.2 使用ShardJedis对象
ShardedJedis jedis= pool.getResource();//从池中获取

jedis.set("foo","bar");//写入Redis
String value = jedis.get("foo");//读出
pool.returnResource(jedis);//释放掉资源

---------------------------------------------

什么是Redis?
Redis 是开源免费的,遵守BSD协议,是一个高性能的key-value非关系型数据库。

redis单线程问题
所谓的单线程指的是网络请求模块使用了一个线程(所以不需考虑并发安全性),即一个线程处理所有网络请求,其他模块仍用了多个线程。

redis采用多路复用机制:即多个网络socket复用一个io线程,实际是单个线程通过记录跟踪每一个Sock(I/O流)的状态来同时管理多个I/O流.

Redis特点:
Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。

Redis不仅仅支持简单的key-value类型的数据,同时还提供String,list,set,zset,hash等数据结构的存储。

Redis支持数据的备份,即master-slave模式的数据备份。

性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。

原子 – Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行。

丰富的特性 – Redis还支持 publish/subscribe, 通知, 设置key有效期等等特性。

redis作用:
可以减轻数据库压力,查询内存比查询数据库效率高。

Redis应用:
token生成、session共享、分布式锁、自增id、验证码等。

比较重要的3个可执行文件:
redis-server:Redis服务器程序

redis-cli:Redis客户端程序,它是一个命令行操作工具。也可以使用telnet根据其纯文本协议操作。

redis-benchmark:Redis性能测试工具,测试Redis在你的系统及配置下的读写性能。 

redis数据结构
存储字符串
1.set key value:设定key持有指定的字符串value,如果该key存在则进行覆盖操作,总是返回OK
2.get key: 获取key的value。如果与该key关联的value不是String类型,redis将返回错误信息,因为get命令只能用于获取String value;如果该key不存在,返回null。
3.getset key value:先获取该key的值,然后在设置该key的值。
4.incr key:将指定的key的value原子性的递增1. 如果该key不存在,其初始值为0,在incr之后其值为1。如果value的值不能转成整型,如hello,该操作将执行失败并返回相应的错误信息
5.decr key:将指定的key的value原子性的递减1.如果该key不存在,其初始值为0,在incr之后其值为-1。如果value的值不能转成整型,如hello,该操作将执 行失败并返回相应的错误信息。
6.incrby key increment:将指定的key的value原子性增加increment,如果该key不存在,器初始值为0,在incrby之后,该值为increment。如果该值不能转成 整型,如hello则失败并返回错误信息
7.decrby key decrement:将指定的key的value原子性减少decrement,如果该key不存在,器初始值为0,在decrby之后,该值为decrement。如果该值不能 转成整型,如hello则失败并返回错误信息
8.append key value:如果该key存在,则在原有的value后追加该值;如果该key 不存在,则重新创建一个key/value

存储list类型

1.lpush key value1 value2...:在指定的key所关联的list的头部插入所有的values,如果该key不存在,该命令在插入的之前创建一个与该key关联的空链表,之后再向该链表的头部插入数据。插入成功,返回元素的个数。

2.rpush key value1、value2…:在该list的尾部添加元素

3.lrange key start end:获取链表中从start到end的元素的值,start、end可为负数,若为-1则表示链表尾部的元素,-2则表示倒数第二个,依次类推…

4.lpushx key value:仅当参数中指定的key存在时(如果与key管理的list中没有值时,则该key是不存在的)在指定的key所关联的list的头部插入value。

5.rpushx key value:在该list的尾部添加元素

6.lpop key:返回并弹出指定的key关联的链表中的第一个元素,即头部元素

7.rpop key:从尾部弹出元素

8.rpoplpush resource destination:将链表中的尾部元素弹出并添加到头部

9.llen key:返回指定的key关联的链表中的元素的数量。

10.lset key index value:设置链表中的index的脚标的元素值,0代表链表的头元素,-1代表链表的尾元素。

 

存储Set

添加或删除元素
1.sadd key values[value1、value2……]:向set中添加数据,如果该key的值有则不会重复添加
例如:sadd myset a b c

2.srem key members[member1、menber2…]:删除set中的指定成员
例如:srem myset 1 2 3

获得集合中的元素
1.smembers key :获取set中所有的成员
smembers myset

2.sismember key menber :判断参数中指定的成员是否在该set中,1表示存在,0表示不存在或者该key本身就不存在(无论集合中有多少元素都可以极速的返回结果)

集合的差集运算 A-B
sdiff key1 key2 … : 返回key1与key2中相差的成员,而且与key的顺序有关。即返回差集。

集合的交集运算
sinter key1 key2 key3… :返回交集

集合的并集运算
sunion key1 key2 key3… : 返回并集

扩展命令(了解)
scard key : 获取set中的成员数量
例子:scard myset

srandmember key : 随机返回set中的一个成员

sdiffstore destination key1 key2 …: 将key1 key2 相差的成员存储到destination中

sinterstore destination key[key…] : 将返回的交集存储在destination上

suninonstore destination key[key…] : 将返回的并集存储在destination上

 

存储hash

1.赋值
hset key field value : 为指定的key设定field/value对

hmset key field1 value1 field2 value2 field3 value3 为指定的key设定多个field/value对

2.取值
hget key field : 返回指定的key中的field的值

hmget key field1 field2 field3 : 获取key中的多个field值

hkeys key : 获取所有的key

hvals key :获取所有的value

hgetall key : 获取key中的所有field 中的所有field-value

3.删除
hdel key field[field…] : 可以删除一个或多个字段,返回是被删除的字段个数

del key : 删除整个list

4.增加数字
hincrby key field increment :设置key中field的值增加increment,如: age增加20
hincrby myhash age 5

自学命令:
hexists key field : 判断指定的key中的field是否存在

hlen key : 获取key所包含的field的数量

hkeys key :获得所有的key
hkeys myhash

hvals key :获得所有的value
hvals myhash

 

存储sortedset

1.添加元素
zadd key score member score2 member2…:将所有成员以及该成员的分数存放到sorted-set中。如果该元素已经存在则会用新的分数替换原有的分数。返回值是新加入到集合中的元素个数。(根据分数升序排列)

2.获得元素
zscore key member :返回指定成员的分数
zcard key :获得集合中的成员数量

3.删除元素
zrem key member[member…] :移除集合中指定的成员,可以指定多个成员

4.范围查询
zrange key strat end [withscores]:获取集合中角标为start-end的成员,[withscore]参数表明返回的成员包含其分数。

zremrangebyrank key start stop :按照排名范围删除元素

zremrangescore key min max :按照分数范围删除元素

扩展命令(了解)
zrangebyscore key min max [withscore] [limit offset count] :返回分数在[min,max]的成员并按照分数从低到高排序。[withscore]:显示分数;[limit offset count];offset,表明从脚标为offset的元素开始并返回count个成员

zincrby key increment member :设置指定成员的增加分数。返回值是修改后的分数

zcount key min max:获取分数在[min,max]之间的成员个数

zrank key member:返回成员在集合中的排名(从小到大)

zrevrank key member :返回成员在集合中的排名(从大到小)

 

key的通用操作
keys pattern : 获取所有与pattern匹配的key ,返回所有与该key匹配的keys。 *表示任意一个或者多个字符, ?表示任意一个字符

del key1 key2… :删除指定的key
del my1 my2 my3

exists key :判断该key是否存在,1代表存在,0代表不存在

rename key newkey :为key重命名

expire key second:设置过期时间,单位秒

ttl key:获取该key所剩的超时时间,如果没有设置超时,返回-1,如果返回-2表示超时不存在。

persist key:持久化key

192.168.25.153:6379> expire Hello 100
(integer) 1
192.168.25.153:6379> ttl Hello
(integer) 77

type key:获取指定key的类型。该命令将以字符串的格式返回。返回的字符串为string 、list 、set 、hash 和 zset,如果key不存在返回none。
例如: type newcompany
none

 

redis的数据类型,以及每种数据类型的使用场景
(一)String
这个其实没啥好说的,最常规的set/get操作,value可以是String也可以是数字。一般做一些复杂的计数功能的缓存。

(二)hash
这里value存放的是结构化的对象,比较方便的就是操作其中的某个字段。博主在做单点登录的时候,就是用这种数据结构存储用户信息,以cookieId作为key,设置30分钟为缓存过期时间,能很好的模拟出类似session的效果。

(三)list
使用List的数据结构,可以做简单的消息队列的功能。另外还有一个就是,可以利用lrange命令,做基于redis的分页功能,性能极佳,用户体验好。

(四)set
因为set堆放的是一堆不重复值的集合。所以可以做全局去重的功能。为什么不用JVM自带的Set进行去重?因为我们的系统一般都是集群部署,使用JVM自带的Set,比较麻烦,难道为了一个做一个全局去重,再起一个公共服务,太麻烦了。
另外,就是利用交集、并集、差集等操作,可以计算共同喜好,全部的喜好,自己独有的喜好等功能。

(五)sorted set

sorted set多了一个权重参数score,集合中的元素能够按score进行排列。可以做排行榜应用,取TOP N操作。另外,参照另一篇《分布式之延时任务方案解析》,该文指出了sorted set可以用来做延时任务。最后一个应用就是可以做范围查找。

 

redis的过期策略以及内存淘汰机制
分析:这个问题其实相当重要,到底redis有没用到家,这个问题就可以看出来。比如你redis只能存5G数据,可是你写了10G,那会删5G的数据。怎么删的,这个问题思考过么?还有,你的数据已经设置了过期时间,但是时间到了,内存占用率还是比较高,有思考过原因么?
回答:
redis采用的是定期删除+惰性删除策略。
为什么不用定时删除策略?
定时删除,用一个定时器来负责监视key,过期则自动删除。虽然内存及时释放,但是十分消耗CPU资源。在大并发请求下,CPU要将时间应用在处理请求,而不是删除key,因此没有采用这一策略.
定期删除+惰性删除是如何工作的呢?
定期删除,redis默认每个100ms检查,是否有过期的key,有过期key则删除。需要说明的是,redis不是每个100ms将所有的key检查一次,而是随机抽取进行检查(如果每隔100ms,全部key进行检查,redis岂不是卡死)。因此,如果只采用定期删除策略,会导致很多key到时间没有删除。
于是,惰性删除派上用场。也就是说在你获取某个key的时候,redis会检查一下,这个key如果设置了过期时间那么是否过期了?如果过期了此时就会删除。
采用定期删除+惰性删除就没其他问题了么?
不是的,如果定期删除没删除key。然后你也没即时去请求key,也就是说惰性删除也没生效。这样,redis的内存会越来越高。那么就应该采用内存淘汰机制。
在redis.conf中有一行配置

# maxmemory-policy allkeys-lru
该配置就是配内存淘汰策略的(什么,你没配过?好好反省一下自己)
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和数据库双写一致性问题
分析:一致性问题是分布式常见问题,还可以再分为最终一致性和强一致性。数据库和缓存双写,就必然会存在不一致的问题。答这个问题,先明白一个前提。就是如果对数据有强一致性要求,不能放缓存。我们所做的一切,只能保证最终一致性。另外,我们所做的方案其实从根本上来说,只能说降低不一致发生的概率,无法完全避免。因此,有强一致性要求的数据,不能放缓存。
回答:首先,采取正确更新策略,先更新数据库,再删缓存。其次,因为可能存在删除缓存失败的问题,提供一个补偿措施即可,例如利用消息队列。

 

如何应对缓存穿透和缓存雪崩问题
分析:这两个问题,说句实在话,一般中小型传统软件企业,很难碰到这个问题。如果有大并发的项目,流量有几百万左右。这两个问题一定要深刻考虑。
回答:如下所示

缓存穿透:即黑客故意去请求缓存中不存在的数据,导致所有的请求都怼到数据库上,从而数据库连接异常。

解决方案:
(一)利用互斥锁,缓存失效的时候,先去获得锁,得到锁了,再去请求数据库。没得到锁,则休眠一段时间重试
(二)采用异步更新策略,无论key是否取到值,都直接返回。value值中维护一个缓存失效时间,缓存如果过期,异步起一个线程去读数据库,更新缓存。需要做缓存预热(项目启动前,先加载缓存)操作。
(三)提供一个能迅速判断请求是否有效的拦截机制,比如,利用布隆过滤器,内部维护一系列合法有效的key。迅速判断出,请求所携带的Key是否合法有效。如果不合法,则直接返回。

 

缓存雪崩,即缓存同一时间大面积的失效,这个时候又来了一波请求,结果请求都怼到数据库上,从而导致数据库连接异常。

解决方案:
(一)给缓存的失效时间,加上一个随机值,避免集体失效。
(二)使用互斥锁,但是该方案吞吐量明显下降了。
(三)双缓存。我们有两个缓存,缓存A和缓存B。缓存A的失效时间为20分钟,缓存B不设失效时间。自己做缓存预热操作。然后细分以下几个小点

I 从缓存A读数据库,有则直接返回

II A没有数据,直接从B读数据,直接返回,并且异步启动一个更新线程。

III 更新线程同时更新缓存A和缓存B。

 

如何解决redis的并发竞争key问题
分析:这个问题大致就是,同时有多个子系统去set一个key。这个时候要注意什么呢?大家思考过么。需要说明一下,博主提前百度了一下,发现答案基本都是推荐用redis事务机制。博主不推荐使用redis的事务机制。因为我们的生产环境,基本都是redis集群环境,做了数据分片操作。你一个事务中有涉及到多个key操作的时候,这多个key不一定都存储在同一个redis-server上。因此,redis的事务机制,十分鸡肋。

回答:如下所示
(1)如果对这个key操作,不要求顺序
这种情况下,准备一个分布式锁,大家去抢锁,抢到锁就做set操作即可,比较简单。
(2)如果对这个key操作,要求顺序
假设有一个key1,系统A需要将key1设置为valueA,系统B需要将key1设置为valueB,系统C需要将key1设置为valueC.
期望按照key1的value值按照 valueA-->valueB-->valueC的顺序变化。这种时候我们在数据写入数据库的时候,需要保存一个时间戳。假设时间戳如下

系统A key 1 {valueA 3:00}
系统B key 1 {valueB 3:05}
系统C key 1 {valueC 3:10}
那么,假设这会系统B先抢到锁,将key1设置为{valueB 3:05}。接下来系统A抢到锁,发现自己的valueA的时间戳早于缓存中的时间戳,那就不做set操作了。以此类推。

其他方法,比如利用队列,将set方法变成串行访问也可以。总之,灵活变通。

 

redis中支持事务吗?【了解即可】
支持,使用multi开启事务,使用exec提交事务。

 

redis发布订阅【了解即可】
Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。

Redis 客户端可以订阅任意数量的频道。

下图展示了频道 channel1 , 以及订阅这个频道的三个客户端 —— client2 、 client5 和 client1 之间的关系:

当有新消息通过 PUBLISH 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端:

 

redis 127.0.0.1:6379> SUBSCRIBE redisChat

Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "redisChat"
3) (integer) 1


现在,我们先重新开启个 redis 客户端,然后在同一个频道 redisChat 发布两次消息,订阅者就能接收到消息。

redis 127.0.0.1:6379> PUBLISH redisChat "hello"

(integer) 1

redis 127.0.0.1:6379> PUBLISH redisChat "What 's your name?"

(integer) 1

# 订阅者的客户端会显示如下消息
1) "message"
2) "redisChat"
3) "hello"
1) "message"
2) "redisChat"
3) "What 's your name?"

(1)什么是redis?

Redis 是一个基于内存的高性能key-value数据库。 (有空再补充,有理解错误或不足欢迎指正)

(2)Reids的特点

Redis本质上是一个Key-Value类型的内存数据库,很像memcached,整个数据库统统加载在内存当中进行操作,定期通过异步操作把数据库数据flush到硬盘上进行保存。因为是纯内存操作,Redis的性能非常出色,每秒可以处理超过 10万次读写操作,是已知性能最快的Key-Value DB。
Redis的出色之处不仅仅是性能,Redis最大的魅力是支持保存多种数据结构,此外单个value的最大限制是1GB,不像 memcached只能保存1MB的数据,因此Redis可以用来实现很多有用的功能,比方说用他的List来做FIFO双向链表,实现一个轻量级的高性 能消息队列服务,用他的Set可以做高性能的tag系统等等。另外Redis也可以对存入的Key-Value设置expire时间,因此也可以被当作一 个功能加强版的memcached来用。
Redis的主要缺点是数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。

(3)Redis支持的数据类型

Redis通过Key-Value的单值不同类型来区分, 以下是支持的类型:
Strings
Lists
Sets 求交集、并集
Sorted Set 
hashes

(4)为什么redis需要把所有数据放到内存中?

Redis为了达到最快的读写速度将数据都读到内存中,并通过异步的方式将数据写入磁盘。所以redis具有快速和数据持久化的特征。如果不将数据放在内存中,磁盘I/O速度为严重影响redis的性能。在内存越来越便宜的今天,redis将会越来越受欢迎。
如果设置了最大使用的内存,则数据已有记录数达到内存限值后不能继续插入新值。

(5)Redis是单进程单线程的

redis利用队列技术将并发访问变为串行访问,消除了传统数据库串行控制的开销

(6)虚拟内存

当你的key很小而value很大时,使用VM的效果会比较好.因为这样节约的内存比较大.
当你的key不小时,可以考虑使用一些非常方法将很大的key变成很大的value,比如你可以考虑将key,value组合成一个新的value.
vm-max-threads这个参数,可以设置访问swap文件的线程数,设置最好不要超过机器的核数,如果设置为0,那么所有对swap文件的操作都是串行的.可能会造成比较长时间的延迟,但是对数据完整性有很好的保证.

自己测试的时候发现用虚拟内存性能也不错。如果数据量很大,可以考虑分布式或者其他数据库

(7)分布式

redis支持主从的模式。原则:Master会将数据同步到slave,而slave不会将数据同步到master。Slave启动时会连接master来同步数据。

这是一个典型的分布式读写分离模型。我们可以利用master来插入数据,slave提供检索服务。这样可以有效减少单个机器的并发访问数量

(8)读写分离模型

通过增加Slave DB的数量,读的性能可以线性增长。为了避免Master DB的单点故障,集群一般都会采用两台Master DB做双机热备,所以整个集群的读和写的可用性都非常高。
读写分离架构的缺陷在于,不管是Master还是Slave,每个节点都必须保存完整的数据,如果在数据量很大的情况下,集群的扩展能力还是受限于单个节点的存储能力,而且对于Write-intensive类型的应用,读写分离架构并不适合。                                               

(9)数据分片模型

为了解决读写分离模型的缺陷,可以将数据分片模型应用进来。

可以将每个节点看成都是独立的master,然后通过业务实现数据分片。

结合上面两种模型,可以将每个master设计成由一个master和多个slave组成的模型。

(10)Redis的回收策略

  • volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰

  • volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰

  • volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰

  • allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰

  • allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰

  • no-enviction(驱逐):禁止驱逐数据

    • 1. 使用Redis有哪些好处?

      (1) 速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)

      (2) 支持丰富数据类型,支持string,list,set,sorted set,hash

      (3) 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行

      (4) 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除

      2. redis相比memcached有哪些优势?

      (1) memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类型

      (2) redis的速度比memcached快很多

      (3) redis可以持久化其数据

      3. redis常见性能问题和解决方案:

      (1) Master最好不要做任何持久化工作,如RDB内存快照和AOF日志文件

      (2) 如果数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一次

      (3) 为了主从复制的速度和连接的稳定性,Master和Slave最好在同一个局域网内

      (4) 尽量避免在压力很大的主库上增加从库

      (5) 主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3...

      这样的结构方便解决单点故障问题,实现Slave对Master的替换。如果Master挂了,可以立刻启用Slave1做Master,其他不变。

      4. MySQL里有2000w数据,redis中只存20w的数据,如何保证redis中的数据都是热点数据

       相关知识:redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略。redis 提供 6种数据淘汰策略:

      voltile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰

      volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰

      volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰

      allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰

      allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰

      no-enviction(驱逐):禁止驱逐数据

      5. Memcache与Redis的区别都有哪些?

      1)、存储方式

      Memecache把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。

      Redis有部份存在硬盘上,这样能保证数据的持久性。

      2)、数据支持类型

      Memcache对数据类型支持相对简单。

      Redis有复杂的数据类型。

      3)、使用底层模型不同

      它们之间底层实现方式 以及与客户端之间通信的应用协议不一样。

      Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。

      4),value大小

      redis最大可以达到1GB,而memcache只有1MB

      6. Redis 常见的性能问题都有哪些?如何解决?

      1).Master写内存快照,save命令调度rdbSave函数,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性暂停服务,所以Master最好不要写内存快照。

      2).Master AOF持久化,如果不重写AOF文件,这个持久化方式对性能的影响是最小的,但是AOF文件会不断增大,AOF文件过大会影响Master重启的恢复速度。Master最好不要做任何持久化工作,包括内存快照和AOF日志文件,特别是不要启用内存快照做持久化,如果数据比较关键,某个Slave开启AOF备份数据,策略为每秒同步一次。

      3).Master调用BGREWRITEAOF重写AOF文件,AOF在重写的时候会占大量的CPU和内存资源,导致服务load过高,出现短暂服务暂停现象。

      4). Redis主从复制的性能问题,为了主从复制的速度和连接的稳定性,Slave和Master最好在同一个局域网内 

      7, redis 最适合的场景

      Redis最适合所有数据in-momory的场景,虽然Redis也提供持久化功能,但实际更多的是一个disk-backed的功能,跟传统意义上的持久化有比较大的差别,那么可能大家就会有疑问,似乎Redis更像一个加强版的Memcached,那么何时使用Memcached,何时使用Redis呢?

             如果简单地比较Redis与Memcached的区别,大多数都会得到以下观点:

           1 、Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
           2 、Redis支持数据的备份,即master-slave模式的数据备份。
           3 、Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。

      (1)、会话缓存(Session Cache)

      最常用的一种使用Redis的情景是会话缓存(session cache)。用Redis缓存会话比其他存储(如Memcached)的优势在于:Redis提供持久化。当维护一个不是严格要求一致性的缓存时,如果用户的购物车信息全部丢失,大部分人都会不高兴的,现在,他们还会这样吗?

      幸运的是,随着 Redis 这些年的改进,很容易找到怎么恰当的使用Redis来缓存会话的文档。甚至广为人知的商业平台Magento也提供Redis的插件。

      (2)、全页缓存(FPC)

      除基本的会话token之外,Redis还提供很简便的FPC平台。回到一致性问题,即使重启了Redis实例,因为有磁盘的持久化,用户也不会看到页面加载速度的下降,这是一个极大改进,类似PHP本地FPC。

      再次以Magento为例,Magento提供一个插件来使用Redis作为全页缓存后端

      此外,对WordPress的用户来说,Pantheon有一个非常好的插件  wp-redis,这个插件能帮助你以最快速度加载你曾浏览过的页面。

      (3)、队列

      Reids在内存存储引擎领域的一大优点是提供 list 和 set 操作,这使得Redis能作为一个很好的消息队列平台来使用。Redis作为队列使用的操作,就类似于本地程序语言(如Python)对 list 的 push/pop 操作。

      如果你快速的在Google中搜索“Redis queues”,你马上就能找到大量的开源项目,这些项目的目的就是利用Redis创建非常好的后端工具,以满足各种队列需求。例如,Celery有一个后台就是使用Redis作为broker,你可以从这里去查看。

      (4),排行榜/计数器

      Redis在内存中对数字进行递增或递减的操作实现的非常好。集合(Set)和有序集合(Sorted Set)也使得我们在执行这些操作的时候变的非常简单,Redis只是正好提供了这两种数据结构。所以,我们要从排序集合中获取到排名最靠前的10个用户–我们称之为“user_scores”,我们只需要像下面一样执行即可:

      当然,这是假定你是根据你用户的分数做递增的排序。如果你想返回用户及用户的分数,你需要这样执行:

      ZRANGE user_scores 0 10 WITHSCORES

      Agora Games就是一个很好的例子,用Ruby实现的,它的排行榜就是使用Redis来存储数据的,你可以在这里看到。

      (5)、发布/订阅

      最后(但肯定不是最不重要的)是Redis的发布/订阅功能。发布/订阅的使用场景确实非常多。我已看见人们在社交网络连接中使用,还可作为基于发布/订阅的脚本触发器,甚至用Redis的发布/订阅功能来建立聊天系统!(不,这是真的,你可以去核实)。

    • Redis提供的所有特性中,我感觉这个是喜欢的人最少的一个,虽然它为用户提供如果此多功能。

posted @ 2022-02-15 19:43  hanease  阅读(429)  评论(0编辑  收藏  举报