为什么Redis数据库内存占用过高不宜过高

redis为什么内存不宜过大 - 简书
redis为什么内存不宜过大
redis的高性能、稳定性都是不用怀疑的,但如果redis塞入数据过多,内存过大,那如果出问题,那它可能会给我们的就是灾难性的。
1 主库宕机
主库宕机,常见的策略为“切主”。具体为从该集群剩余从库中选出一个从库奖其升级为主库, 该从库升级为主库后再讲剩余从库挂载至其下成为其从库。
以上是一个完整的容灾过程,而代价最大的过程为从库的重新挂载,而非主库的切换。
2 扩容问题
一个20G的redis扩容一个从库需要将近20分钟,这个紧急时刻20分钟业务能够容忍吗?
为什么扩容还要业务呢?
3 网络不好导致从库从做最终引发雪崩
网络不好的情况下, 从库以为是断开连接,会自行从做主库,会导致从库雪崩。
4 内存越大,触发持久化的操作阻塞主线程的时间越长
Redis是单线程的内存数据库,在redis需要执行耗时的操作时,会fork一个新进程来做,比如bgsave,bgrewriteaof。 Fork新进程时,虽然可共享的数据内容不需要复制,但会复制之前进程空间的内存页表,这个复制是主线程来做的,会阻塞所有的读写操作,并且随着内存使用量越大耗时越长。
enjoy yourself. 博客让生活丰富起来。
pdf下载地址:Java面试宝典 第一章内容介绍 20 第二章JavaSE基础 21 一、Java面向对象 21 1. 面向对象都有哪些特性以及你对这些特性的理解 21 2. 访问权限修饰符public、private、protected, 以及不写(默认)时的区别(201...
转载地址:http://gnucto.blog.51cto.com/509 Redis与Memcached的区别 传统MySQL+ Memcached架构遇到的问题实际MySQL是适合进行海量数据存储的,通过Memcached将热点数据加载到cache...
Java 基础思维导图,让 Java 不再难懂 - 工具资源 - 掘金思维导图的好处 最近看了一些文章的思维导图,发现思维导图真是个强大的工具。了解了思维导图的作用之后,觉得把它运用到java上应该是个不错的想法,这样回顾知识点的时候一目了然,快速知道自己的短板。 思维导图...
分布式缓存技术PK:选择Redis还是Memcached? 经平台同意授权转载 作者:田京昆(腾讯后台研发工程师) 来源:腾云阁(https://www.qcloud.com/community/article/129) Memcached和Redis,作为近些年最常用的缓...
日常的业务系统中经常使用到redis,平时也会研究下redis的设计文档和源码,对redis的使用场景、实现方案、运维要点这些常规知识点都有所了解,但是零零碎碎总感觉不够系统,这里结合源码对自己使用redis过程中的一些经验、疑惑、思考进行归纳和总结。 主要有以下内容: R...
又是一年高考季来了。 昨晚的一场大雨把广州的天气冲刷得干净澄澈,今晨阳光明媚,空气清新,微风轻拂,为高考的孩子们送去好天气好兆头的福利。 今天我有两个好朋友的孩子参加高考。 两个孩子都是女孩,成绩都非常好,都就读于广州高中(省实、六中)排名前五的学校。 前一阵就或微信或电话...
----E31 MELD负荷过大《SD-SLTT》 ----E32 异常低速《SW-TGBL》 ----E33 速度异常过高《SW-TGBH》 ----E34 AST异常动作《SW-ASTW》低速梯使用 ----E35 逆转运行《SW-TGBR》 ----E36 AST异常...
中国古代除了武则天是没有女皇。欧洲历史上的女皇女王很多,但是其实欧洲不是所有国家都能由女子继位。只有个别国家可以,欧洲大陆上的国家很少有女的继位的。就奥地利有一个特雷西亚。特雷西亚的爸爸没有男性继承人,临死前写了遗嘱让女儿出任国王。特雷西亚一上任就引起了奥地利王位继承战争,...
今天是12月11号,白天早晨没课,本来打算七点多起床的,可是昨晚睡迟了,早晨又睡睡醒醒,最后决定起床的时候已经都十二点整了。想想这离四级已经只剩下一星期左右的时间了,而我还是这么的浑浑噩噩,真不敢想四级怎么过,虽然大家都说我回过,可是心里还是没有底。明天开始真的要好好复...
早上醒来发现又起晚了,最近一段时间有点疲惫不堪,早晨老是起不来,睡的特别香,今天还是被孕妇叫了起来,惭愧啊。从睁开眼就打开手机,一边听着武校的金言金语说家教,一边忙着收拾房间。一直听到忙完所有的事情后,才依依不舍的关上手机下楼去店里看店(我经营着一家洗化用品店)。不...redis这个内存数据库,它的高性能、稳定性都是不用怀疑的,但我们塞进redis的数据过多,内存过大,那如果出问题,那它可能会带给我们的就是灾难性。这几年的线上业务表明,redis这个内存数据库,它的高性能、稳定性都是不用怀疑的,但我们塞进redis的数据过多,内存过大,那如果出问题,那它可能会带给我们的就是灾难性(我想很多公司都遇到过)
这里列举一下,我们遇到的一些问题:
1、主库宕机
先来看一下主库宕机容灾过程:如下图
在主库宕机的时候,我们最常见的容灾策略为“切主”。具体为从该集群剩余从库中选出一个从库并将其升级为主库,该从库升级为主库后再将剩余从库挂载至其下成为其从库,最终恢复整个主从集群结构。以上是一个完整的容灾过程,在此切换的过程中,代价最大的是从库的重新挂载,而非从库到主库的切换。这是因为redis无法像mysql、mongodb那样基于同步的点位在主库发生变化后从新的主库继续同步数据。
在redis集群中一旦从库换主,redis的做法是将更换主库的从库清空然后从新主库完整同步一份数据再进行续传。整个从库重做流程是这样的:
1. 主库bgsave自身数据到磁盘
2. 主库发送rdb文件到从库
3. 从库开始加载
4. 加载完毕开始续传,同时开始提供服务
很明显,在这个过程中redis的内存体积越大,以上每一个步骤的时间都会被拉长,实际测试的数据如下(我们自认我们的机器性能比较好):
可以看到,当数据达到20G的时候,一个从库的恢复时间已经被拉长到了将近20分钟,如果有10个从库那么如果依次恢复则共需200分钟,而如果此时该从库承担着大量的读取请求你能够忍受这么长的恢复时间吗?看到这里你肯定会问:为什么不能同时重做所有从库?这是因为所有从库如果同时向主库请求rdb文件那么主库的网卡则立即跑满从而进入一个无法正常提供服务的状态,此时主库又死了,简直是雪上加霜。
当然,我们可以批量恢复从库,例如两两一组,那么全部从库的恢复时间也仅仅从200分钟降低到了100分钟,这不是五十步笑百步吗?
另一个重要问题在于第四点中的标红位置,续传可以理解为一个简化的mongodb的oplog,它是一个体积固定的内存空间,我们称之为“同步缓冲区”。
redis主库的写入操作都会在该区域存放一份然后发送给从库,而如果在上文中1,2,3步耗时太久那么很可能这个同步缓冲区就被重写,此时从库无法找到对应的续传位置它会怎么办?答案是重做1,2,3步!
但因为我们无法解决1,2,3步的耗时因此该从库会永远的进入恶性循环:不停的向主库请求完整数据,结果对主库的网卡造成严重影响。
很多时候会出现流量的突发性增长,通常在找到原因之前我们的应急做法就是扩容了。而根据场景一中的表格,一个20G的redis扩容一个从库需要将近20分钟,在这个紧急的时刻20分钟业务能够容忍吗?可能还没扩好就死翘翘了。
网络不好导致从库重做最终引发雪崩
该场景的最大问题是主库与从库的同步中断,而此时很可能从库仍然在接受写入请求,那么一旦中断时间过长同步缓冲区就很可能被复写。此时从库上一次的同步位置已丢失,在网络恢复后虽然主库没有发生变化但由于从库的同步位置丢失了从库必须进行重做,也就是问题一中的1,2,3,4步。如果此时主库内存体积过大那么从库重做速度就会很慢,而发送到从库的读请求就会受到严重影响,同时由于传输的rdb文件的体积过大,主库的网卡在相当长的一段时间内都会受到严重影响。
内存越大,触发持久化的操作阻塞主线程的时间越长
Redis是单线程的内存数据库,在redis需要执行耗时的操作时,会fork一个新进程来做,比如bgsave,bgrewriteaof。
Fork新进程时,虽然可共享的数据内容不需要复制,但会复制之前进程空间的内存页表,这个复制是主线程来做的,会阻塞所有的读写操作,并且随着内存使用量越大耗时越长。例如:内存20G的redis,bgsave复制内存页表耗时约为750ms,redis主线程也会因为它阻塞750ms。
解决办法当然就是极力减少内存的使用了,一般情况下,我们都是这么做的:
设置过期时间
对具有时效性的key设置过期时间,通过redis自身的过期key清理策略来降低过期key对于内存的占用,同时也能够减少业务的麻烦,不需要定期清理了
不存放垃圾到redis中
这简直就是废话,但是,有跟我们同病相怜的人么?
及时清理无用数据
例如一个redis承载了3个业务的数据,一段时间后有2个业务下线了,那你就把这两个业务的相关数据清理了呗
尽量对数据进行压缩
例如一些长文本形式的数据,压缩能够大幅度降低内存占用
关注内存增长并定位大容量key
不管是DBA还是开发人员,你用redis,你就必须关注内存,否则,你其实就是不称职的,这里可以分析redis实例中哪些key比较大从而帮助业务快速定位异常key(非预期增长的key,往往是问题之源)
如果实在不想搞的那么累,那就把业务迁移到新开源的pika上面,这样就不用太关注内存了,redis内存太大引发的问题,那也都不是问题了。
最后祈祷线上5000个redis实例都不要异常~~~分割超大Redis数据库例子
转载 & & 投稿:junjie
这篇文章主要介绍了分割超大Redis数据库例子,本文讲解了分割的需求、分割的思路及分割实例,需要的朋友可以参考下
薄荷 App 上的伙伴功能大量使用了内存数据库 Redis,随着数据量的快速增长,Redis 膨胀得很快,已经接近 12 GB规模,这些数据全部放在单个 Redis 实例中。单个巨大 Redis 实例有如下几个坏处:
1.首先,需要一台内存很大的机器。Redis 是内存数据库,它需要把所有需求全部放在内存中,需要为之装下 12 GB的 Redis 实例,至少需要 12 GB 内存大小的机器,考虑的预留增长空间,一般需要 12 * 1.5 约 18 GB 内存。 另外还有一个考虑的因素是,Redis 进行硬盘数据存储时,fork 进程需要消耗同样大小的内存,因此一个 12GB 的 redis 实例需要 32 GB左右的内存比较合适,这对机器提出了很高的要求,常常难以满足。
2.然后,Redis 容易成为性能瓶颈。Redis 的并发模型是单进程单线程,它不能充分利用多核 CPU,在请求数很高,或者某一些请求处理比较慢时(比如一些大的数据排序),可能会成为系统的性能瓶颈。有方法可以缓解甚至这个问题,就是建立多个 Redis 实例,通过多个 Redis 连接来实现。
3.另外,单个巨大的 Redis 实例也会增加数据管理难度,因为这么大的数据量,无论是复制,备份操作都比较慢,容易对线上系统造成冲击。
因此,十分有必要把单个巨大的 Redis 实例分割成多个小的 Redis 实例。
使用 Redis 的复制机制,可以在线平滑处理 Redis 实例分割,几乎不会对系统有很大的影响。
分割的具体操作思路如下:
1.首先,规划 Redis 分割策略,通常是基于业务划分,比如薄荷伙伴是基于业务分成 timeline, user_relationship, other 3个 Redis 实例。规划好之后,需要根据规划结果对应用程序中 Redis 程序代码做修改,通常是有一个统一的 Redis 链接修改为多个 Redis 连接,不同业务使用不同的连接。
2.然后,通过 Redis 复制功能建立多个 Redis 副本,让不同 Redis 连接使用不同的 Redis 副本,在 Redis 副本中删除多余的数据。批量删除某个模式的 keys,可以使用下面的 shell 命令:
redis-cli KEYS "&pattern&" | xargs redis-cli DEL
改成实际的模式,如
redis-cli KEYS "user:*:followers" | xargs redis-cli DEL
表示删除 user followers 数据。
最后通过来回切换并重启 Redis 实例达到完全分割 Redis 实例。
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具博主最新文章
博主热门文章
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)支持持久化的内存数据库-----Redis - losbyday - 博客园
一名热爱开源、朴实的搬运工
一、Redis概述
1.1、什么是Redis
Redis是一种高级key-value数据库。它跟memcached类似,不过数据 可以持久化,而且支持的数据类型很丰富。有字符串,链表,集 合和有序集合。支持在服务器端计算集合的并,交和补集(difference)等,还支持多种排序功能。所以Redis也可以被看成是一个数据结构服务 器。Redis的所有数据都是保存在内存中,然后不定期的通过异步方式保存到磁盘上(这称为&半持久化模式&);也可以把每一次数据变化都写入到一个append only file(aof)里面(这称为&全持久化模式&)。
1.2、Redis数据持久化(俗称&数据落地&)
redis是一个支持持久化的内存数据库,也就是说redis需要经常将内存中的数据同步到磁盘来保证持久化。redis支持四种持久化方式,一是 Snapshotting(快照)也是默认方式;二是Append-only file(缩写aof)的方式;三是虚拟内存方式;四是diskstore方式。下面分别介绍之。(一)Snapshotting
快照是默认的持久化方式。这种方式是就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为dump.rdb。可以通过配置设置自动做快照持久化的方式。我们可以配置redis在n秒内如果超过m个key被修改就自动做快照,下面是默认的快照保存配置:
save&900&1&&#900秒内如果超过1个key被修改,则发起快照保存&&&save&300&10&#300秒内容如超过10个key被修改,则发起快照保存&&&save&60&10000
快照保存过程:
1. redis调用fork函数,有了子进程和父进程。fork()
创建一个新进程,并为它创建新的地址空间
2. 父进程继续处理client请求,子进程负责将内存内容写入到临时文件。由于os的写时复制机制(copy on write)父子进程会共享相同的物理页面,当父进程处理写请求时os会为父进程要修改的页面创建副本,而不是写共享的页面。所以子进程的地址空间内的数据是fork时刻整个数据库的一个快照。
3. 当子进程将快照写入临时文件完毕后,用临时文件替换原来的快照文件,然后子进程退出(fork一个进程入内在也被复制了,即内存会是原来的两倍)。
client 也可以使用save或者bgsave命令通知redis做一次快照持久化。save操作是在主线程中保存快照的,由于redis是用一个主线程来处理所有 client的请求,这种方式会阻塞所有client请求。所以不推荐使用。另一点需要注意的是,每次快照持久化都是将内存数据完整写入到磁盘一次,并不是增量的只同步脏数据。如果数据量大或者频繁修改的话,而且写操作比较多,必然会引起大量的磁盘io操作,可能会严重影响性能。
另外由于快照方式是在一定间隔时间做一次的,所以如果redis意外down掉的话,就会丢失最后一次快照后的所有修改。如果应用要求不能丢失任何修改的话,可以采用aof持久化方式。下面介绍:(二)Append-only fileaof 比快照方式有更好的持久化性,是由于在使用aof持久化方式时,redis会将每一个收到的写命令都通过write函数追加到文件中(默认是appendonly.aof)。当redis重启时会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。当然由于os会在内核中缓存 write做的修改,所以可能不是立即写到磁盘上。这样aof方式的持久化也还是有可能会丢失部分修改。不过我们可以通过配置文件告诉redis我们想要通过fsync函数强制os写入到磁盘的时机。有三种方式如下(默认是:每秒fsync一次):
appendonly&&&yes&&&&&&&&&&&#启用aof持久化方式&&&#&appendfsync&always&&&#每次收到写命令就立即强制写入磁盘,最慢的,但是保证完全的持久化,不推荐使用&&&appendfsync&&&everysec&&&&&#每秒钟强制写入磁盘一次,在性能和持久化方面做了很好的折中,推荐&&&#&appendfsync&no&&&&#完全依赖os,性能最好,持久化没保证
aof 的方式也同时带来了另一个问题。持久化文件会变的越来越大。例如我们调用incr test命令100次,文件中必须保存全部的100条命令,其实有99条都是多余的。因为要恢复数据库的状态其实文件中保存一条set test 100就够了。为了压缩aof的持久化文件。redis提供了bgrewriteaof命令。收到此命令redis将使用与快照类似的方式将内存中的数据以命令的方式保存到临时文件中,最后替换原来的文件。具体过程如下:
redis调用fork ,现在有父子两个进程
2. 子进程根据内存中的数据库快照,往临时文件中写入重建数据库状态的命令
3. 父进程继续处理client请求,除了把写命令写入到原来的aof文件中。同时把收到的写命令缓存起来。这样就能保证如果子进程重写失败的话并不会出问题。
4. 当子进程把快照内容写入已命令方式写到临时文件中后,子进程发信号通知父进程。然后父进程把缓存的写命令也写入到临时文件。
5. 现在父进程可以使用临时文件替换老的aof文件,并重命名,后面收到的写命令也开始往新的aof文件中追加。
需要注意到是重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似。 (三)虚拟内存方式(desprecated)首先说明:在Redis-2.4后虚拟内存功能已经被deprecated了,原因如下:1)slow restart重启太慢2)slow saving保存数据太慢3)slow replication上面两条导致 replication 太慢4)complex code代码过于复杂下面还是介绍一下redis的虚拟内存。redis的虚拟内存与os的虚拟内存不是一码事,但是思路和目的都是相同的。就是暂时把不经常访问的数据从内存交换到磁盘中,从而腾出宝贵的内存空间用于其他需要访问的数据。尤其是对于redis这样的内存数据库,内存总是不够用的。除了可以将数据分割到多个redis server外。另外的能够提高数据库容量的办法就是使用vm把那些不经常访问的数据交换的磁盘上。如果我们的存储的数据总是有少部分数据被经常访问,大部分数据很少被访问,对于网站来说确实总是只有少量用户经常活跃。当少量数据被经常访问时,使用vm不但能提高单台redis server数据库的容量,而且也不会对性能造成太多影响。
redis没有使用os提供的虚拟内存机制而是自己在用户态实现了自己的虚拟内存机制,作者在自己的blog专门解释了其中原因。 主要的理由有两点:
1. os 的虚拟内存是已4k页面为最小单位进行交换的。而redis的大多数对象都远小于4k,所以一个os页面上可能有多个redis对象。另外redis的集合对象类型如list,set可能存在与多个os页面上。最终可能造成只有10%key被经常访问,但是所有os页面都会被os认为是活跃的,这样只有内存真正耗尽时os才会交换页面。
2.相比于os的交换方式。redis可以将被交换到磁盘的对象进行压缩,保存到磁盘的对象可以去除指针和对象元数据信息。一般压缩后的对象会比内存中的对象小10倍。这样redis的vm会比os vm能少做很多io操作。
下面是vm相关配置:
slaveof&192.168.1.1&6379&&#指定master的ip和端口&&vm-enabled&&&yes&&&&&&&&&&#开启vm功能&&&vm-swap-file&/tmp/redis.swap&&&#交换出来的value保存的文件路径/tmp/redis.swap&&&vm-max-memory&1000000&&#redis使用的最大内存上限,超过上限后redis开始交换value到磁盘文件中&&&vm-page-size&32&&&&&&&&#每个页面的大小32个字节&&&vm-pages&&&&&&#最多使用在文件中使用多少页面,交换文件的大小&=&vm-page-size&*&vm-pages&&&vm-max-threads&4&&&&&&&#用于执行value对象换入换出的工作线程数量,0表示不使用工作线程(后面介绍)
redis的vm在设计上为了保证key的查找速度,只会将value交换到swap文件中。所以如果是内存问题是由于太多value很小的key造成的,那么vm并不能解决。和os一样redis也是按页面来交换对象的。redis规定同一个页面只能保存一个对象。但是一个对象可以保存在多个页面中。在redis使用的内存没超过vm-max-memory之前是不会交换任何value的。当超过最大内存限制后,redis会选择较老的对象。如果两个对象一样老会优先交换比较大的对象,精确的公式swappability = age*log(size_in_memory)。对于vm-page-size的设置应该根据自己的应用将页面的大小设置为可以容纳大多数对象的大小。太大了会浪费磁盘空间,太小了会造成交换文件出现碎片。对于交换文件中的每个页面,redis会在内存中对应一个1bit值来记录页面的空闲状态。所以像上面配置中页面数量(vm-pages
)会占用16M内存用来记录页面空闲状态。vm-max-threads表示用做交换任务的线程数量。如果大于0推荐设为服务器的cpu core的数量。如果是0则交换过程在主线程进行。
参数配置讨论完后,在来简单介绍下vm是如何工作的: 当vm-max-threads设为0时(Blocking VM)换出:
主线程定期检查发现内存超出最大上限后,会直接已阻塞的方式,将选中的对象保存到swap文件中,并释放对象占用的内存,此过程会一直重复直到下面条件满足
1.内存使用降到最大限制以下
2.swap文件满了
3.几乎全部的对象都被交换到磁盘了 换入:
当有client请求value被换出的key时。主线程会以阻塞的方式从文件中加载对应的value对象,加载时此时会阻塞所有client。然后处理client的请求
当vm-max-threads大于0(Threaded VM) 换出:
当主线程检测到使用内存超过最大上限,会将选中的要交换的对象信息放到一个队列中交由工作线程后台处理,主线程会继续处理client请求。 换入:
如果有client请求的key被换出了,主线程先阻塞发出命令的client,然后将加载对象的信息放到一个队列中,让工作线程去加载。加载完毕后工作线程通知主线程。主线程再执行client的命令。这种方式只阻塞请求value被换出key的client
总的来说blocking vm的方式总的性能会好一些,因为不需要线程同步,创建线程和恢复被阻塞的client等开销。但是也相应的牺牲了响应性。threaded vm的方式主线程不会阻塞在磁盘io上,所以响应性更好。如果我们的应用不太经常发生换入换出,而且也不太在意有点延迟的话则推荐使用blocking vm的方式。关于redis vm的更详细介绍可以参考下面链接:
(四)diskstore方式diskstore方式是作者放弃了虚拟内存方式后选择的一种新的实现方式,也就是传统的B-tree的方式。具体细节是:1) 读操作,使用read through以及LRU方式。内存中不存在的数据从磁盘拉取并放入内存,内存中放不下的数据采用LRU淘汰。2) 写操作,采用另外spawn一个线程单独处理,写线程通常是异步的,当然也可以 把cache-flush-delay配置设成0,Redis尽量保证即时写入。但是在很多场合延迟写会有更好的性能,比如一些计数器用Redis存储, 在短时间如果某个计数反复被修改,Redis只需要将最终的结果写入磁盘。这种做法作者叫per key persistence。由于写入会按key合并,因此和snapshot还是有差异,disk store并不能保证时间一致性。由于写操作是单线程,即使cache-flush-delay设成0,多个client同时写则需要排队等待,如果队列容量超过cache-max-memory Redis设计会进入等待状态,造成调用方卡住。Google Group上有热心网友迅速完成了压力测试,当内存用完之后,set每秒处理速度从25k下降到10k再到后来几乎卡住。 虽然通过增加cache-flush-delay可以提高相同key重复写入性能;通过增加cache-max-memory可以应对临时峰值写入。但是diskstore写入瓶颈最终还是在IO。3) rdb 和新 diskstore 格式关系 rdb是传统Redis内存方式的存储格式,diskstore是另外一种格式,那两者关系如何?&通过BGSAVE可以随时将diskstore格式另存为rdb格式,而且rdb格式还用于Redis复制以及不同存储方式之间的中间格式。& 通过工具可以将rdb格式转换成diskstore格式。当然,diskstore原 理很美好,但是目前还处于alpha版本,也只是一个简单demo,diskstore.c加上注释只有300行,实现的方法就是将每个value作为一 个独立文件保存,文件名是key的hash值。因此diskstore需要将来有一个更高效稳定的实现才能用于生产环境。但由于有清晰的接口设 计,diskstore.c也很容易换成一种B-Tree的实现。很多开发者也在积极探讨使用bdb或者innodb来替换默认diskstore.c的 可行性。 下面介绍一下Diskstore的算法。其实DiskStore类 似于Hash算法,首先通过SHA1算法把Key转化成一个40个字符的Hash值,然后把Hash值的前两位作为一级目录,然后把Hash值的三四位作 为二级目录,最后把Hash值作为文件名,类似于&/0b/ee/0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33& 形式。算法如下:dsKeyToPath(key):char path[1024];char *hashKey = sha1(key);path[0] = hashKey[0];path[1] = hashKey[1];path[2] = '/';path[3] = hashKey[2];path[4] = hashKey[3];path[5] = '/';memcpy(path + 6, hashKey, 40); 存储算法(如key == apple):dsSet(key, value, expireTime):
// d0be2dc421be4fcd0172e5afceead940char *hashKey = sha1(key); // d0/be/d0be2dc421be4fcd0172e5afceead940char *path = dsKeyToPath(hashKey);FILE *fp = fopen(path, "w");rdbSaveKeyValuePair(fp, key, value, expireTime);fclose(fp) 获取算法:dsGet(key):char *hashKey = sha1(key);char *path = dsKeyToPath(hashKey);FILE *fp = fopen(path, "r");robj *val = rdbLoadObject(fp);
二、Redis安装
2.1、redis安装
# cd /data/soft/# tar xf redis-2.4.15.tar.gz -C tmp/# cd tmp/redis-2.4.15/#make PREFIX=/usr/local/services/redis-2.4.15 &/dev/null#make PREFIX=/usr/local/services/redis-2.4.15 install &/dev/null#ln -s /usr/local/services/redis-2.4.15/bin/ /usr/local/services/redis/会在当前目录下生成本个可执行文件,分别是redis-server、redis-cli、redis-benchmark、redis-stat,它们的作用如下:&
redis-server:Redis服务器的daemon启动程序&
redis-cli:Redis命令行操作工具。当然,你也可以用telnet根据其纯文本协议来操作&
redis-benchmark:Redis性能测试工具,测试Redis在你的系统及你的配置下的读写性能&
redis-stat:Redis状态检测工具,可以检测Redis当前状态参数及延迟状况
2.2、调整overcommit_memory参数
如果内存情况比较紧张的话,需要设定内核参数overcommit_memory,指定内核针对内存分配的策略,其值可以是0、1、2。 0,表示内核将检查是否有足够的可用内存供应用进程使用;如果有足够的可用内存,内存申请允许;否则,内存申请失败,并把错误返回给应用进程。 1,表示内核允许分配所有的物理内存,而不管当前的内存状态如何。 2,表示内核允许分配超过所有物理内存和交换空间总和的内存 Redis在dump数据的时候,会fork出一个子进程,理论上child进程所占用的内存和parent是一样的,比如parent占用的内存为 8G,这个时候也要同样分配8G的内存给child, 如果内存无法负担,往往会造成redis服务器的down机或者IO负载过高,效率下降。所以这里比较优化的内存分配策略应该设置为 1(表示内核允许分配所有的物理内存,而不管当前的内存状态如何)。 设置方式有两种,需确定当前用户的权限活使用root用户修改: 1:重设文件 # echo 1 & /proc/sys/vm/overcommit_memory(默认为0) 2: # echo "vm.overcommit_memory=1" && /etc/sysctl.conf # /sbin/sysctl -p
2.3、拷贝配置文件
#mkdir /usr/local/services/redis-2.4.15/etc# cd /soft/redis/redis-2.4.15# cp redis.conf /usr/local/services/redis-2.4.15/etc/
2.4、redis配置文件
# mkdir &p /data/redis/redis_db#mkdir &p /data/redis/redis_dumpdaemonize yespidfile /data/redis/redis_db/redis.pidport 6379timeout 300loglevel debuglogfile stdoutdatabases 16save 900 1save 300 10save 60 10000rdbcompression yesdbfilename dump.rdbdir /data/redis/redis_dumpslave-serve-stale-data yesappendonly noappendfsync everysecno-appendfsync-on-rewrite noauto-aof-rewrite-percentage 100auto-aof-rewrite-min-size 64mbslowlog-log-slower-than 10000slowlog-max-len 128vm-enabled novm-swap-file /data/redis/redis_db/redis.swapvm-max-memory 0vm-page-size 32vm-pages vm-max-threads 4hash-max-zipmap-entries 512hash-max-zipmap-value 64list-max-ziplist-entries 512list-max-ziplist-value 64set-max-intset-entries 512zset-max-ziplist-entries 128zset-max-ziplist-value 64activerehashing yesbind 127.0.0.1
配置文件说明daemonize yes # Redis默认不是以守护进程的方式运行,可以通过该配置项修改,使用yes启用守护进程pidfile /data/redis/redis_db/redis.pid #当Redis以守护进程方式运行时,Redis默认会把pid写入/var/run/redis.pid文件,可以通过pidfile指定 port 6379 #指定Redis监听端口,默认端口为6379,作者在自己的一篇博文中解释了为什么选用6379作为默认端口,因为6379在手机按键上MERZ对应的号码,而MERZ取自意大利歌女Alessia Merz的名字 timeout 300 &#当客户端闲置多长时间后关闭连接,如果指定为0,表示关闭该功能 loglevel debug #指定日志记录级别,Redis总共支持四个级别:debug、verbose、notice、warning,默认为verbose logfile stdout& #日志记录方式,默认为标准输出,如果配置Redis为守护进程方式运行,而这里又配置为日志记录方式为标准输出,则日志将会发送给/dev/null databases 16 #设置数据库的数量,默认数据库为0,可以使用SELECT &dbid&命令在连接上指定数据库id #指定在多长时间内,有多少次更新操作,就将数据同步到数据文件,可以多个条件配合Redis默认配置文件中提供了三个条件save 900 1 save 300 10save 60 10000 #指定存储至本地数据库时是否压缩数据,默认为yes,Redis采用LZF压缩,如果为了节省CPU时间,可以关闭该选项,但会导致数据库文件变的巨大rdbcompression yes #指定本地数据库文件名,默认值为dump.rdbdbfilename dump.rdb#指定本地数据库存放目录dir /data/redis/redis_dump 设置当本机为slav服务时,设置master服务的IP地址及端口,在Redis启动时,它会自动从master进行数据同步slave-serve-stale-data yes . 指定是否在每次更新操作后进行日志记录,Redis在默认情况下是异步的把数据写入磁盘,如果不开启,可能会在断电时导致一段时间内的数据丢失。因为 redis本身同步数据文件是按上面save条件来同步的,所以有的数据会在一段时间内只存在于内存中。默认为noappendonly no 指定更新日志条件,共有3个可选值:
no:表示等操作系统进行数据缓存同步到磁盘(快)
always:表示每次更新操作后手动调用fsync()将数据写到磁盘(慢,安全) everysec:表示每秒同步一次(折衷,默认值)appendfsync everysec 当AOF文件增长到一定大小的时候Redis能够调用 BGREWRITEAOF 对日志文件进行重写no-appendfsync-on-rewrite noauto-aof-rewrite-percentage 100auto-aof-rewrite-min-size 64mb 注意制定一个负数将关闭慢日志,而设置为0将强制每个命令都会记录slowlog-log-slower-than 10000slowlog-max-len 128 指定是否启用虚拟内存机制,默认值为no,简单的介绍一下,VM机制将数据分页存放,由Redis将访问量较少的页即冷数据swap到磁盘上,访问多的页面由磁盘自动换出到内存中(在后面的文章我会仔细分析Redis的VM机制)vm-enabled no 虚拟内存文件路径,默认值为/tmp/redis.swap,不可多个Redis实例共享vm-swap-file /data/redis/redis_db/redis.swap #交换文件 将所有大于vm-max-memory的数据存入虚拟内存,无论vm-max-memory设置多小,所有索引数据都是内存存储的(Redis的索引数据 就是keys),也就是说,当vm-max-memory设置为0的时候,其实是所有value都存在于磁盘。默认值为0vm-max-memory 0 Redis swap文件分成了很多的page,一个对象可以保存在多个page上面,但一个page上不能被多个对象共享,vm-page-size是要根据存储的 数据大小来设定的,作者建议如果存储很多小对象,page大小最好设置为32或者64bytes;如果存储很大大对象,则可以使用更大的page,如果不 确定,就使用默认值vm-page-size 32 设置swap文件中的page数量,由于页表(一种表示页面空闲或使用的bitmap)是在放在内存中的,,在磁盘上每8个pages将消耗1byte的内存vm-pages
设置访问swap文件的线程数,最好不要超过机器的核数,如果设置为0,那么所有对swap文件的操作都是串行的,可能会造成比较长时间的延迟。默认值为4vm-max-threads 4 指定在超过一定的数量或者最大的元素超过某一临界值时,采用一种特殊的哈希算法hash-max-zipmap-entries 512hash-max-zipmap-value 64 list数据类型节点值大小小于多少字节会采用紧凑存储格式。list-max-ziplist-entries 512list-max-ziplist-value 64 set数据类型内部数据如果全部是数值型,且包含多少节点以下会采用紧凑格式存储。set-max-intset-entries 512 zsort数据类型节点值大小小于多少字节会采用紧凑存储格式。zset-max-ziplist-entries 128zset-max-ziplist-value 64 指定是否激活重置哈希,默认为开启activerehashing yes 绑定的主机地址bind 127.0.0.1
2.5、启动Redis服务
# redis-server conf/redis.conf# redis-cli shutdown &停止Redis &关闭服务 # redis-cli -p 6380 shutdown &如果非默认端口,可指定端口:
2.6、测试Redis
# ls /data/redis/redis_dump/& 看看是否有文件。没有?正常。我们写入数据进去# telnet localhost& 6379&&& Trying ::1...telnet: connect to address ::1: Connection refusedTrying 127.0.0.1...Connected to localhost.Escape character is '^]'.get mykey$-1set foo 3+OKget foo$13quit+OKConnection closed by foreign host. # ls /data/redis/redis_dump/& 在此尝试看看。。

我要回帖

更多关于 内存占用过高 的文章

 

随机推荐