Redis详解

redis是什么?

redis是一款NoSql(非关系型数据库)数据库,它是一个基于内存存储的key-value数据库 。

特点

1、支持多种数据类型:String、list、hash、set、zset等;

2、Redis以内存作为数据存储介质,所以读写数据的效率极高,远远超过其他数据库。Redis能读的速度是110000次/s,写的速度是81000次/s ;

3、支持数据持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。支持AOF和RDB两种持久化,RDB是将内存中的数据压缩后写入到硬盘上,AOF是将操作的日志整理写到硬盘,而且通过配置可以设置不同的持久化方式,这两种方式有效减轻了服务器的压力,同时在很大程度上防止了数据丢失;

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

5、可用于缓存、消息等,可以给key设置过期时间,过期后自动删除;

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

7、支持主从复制,主服务器(master)执行添加、修改、删除,从服务器执行查询;

8、高可用及分布式

    Redis-Sentinel(v2.8)支持高可用 

    Redis-Cluster(v3.0)支持分布式

安全性

1、IP绑定,如果不许要直接对外提供服务,在redis.conf配置文件中绑定具体的ip地址这样只有该ip地址才能访问redis服务器,如:bind 127.0.0.1就行了,切忌bind 0.0.0.0!

2、端口设置。修改默认的6379,一定程度上避免被扫描。

3、设置密码。Redis的密码是通过requirepass以明文的形式配置在conf文件里的,所以要尽可能得长和复杂,降低被破解的风险,设置长度为20位左右的密码,从而保证只有进行了密码授权才能进行相关的操作。

4、重命名或禁用某些高危操作命令。向config、flushall、flushdb这些操作都是很关键的,不小心就会导致数据库不可用。可以在配置文件中通过rename-command重命名或禁用这些命令。

Redis事务机制

redis的事务机制与常见的关系型数据库有很大的区别,就是不支持事务回滚,事务执行时会阻塞其他客户端的请求执行,也就是redis事务的执行时串行的。单个redis命令的执行是原子性操作,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的。而且,事务中的所有指令都会被序列化,开始执行过程中,不会被即时过来的指令打断,其需要经历三个过程,分别为开始事务、命令入队以及执行事务。事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。

redis事务基础命令:

multi:通知redis服务器开启一个事务,注意:只是标记事务块的开始,而不是执行。redis会将后续的命令逐个放入队列中,

exec:通知redis开始执行事务,在一个事务中执行所有先前放入队列中的命令,然后恢复正常的连接状态,当使用watch命令时,只有当受监控的键没有被修改时,exec命令才会执行事务中的命令,这种方式利用了检查再设置(CAS)的机制。这个命令的返回值是一个数组,其中的每个元素分别是原子化事务中的每个命令的返回值。当使用watch命令时,如果事务执行终止,那么exce命令就会返回一个Null值。

discard:通知redis取消事务。清除所有先前在一个事务中放入队列的命令,然后恢复正常的连接状态。如果使用了watch命令,那么discard命令就会将当前连接监控的所有键取消监控。

watch:监控某一键值对,它的作用是在事务执行之前如果监控的键值被修改,事务会被取消。

unwatch:清除所有先前为一个事务监控的键。如果你调用了exec或者discard命令,那么就不需要手动调用nuwatch命令。

redis的数据类型

String类型:

String是redis最基本的类型,可以理解成与memcached一模一样的类型,一个key对应一个value,string类型是二进制安全的,意思是redis的string可以包含任何数据,比如jpg图片或者序列化的对象,它是redis最基本的数据类型,一个键最大能存储512MB。

命令:

redis 127.0.0.1:6379> SET name "runoob"
OK
redis 127.0.0.1:6379> GET name
"runoob"

Hash类型:

hash是一个键值对集合, hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。每个 hash 可以存储 232 -1 键值对(40多亿)。

命令:

redis> HMSET myhash field1 "Hello" field2 "World"
"OK"
redis> HGET myhash field1
"Hello"
redis> HGET myhash field2
"World"

list类型:

Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。列表最多可存储 232 - 1 元素 (4294967295, 每个列表可存储40多亿)。

命令:

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

set集合:

Redis的Set是string类型的无序集合。集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。集合中最大的成员数为 232 - 1(4294967295, 每个集合可存储40多亿个成员)。

sadd命令:

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

 

asdd key member
redis 127.0.0.1:6379> sadd runoob redis
(integer) 1
redis 127.0.0.1:6379> sadd runoob mongodb
(integer) 1
redis 127.0.0.1:6379> sadd runoob rabitmq
(integer) 1
redis 127.0.0.1:6379> sadd runoob rabitmq
(integer) 0
redis 127.0.0.1:6379> smembers runoob
​
1) "redis"
2) "rabitmq"
3) "mongodb"

zset类型:

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

zadd命令:

添加元素到集合,元素在集合中存在则更新对应score。

 

zadd key score member
redis 127.0.0.1:6379> zadd runoob 0 redis
(integer) 1
redis 127.0.0.1:6379> zadd runoob 0 mongodb
(integer) 1
redis 127.0.0.1:6379> zadd runoob 0 rabitmq
(integer) 1
redis 127.0.0.1:6379> zadd runoob 0 rabitmq
(integer) 0
redis 127.0.0.1:6379> > ZRANGEBYSCORE runoob 0 1000
1) "mongodb"
2) "rabitmq"
3) "redis"

redis的三大清除策略:

1、定时删除:

定时删除对内存是最友好的,可以保证过期键会被尽快的删除掉,内存被尽早的释放出来,虽然对内存比较友好,但是服务器需要创建大量的定时器,来实现定时删除,如果内存现在并不缺少,有空闲的内存,而这个时候有大量的命令请求在等待服务器处理,那么服务器也应该优先把CPU时间给处理客户端请求上,而不是删除过期键上。

2、懒惰删除:

客户端现在取这个数据,这个key已经过期了,明显不能返回给客户端,这个时候也就是使用懒惰删除了。可以看出,这个策略对内存是极不好的,但是CPU时间是最友好的,不会轻易动用CPU时间去删除过期键。

3、定期删除

比定时时间长一点,不会时刻的去检查过期键,删除过期键,定期删除,是每隔一段时间检查一次数据库,删除里面的过期键,很明显定期删除是定时删除和懒惰删除的一个折中。

redis的回收策略

1、volatile-lru:从设置了过期时间的数据集中,选择最近最久未使用的数据释放;

2、allkeys-lru:从数据集中(包裹设置过期时间以及未设置过期时间的数据集中),选择最近最久未使用的数据释放;

3、volatile-random:从设置了过期时间的数据集中,随机选择一个数据进行释放;

4、allkeys-random:从数据集中(包裹了设置过期时间以及未设置过期时间)随机选择一个数据进行入释放;

5、volatile-ttl:从设置了过期时间的数据集中,选择马上就要过期的数据进行释放操作;

6、noeviction:不删除任意数据(但redis还会根据引用计数器进行释放)这时如果内存不够时,会直接返回错误;

使用策略

1、如果数据呈现幂律分布,也就是一部分数据访问频率高,一部分数据访问频率低,则使用allkeys-lru;

2、如果数据呈现平等分布,也就是所有的数据访问频率都相同,则使用allkeys-random;

注:redis回收算法,实际不是严谨的lru算法,而是抽样回收数据,这样算是为了减少消耗内存使用,但是抽样回收的缓存和全部数据回收缓存差异非常小,或者根本就没有;

生产使用

1、先预测好系统所需要的内存高峰,部署相对应内存的缓存服务器;

2、设置maxmemory和相对应的回收策略算法,设置最好为物理内存的3/4,或者比例更小,因为redis复制数据等其他服务时,也是需要缓存的。以防缓存数据过大致使redis崩溃,造成系统出错不可用。牺牲一部分缓存数据,保存整体系统可用性;

redis性能调优:

针对redis的性能优化主要从几个层面入手:

1、最初的也是最重要的,确保redis没有执行耗时长的命令

2、使用pipelining将连续执行的命令组合执行

3、操作系统的Transparent huge pages功能必须关闭:

    echo never > /sys/kernel/mm/transparent_hugepage/enabled

4、如果在虚拟机中运行Redis,可能天然就有虚拟机环境带来的固有延迟。可以通过./redis-cli --intrinsic-latency 100命令查看固有延迟。同时如果对Redis的性能有较高要求的话,应尽可能在物理机上直接部署Redis。

5、检查数据持久化策略

6、可以引入读写分离机制

长耗时命令

Redis绝大多数读写命令的时间复杂度都在O(1)到O(N)之间,在文本和官方文档中均对每个命令的时间复杂度有说明。通常来说,O(1)的命令是安全的,O(N)命令在使用时需要注意,如果N的数量级不可预知,则应避免使用。例如对一个field数未知的Hash数据执行HGETALL/HKEYS/HVALS命令,通常来说这些命令执行的很快,但如果这个Hash中的field数量极多,耗时就会成倍增长。又如使用SUNION对两个Set执行Union操作,或使用SORT对List/Set执行排序操作等时,都应该严加注意。

避免在使用这些O(N)命令时发生问题主要有几个办法:

1、不要把List当做列表使用,仅当做队列来使用

2、通过机制严格控制Hash、Set、Sorted Set的大小

3、可能的话,将排序、并集、交集等操作放在客户端执行

4、绝对禁止使用KEYS命令

5、避免一次性遍历集合类型的所有成员,而应使用SCAN类的命令进行分批的,游标式的遍历
Redis提供了Slow Log功能,可以自动记录耗时较长的命令。相关的配置参数有两个:

slowlog-log-slower-than xxxms  #执行时间慢于xxx毫秒的命令计入Slow Log
slowlog-max-len xxx  #Slow Log的长度,即最大纪录多少条Slow Log

使用slowlog get[number]命令,可以输出最近进入slow log的number条命令;

使用slowlog reset命令,可以重置slow log

网络引发的延迟

1、尽可能使用长连接或连接池,避免频繁创建销毁连接

2、客户端进行的批量数据操作,应使用Pipeline特性在一次交互中完成。具体请参照本文的Pipelining章节

数据持久化引发的延迟

redis的数据持久化工作本身就会带来延迟,需要根据数据的安全级别和性能要求制定合理的持久化策略:

1、AOF + fsync always的设置虽然能够绝对确保数据安全,但每个操作都会触发一次fsync,会对Redis的性能有比较明显的影响;

2、AOF + fsync every second是比较好的折中方案,每秒fsync一次;

3、AOF + fsync never会提供AOF持久化方案下的最优性能;

4、使用RDB持久化通常会提供比使用AOF更高的性能,但需要注意RDB的策略配置;

5、每一次RDB快照和AOF Rewrite都需要Redis主进程进行fork操作。fork操作本身可能会产生较高的耗时,与CPU和Redis占用的内存大小有关。根据具体的情况合理配置RDB快照和AOF Rewrite时机,避免过于频繁的fork带来的延迟

Redis在fork子进程时需要将内存分页表拷贝至子进程,以占用了24GB内存的Redis实例为例,共需要拷贝24GB / 4kB * 8 = 48MB的数据。在使用单Xeon 2.27Ghz的物理机上,这一fork操作耗时216ms。

可以通过INFO命令返回的latest_fork_usec字段查看上一次fork操作的耗时(微秒)

swap引发的延迟

当Linux将Redis所用的内存分页移至swap空间时,将会阻塞Redis进程,导致Redis出现不正常的延迟。Swap通常在物理内存不足或一些进程在进行大量I/O操作时发生,应尽可能避免上述两种情况的出现。

/proc/<pid>/smaps文件中会保存进程的swap记录,通过查看这个文件,能够判断Redis的延迟是否由Swap产生。如果这个文件中记录了较大的Swap size,则说明延迟很有可能是Swap造成的。

数据淘汰引发的延迟

当同一秒内有大量key过期时,也会引发Redis的延迟。在使用时应尽量将key的失效时间错开;

引入读写分离机制

Redis的主从复制能力可以实现一主多从的多节点架构,在这一架构下,主节点接收所有写请求,并将数据同步给多个从节点。
在这一基础上,我们可以让从节点提供对实时性要求不高的读请求服务,以减小主节点的压力。
尤其是针对一些使用了长耗时命令的统计类任务,完全可以指定在一个或多个从节点上执行,避免这些长耗时命令影响其他请求的响应。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值