深入理解Redis

Redis作为目前非常火的非关系型数据库我们有必要花精力和时间来研究,redis是一个开源的、使用C语言编写的单线程的、支持网络交互的、可基于内存也可持久化的Key-Value数据库。

Redis的优势

redis较其他数据库最明显的优势就是性能和并发上:

  • redis是纯内存操作,所以它能够做出快速响应;
  • 高并发的情况下,如果所有的请求都直接访问MySql会导致数据库连接异常,而redis支持高性能的主从复制的集群策略,这样就大大提高满足高并发访问及快速响应,请求会优先访问redis请求数据,从而避免高并发情况下都直接访问数据库;

Redis单线程

既然redis是单线程,那为什么它性能还这么高呢?

  • redis是纯内存操作;
  • 因为单线程,避免了频繁的上下文切换;
  • 采用I/O多路复用机制;

Redis支持多数据类型

string,hash,list,set,sorted set

1. string

字符串类型,其实它表示一个可变的字节数组,其key对应的value值可以使字符串也可以是数字。如果对应的value值是字符串时,常规的字符串操作基本上都是支持的;如果对应的value值是数字,可以通过incr/incrby/decr/decrby等操作来实现类似计数器的功能。

2. list

利用这个数据结构可以实现最新消息的排行、消息队列的存取等。

3. hash

可以类比python的字典,比如用于存放微博用户的信息(出生年月、性别、手机号等)

4. set

全局去重

5. sorted set

带有权重(score)的set,根据score自动排序

Redis的持久化

持久化就是把数据保存到磁盘上,而redis提供了两种持久化的方式,分别是RDB(Redis DataBase)和AOF(Append Only File)。

RDB,简而言之,就是在不同的时间点,将redis存储的数据生成快照并存储到磁盘等介质上。

AOF,Append Only File,则是换了一个角度来实现持久化,那就是将redis执行过的所有写指令记录下来,在下次redis重新启动时,只要把这些写指令从前到后再重复执行一遍,就可以实现数据恢复了。

其实RDB和AOF两种方式也可以同时使用,在这种情况下,如果redis重启的话,则会优先采用AOF方式来进行数据恢复,这是因为AOF方式的数据恢复完整度更高。

如果你没有数据持久化的需求,也完全可以关闭RDB和AOF方式,这样的话,redis将变成一个纯内存数据库,就像memcache一样,下面着重谈谈Redis的两种持久化方式。

1. RDB

RDB方式,是将redis某一时刻的数据持久化到磁盘中,是一种快照式的持久化方法。

redis在进行数据持久化的过程中,会先将数据写入到一个临时文件中,待持久化过程都结束了,才会用这个临时文件替换上次持久化好的文件。正是这种特性,让我们可以随时来进行备份,因为快照文件总是完整可用的。

对于RDB方式,redis会单独创建(fork)一个子进程来进行持久化,而主进程是不会进行任何IO操作的,这样就确保了redis极高的性能。

如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效,另外通过RDB对数据进行备份容灾是个不错的选择。

虽然RDB有不少优点,但它的缺点也是不容忽视的。如果你对数据的完整性非常敏感,那么RDB方式就不太适合你,因为即使你每5分钟都持久化一次,当redis故障时,仍然会有近5分钟的数据丢失。所以,redis还提供了另一种持久化方式,那就是AOF。

2. AOF

如果对数据的耐久性需求很高,那么RDB快照持久化的方式可能就显得不那么合适了,此时可以采用AOF,每次有更新数据的操作则会把对应的命令追加到文件的末尾,等下次redis重启时通过执行文件中的命令来实现数据的重建。

AOF通过fsync来进行新命令的追加,默认是间隔1秒。

另外,一般情况下,对于同等规模的数据集,一般AOF文件的体积要比RDB大,并且RDB会具有更好的载入数据性能,实际使用场景中,建议两种持久化方式同时使用。

Redis主从复制策略

redis主从复制就是把redis服务器的数据复制到其他redis节点,前者是主节点,后者是从节点,一般是一主多从的架构且一个从节点只能对应唯一的主节点,另外数据的复制是单向的只能从主节点到从节点,而数据同步的方法是异步的(从redis 2.8开始,采用psync来实现主从数据库的同步)。

1. 主从复制的功能

  • 数据备份;
  • 故障恢复,当主节点出现故障时,可以切换到从节点恢复;
  • 读负载均衡能力加强,一般主节点承担写而从节点负责读,这样多个从节点就分担了读压力;
  • 主从复制是redis sentinel和cluster实现的基础;

2. 缺陷

从上面所述不难看出主从没能解决至少下面三个问题:

  • 无法自动故障切换,只能手动干预或者写监控程序来实现,都比较麻烦;
  • 从节点故障没法恢复;
  • 受单机内存限制无法存储更多的数据,且没实现写的负载均衡能力;

3. 数据复制

分部分复制和全量复制,得看场景。

4. 超时问题

5. 相关配置

从节点配置文件redis.conf加上slaveof <redis_master_ip> <redis_master_port>

Redis Sentinel

在复制的基础上,哨兵实现了自动化的故障恢复。缺陷:写操作无法负载均衡;存储能力受到单机的限制。

1. Sentinel模块构成

  • 监控模块:随时监察主从节点的在线状态;
  • 自动故障转移:当主节点出现故障时,哨兵会自动开始故障转移的操作,选举一个从节点作为新的主节点,并且其它从节点会开始复制新的主节点;
  • 通知:主要是跟客户端交互的,比如把故障转移的结果通知到客户端;
  • 配置提供者:客户端在初始化时,通过连接哨兵来获得当前Redis服务的主节点地址;

哨兵架构的主从节点跟一般的主从节点没有区别,故障发现和自动转移是由哨兵控制和完成的;哨兵本质上也是redis节点,不过不存储数据;每个哨兵节点只需配置对主节点的监控,并可自动发现其他哨兵节点及从节点;在哨兵系统启动和故障转移阶段,每个节点的配置文件都会被重写,另外,一个哨兵节点可以监控多个主节点,在sentinel.conf配置多条sentinel monitor语句即可。

2. 实现原理

  • 定时任务:通过向主从节点发送info命令获取最新的主从拓扑结构;通过发布订阅功能发现和获取其他哨兵的信息;通过ping来判断是不是下线;
  • 主观下线:单个哨兵通过心跳检测对服务器做出的下线判断;
  • 客观下线:多个哨兵通过心跳检测对同一个服务器做出的下线判断,并且客观下线只针对主节点;
  • 选举领导者哨兵节点:通过某种算法选择一个哨兵节点来来控制和操作故障转移;
  • 故障转移

3. 相关配置

配置文件:sentinel.conf
sentinel monitor {masterName} {masterIp} {masterPort} {quorum}哨兵系统最核心的配置。

Redis Cluster

redis集群的设计可以说是真正实现了redis高可用,集群中是由多个节点组成,而redis的数据是分布在这些节点上,节点分为主节点和从节点,只有主节点负责数据的读写和集群状态的维护,而从节点只进行主节点数据和状态信息的复制。

redis集群引入了一个新的概念:数据分片,它可以使数据分散到各个主节点,解决了单机内存大小的限制,存储量大大增加;另一方面每一个主节点都可以对外提供读写服务,提高了服务的响应能力。redis集群支持主从复制和自动故障转移的功能,实现高可用。

1. 集群搭建

一般集群的搭建分如下四个步骤:

  • 启动节点:仍然使用redis-server命令,需要使用集群模式启动,cluster-enabled和cluster-config-file这两个配置参数是跟集群相关的,前者是开启集群模式,默认是单机(standalone)模式,后者是集群节点配置文件,无须人为修改, 它由 Redis 集群在启动时创建, 并在有需要时自动进行更新;
  • 节点握手:各个节点之间建立网络让各个节点间可以通讯,相关的命令cluster meet <node_ip> <node_port>
  • 分配主节点槽:集群总共有16383个槽,redis-cli -h <master_ip> -p <master_port> cluster addslots {0..5461}
  • 设置主从关系:redis-cli cluster replicate <master_node_id>

也可以使用官方推荐的指令redis-trib.rb来完成集群的自动搭建。

Redis分布式锁/并发锁

通过redis的SETNX命令来实现,具体就是有且只有当该key不存在时,才能获取锁设置value;为避免其他客户端来竞争锁造成死锁的问题,可以给key设置一个合理的过期时间,当检测到key过期时删除数据来释放锁以供其他客户端使用。

另外,存在setnx操作之后expire操作之前服务器发生宕机的可能,可以把这两个操作打包成一个原子性操作,命令格式:set <key> <value> nx ex <expire_time>

Redis和数据库双写及数据的一致性

如果只是读操作其实不用太关心redis跟mysql数据的一致性问题,但在进行增删改操作时,由于读写的并发及顺序问题,可能会造成缓存和数据库数据的不一致,现罗列网上的其中两种解决方案:

  • 延时双删策略:在写库前后都加上删除缓存的操作,且要在写库后设置一个合理的延时时间,保证这个时间内其他进程的读请求已经结束;
  • 订阅MySQL的bin log(异步更新缓存):一旦发现数据库有数据更新,就将bin log的消息推送给redis达到及时更新缓存的目的;

缓存雪崩、缓存穿透、缓存击穿

这两种情形一般中小型企业不会遇到,但是高并发情形下需要考虑这两种问题。

1. 缓存雪崩

缓存数据某个时间段大面积失效,高并发情形下大量请求直接到数据库,导致数据库连接异常。
解决方案:

  • 加锁排队;
  • 给数据的失效时间加一个随机值,避免数据一时间集体过期失效;
  • 双缓存,一个缓存设置失效时间,另一个不设置;

2. 缓存穿透

特意大量请求缓存不存在的数据,导致所有请求都直接到了数据库,从而数据库连接异常。
解决方案:

  • 采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,对一定不存在的数据进行拦截;
  • 如果返回的结果为空,不管数据是不存在还是因为其他故障,依然对这个空结果进行缓存,并设置过期时间(不要超过5mins)

3. 缓存击穿

指一个key非常热点,大并发集中对这个key进行访问,当这个key在失效的瞬间,仍然持续的大并发访问就穿破缓存,转而直接请求数据库。
解决方案:
在访问key之前,采用SETNX(set if not exists)来设置另一个短期key来锁住当前key的访问,访问结束再删除该短期key。

Redis过期删除和内存淘汰机制

对于过期的数据,redis默认会采用定期删除+惰性删除的策略
定期删除:每隔比如100ms对过期的数据进行随机删除(显然,这样做会导致有些过期数据没被删除);
惰性删除:对于那些过期但是通过定期删除没删除的数据此时就可以通过惰性删除策略来删除了。查询时先判断该数据是否过期,如果过期则删除,但是如果短期内该数据没被查询访问,那么这些数据还是没被删除;

如此可以设置内存淘汰策略(设置maxmemeory-policy)来优化,目前内存淘汰策略有6种:

  • noeviction:当内存不足以容纳新数据时直接报错;
  • allkeys-lru:当内存不足以容纳新数据,在键空间中,移除最近最少使用的key --- 推荐使用
  • allkeys-random:当内存不足以容纳新数据,在键空间中,随机移除一个key;
  • volatile-lru:当内存不足以容纳新数据,在设置了过期时间的键空间中,移除最近最少使用的key,这种情况redis既做缓存又做了持久化才用;
  • volatile-random:当内存不足以容纳新数据,在设置了过期时间的键空间中,随机移除一个key;
  • volatile-ttl:当内存不足以容纳新数据,在设置了过期时间的键空间中,有更早过期时间的key优先移除;

具体采取哪种内存淘汰策略要结合实际的应用场景。

Redis的事务处理

虽然redis提供的事务功能不能算是严格意义上的事务,但在服务器不出问题的情况下,还是可以保证命令打包执行的。

  • MULTI:用来组装一个事务;
  • EXEC:用来执行一个事务;
  • DISCARD:用来取消一个事务;
  • WATCH:用来监视一些key,一旦这些key在事务执行之前被改变,则取消事务的执行。

python下redis缓存的应用场景

一般生产环境中对于使用频率很大的数据经常用Redis作缓存,比如在进行数据读的操作时会先从Redis获取数据,如果获取不到才会从mysql数据库读。

下面是一个简单的缓存实现

def cache(cmd_func, ex=86400):
    def wrapper(func):
        def decorater(*args, **kwargs):
            client = redisclient.get_client('cache')
            key = 'br_args:{}'.format(cmd_func)
            value = client.get(key)
            if data:
                data = pickle.loads(value)
            else:
                data = func(*args, **kwargs)
                value = pickle.dumps(data)
                client.set(key, value, ex)
            return data
        return decorater
    return wrapper

python下获取redis client连接接口可以用redis.StrictRedis()

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容

  • 本文是我自己在秋招复习时的读书笔记,整理的知识点,也是为了防止忘记,尊重劳动成果,转载注明出处哦!如果你也喜欢,那...
    波波波先森阅读 3,418评论 0 40
  • 超强、超详细Redis入门教程 转载2017年03月04日 16:20:02 16916 转载自: http://...
    邵云涛阅读 17,419评论 3 313
  • 文章已经放到github上 ,如果对您有帮助 请给个star[https://github.com/qqxuanl...
    尼尔君阅读 2,279评论 0 22
  • 【本教程目录】 1.redis是什么2.redis的作者3.谁在使用redis4.学会安装redis5.学会启动r...
    徐猿猿阅读 1,863评论 0 35
  • 从这篇文章开始,将依次介绍Redis高可用相关的知识——持久化、复制(及读写分离)、哨兵、以及集群。 本文将先说明...
    不变甄心阅读 690评论 0 4