IX redis(1)

 

自关系型DB诞生40年来,从理论产生到现实产品(MySQLOracle),已在DB领域上升到了霸主地位,每年数百亿$的庞大产业市场;

随着web2.0的兴起,对于规模日益庞大的海量数据,传统的关系型DB显得力不从心,如超大规模和高并发的微博、微信、SNSsocail network sitesocail network services)纯动态网站等,关系型DB面临很多难以克服的问题,有IO瓶颈、性能瓶颈都难以有效突破,于是出现了很多针对特定场景,以高性能和使用便利为目的的差异化的DB产品——NOSQLnot only SQL,非关系型类的DB),如:专注于key-value查询的redismemcachedttserver,面向文档的mongoDB,面向列的hbase、cassandra,面向图的neo4j,这些nosql的共同特点是:去除一切和高性能无关的功能,追求高并发、高性能,在扩展上支持集群和分布式;

NOSQLnot only sql,而不是no sql,并没完全否定关系型DB,而是作为传统关系型DB的一个补充,在特定的场景下可发挥出难以想象的高效率和高性能;

 

facebook360使用cassandra来存储海量社交数据;twitter在其url抓取系统里综合运用了assandramemcachedgoogle使用了bigtableamazon使用dynamosina微博使用redismemcached来提高高并发性能;taobao使用hbase并在其基础上研发了自己的oceanbasedouban也有自己的nosql产品beansDB

mongoDB被广泛用于存储非结构化数据,中小企业对于常规内容的存储也有用mongoDB;在电信运营商的分析项目中,使用hbase承载从switch上采集高速数据流;

 

熟知nosql的原理和每种产品的特性和使用场景,用以进行技术选型,实施、管理集群,是系统管理者、DBA、架构师要掌握的知识;

 

使用nosql

high performance,对DB高并发rw的需求(web2.0网站要根据用户个性化信息来实时生成动态页面和提供动态信息,所以难以使用动态页面静态化技术,因此对DB的并发和负载要求非常高,往往要达到每秒上万次rw,关系型DB包括分布式集群应付上万次查询(r)还勉强顶得住,但若应付上万次SQLw操作,物理硬盘IO已无法承受,对于普通大型BBS网站,存在高并发w的需求);

huge storage,海量数据的高效率存储和访问需求(对于大型SNS,每天用户产生海量的动态数据,如friendfeed一个月有2.5亿条用户动态,对于关系型DB若要在一张2.5亿条记录的表中进行SQL查询,效率极其低下;大型web网站的用户登录系统,如tencentshengda等数以亿计的账号,关系型DB难以应付);

high scalability & high availability,高扩展性和高可用需求(在互联网网站架构中,DB是最难横向扩展的,在应用系统的用户量和访问量与日俱增时,DB很难像web serverapp server那样简单的通过添加硬件node来扩展性能和负载能力,对很多需提供24h不间断业务的网站来说,对DB进行升级和扩展非常痛苦,往往要停机维护和数据迁移);

 

nosql将关系型DB的以下特性去除:

关系型DB数据事务一致性需求(传统关系型DB要保持DB事务一致性需求,从而无法满足高并发rw的需求);

DBr实时性和w实时性需求(对关系型DB插入一条数据之后立即查询,可查出来,但对很多web应用来说,并不要求这么高的实时性);

对复杂的SQL查询,特别是多表关联查询需求(任何大数据量的web系统,尤其是SNS,非常忌讳多个大表的关联查询,和复杂的数据分析类型SQL查询,从需求和产品设计角度,避免这种情况发生,往往更多的是单表的主键查询,及单表的简单条件分页查询,SQL的功能被极大的弱化);

 

nosql适用情况(数据模型比较简单;需要灵活更强的IT系统;对DB性能要求较高;不需要高度的数据一致性;对于给定的key,较容易映射复杂value的环境);

 

 

nosql主流软件分类及特点:

1key-value存储,key-value systems basically support getputand delete operations based on a primary key,类似传统语言中的hash表,可通过key来添加、查询、删除数据,因为使用主键访问,所以会有不错的性能及扩展性;key-value DB主要是使用一个hash表,此表中有一个特定的key和一个指针指向特定的数据,key-value模型对于IT系统的优势在于简单、易部署;

产品有:redismemcachedBDBberkeley DB)memcacheDBttserver(tokyo cabinet/tokyo tyrant)riakamazon'sdynamoproject voldemort

典型应用:内容缓存、适合混合工作负载并扩展大的数据集;

数据模型:一系统key:value对,仅能通过key查询value

优势:快速查询;劣势:存储的数据缺少结构化;

适用场景:存储用户信息,如会话、配置文件、参数、购物车、计数(统计粉丝关注)等,这些信息一般和IDkey)挂钩;

不适用场景:不能通过value查询key;不能存储数据之间的关系(不能通过两个或更多的key来关联数据);不支持事务,有故障时不能回滚;

企业应用:twitterredis&memcached),stackoverflowredis),instagramredis),youtubememcached),wikipediamemcached),sinaredis),baidumemcached),baikettserver

2column-oriented列存储,column-oriented systems still use tables but have no joins(joins must be handled within your application).obviously,they store data by column as a opposed to traditional row-oriented database.this makes aggregations much easier.column-oriented存储DB将数据存储在列族column family中,一个column family存储经常被一起查询的相关数据;例如一个person类,通常会一起查询nameage而不是salarynameage会在一个comumn family中,而salary在另一个column family中;通常用来应付分布式存储的海量数据,key仍存在,只不过是指向了多个column,这些columncolumn family来安排;

产品有:cassandrahbaseriak

典型应用:分布式FS

数据模型:以column family式存储,将同一列数据存到一起;

优势:查找速度快,可扩展性强,尤其在分布式扩展方面;劣势:功能相对局限;

适用场景:日志(每个application可将信息写入自己的column family中);blog平台(存储每个信息到不同的column family中,如标签、类别、文章等);

不适用场景:不支持事务;不支持原型设计,一旦查询方式改变,必须重新设计column family

企业应用:githubriak),bestbuyriak),ebaycassandra),instagramcassandra),nasacassandra),twittercassandra&hbase),facebookhbase),yahoohbase),taobaohbase),360cassandra

3document-oriented存储,document-orientedsystems store structured "documents" such as JSON or XML but have nojoins(joins must be handled within your application).it's very easy to map datafrom object-oriented software to these systems.,文档DB来自lotus notes办公软件,与key-value存储类似,可看作是key-valueDB的升级版,允许嵌套key-valuedocument-oriented DBkey-value DB查询效率要高,document-oriented的数据模型是版本化的文档、半结构化的文档以特定的格式存储,如jsondocument-oriented DB将数据以文档形式存储,每个文档都是自包含的数据单元,是一系列数据项的集合,每个数据项都有一个名称与对应的值,值可以是简单的数据类型(stringnumberdate等),也可以是复杂的数据类型(sequence tablerelated object),数据存储的最小单位是文档,同一个表中存储的文档属性可以是不同的,数据可用xmljsonbson等多种形式存储;

产品有:mongoDBravenDBcouchDB

典型应用:web应用;

数据模型:一系列key:value对;

优势:数据结构要求不严格;劣势:查询性能不高,缺乏统一的查询语法;

适用场景:日志(每个应用程序有不同的日志信息);document-oriented DB无固定的模式,可用来存储不同的信息;分析(鉴于它的弱模式结构,不改变模式就可存储不同的度量方法及添加新的度量);

不适用场景:不支持在不同的文档上添加事务(不支持文档间的事务);

企业应用:SAPmongoDB),codecademymongoDB),foursquaremongoDB),nbc newsravenDB

4graph存储,数据以图的方式存储,实体作为顶点,实体之间的关系作为边,如有3个实体,steve jobsapplenext,则会有2founded by边,将applenext连接到steve jobs;图形结构的DB同行列及刚性结构的SQL DB不同,它是使用灵活的图形模型,并能扩展到多个server上,nosql DB没有标准的查询语言SQL,在进行DB查询需要制定数据模型,许多nosql DBREST式的数据接口或查询API

产品有:neo4jinfogridinfinite graphorientDB

典型应用:SNS、推荐系统等,专注于构建关系图谱;

数据模型:图结构;

优势:利用图结构相关算法;劣势:需要对整个图作计算才能得出结果,不容易做分布式的集群方案;

适用场景:在关系型强的数据中;推荐引擎,若将数据以图的形式表现,会非常有益于推荐的制定;

不适用场景:数据模型适用范围小,很少有操作涉及到整个图;

企业应用:adobeciscot-mobile均使用neo4j

 

 

memcached

http://memcached.org/

key-value,是一个开源的高性能的,具有分布式内存对象的缓存系统,通过它可以减轻DB负载,加速动态web应用,最初始版本在2003年开发完成,很多公司都使用它来构建自己的大负载网站或提高网站响应速度,项目名称memcache,主程序文件为memcached

缓存一般用来保存一些经常被存取的对象或数据,通过内存来存取对象或数据要比disk快很多,memcached是一种内存缓存(进程退出,数据将丢失),把经常存取的对象或数据缓存在内存中,内存中缓存的这些数据通过API的方式被存取,数据就像一张大的hash表,以key:value对的方式存在,memcached通过缓存经常被存取的对象或数据,减轻DB的压力,提高网站的响应速度,构建出速度更快的可扩展的web应用;

memcached是内存缓存,缓存的数据不能持久化,若memcached在下次刚启动,由于内存中还没有缓存的数据,若在这一时间段请求DB的量很多,会造成DB压力过大,解决:通过脚本或程序,从DB里把数据r出来,先存到memcached中,再允许前端访问;

特点(部署简单,支持高并发、高性能;通过程序或LB可实现分布式;缓存的数据在内存中,重启服务后内容丢失);

 

memcacheDB

http://memcachedb.org/

sina基于memcached开发的一个开源项目,通过为memcached增加BDB的持久化存储机制和异步master-slave复制机制,使memcached具备事务恢复能力、持久化能力和分布式复制能力,适合超高性能rw速度、持久化保存的应用场景,sina用在blog上,若对memcached有持久化需求可考虑使用memcacheDB

memcacheDB支持类似MySQLmaster-slave同步(rw-splittingmaster可读写,slave只读);

特点(high performance r/w for a key-value based object;high reliablepersistent storage with transaction; high availability data storage withreplication; memcache protocol compatibility);

 

ttservertokyo tyrant/tokyocabinet):

tokyo cabinet是日本人mikio hirabayashi开发的一款DBM数据库,该DBrw非常快,是BDB的几倍,哈希模式写入100万条数据只需0.643s,读100万条数据只需0.773s

tokyo tyrant是提供tokyo cabinet数据库的网络接口,它使用简单的基于tcp/ip二进制协议进行通信,同时它拥有memcached的兼容协议,还可用http/1.1协议进行数据转换,实现了跨平台、跨语言使用,tokyo tyrant采用热备份、更新日志记录、replication来实现HA和高可靠性,tokyo tyrant可运行在linuxfreeBSDmacsolaristokyo tyrant is written in the C language,and provided as API ofC,Perl,and ruby,tokyo tyrant is available on platforms which have APIconforming to C99 and POSIX,tokyo tyrant is a free software licensed under theGNU lesser general public license

tokyo cabinet + tokyo tyrant构成了一款支持高并发的分布式持久存储系统,支持故障转移、高并发的分布式key-value持久存储系统,查询速度快、存放数据量大、支持高并发、非常适合主键进行查询,但不能进行复杂的条件查询;对任何原有memcached客户端来讲,可将tokyo tyrant看成是一个memcached,唯一不同的是它可持久存储

优势(不但支持内存缓存还可持久化存储;故障转移,tokyo tyrant支持主从模式,也支持双机互为主从模式,主从库均可rw5KW条数据级别内的访问相当快;兼容memcached协议,客户端不需更改任何代码);

 

mongoDBdocument-oriented):

MongoDB是一个基于分布式文件存储的数据库。由C++语言编写。旨在为WEB应用提供可扩展的高性能数据存储解决方案。

MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。他支持的数据结构非常松散,是类似jsonbson格式,因此可以存储比较复杂的数据类型。Mongo最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。

特点(易部署、高性能、易使用、存储数据非常方便);

主要功能特性有:

面向集合存储,易存储对象类型的数据(所谓“面向集合”(Collection-Oriented),意思是数据被分组存储在数据集中,被称为一个集合(Collection)。每个集合在数据库中都有一个唯一的标识名,并且可以包含无限数目的文档。集合的概念类似关系型数据库(RDBMS)里的表(table),不同的是它不需要定义任何模式(schema)Nytro MegaRAID技术中的闪存高速缓存算法,能够快速识别数据库内大数据集中的热数据,提供一致的性能改进。)

模式自由(schema-free,意味着对于存储在mongodb数据库中的文件,我们不需要知道它的任何结构定义。如果需要的话,你完全可以把不同结构的文件存储在同一个数据库里。)

支持动态查询。

支持完全索引,包含内部对象。

支持查询。

支持复制和故障恢复。

使用高效的二进制数据存储,包括大型对象(如视频等)。

自动处理碎片,以支持云计算层次的扩展性。

支持RUBYPYTHONJAVAC++PHPC#等多种语言。

文件存储格式为BSON(一种JSON的扩展)(存储在集合中的文档,被存储为键-值对的形式。键用于唯一标识一个文档,为字符串类型,而值则可以是各种复杂的文件类型。我们称这种存储形式为BSONBinary Serialized Document Format)

可通过网络访问(mongoDBserver-side可运行在linuxwinOS X平台,最好64bit,默认port27017

mongoDB把数据存储在文件中,默认在/data/db,为提高效率使用内存映射对文件进行管理;

 

cassandracolumn-oriented):

Cassandra是一套开源分布式NoSQL数据库系统。它最初由Facebook开发,用于储存收件箱等简单格式数据,集GoogleBigTable的数据模型与Amazon Dynamo的完全分布式的架构于一身Facebook2008 Cassandra 开源,此后,由于Cassandra良好的可扩展性,被DiggTwitter等知名Web 2.0网站所采纳,成为了一种流行的分布式结构化数据存储方案。

Cassandra是一个混合型的非关系的数据库,类似于GoogleBigTable。其主要功能比Dynamo(分布式的Key-Value存储系统)更丰富,但支持度却不如文档存储MongoDB(介于关系数据库和非关系数据库之间的开源产品,是非关系数据库当中功能最丰富,最像关系数据库的。支持的数据结构非常松散,是类似jsonbjson格式,因此可以存储比较复杂的数据类型)。Cassandra最初由Facebook开发,后转变成了开源项目。它是一个网络社交云计算方面理想的数据库。以Amazon专有的完全分布式的Dynamo为基础,结合了Google BigTable基于列族(Column Family)的数据模型。P2P去中心化的存储。很多方面都可以称之为Dynamo 2.0

Cassandra的主要特点就是它不是一个数据库,而是由一堆数据库节点共同构成的一个分布式网络服务,对Cassandra 的一个写操作,会被复制到其他节点上去,对Cassandra的读操作,也会被路由到某个节点上面去读取。对于一个Cassandra群集来说,扩展性能是比较简单的事情,只管在群集里面添加节点就可以了。

主要特性(分布式、基于cloumn的结构化、高伸展性)

 

 

生产环境选择:

常规cache应用,memcached最合适,简单、易用、高效;不好的是仅在内存缓存,大数据量要先预热再提供服务,无法直接实现master-salve同步;

若要持久化存储,用memcacheDBttserver,好处是兼容memcached协议,还能实现master-salve同步(生产中若有2KW条以内的数据量,用ttserver替代memcached最合适);

生产中有大数据量,可用redis持久化存储替代memcached,不好的是要重写代码(不兼容memcached协议);

生产中有PB级海量数据,且有很多server,延时和响应在一定时间内可接受,用cassandrahbasemongoDB都可胜任;

 

 

rediskey-value):

redis is an opensource,BSD liscensed,advanced key-value store,it is often referred to as a data structure server since keys can contain string,hashes,lists,sets and sorted sets.

REmote DIctionary server是一个开源的使用ANSIC语言编写(代码3W多行)、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API;从2010315日起,Redis的开发工作由VMware主持,从20135月开始,Redis的开发由Pivotal赞助;

redis是一个key-value存储系统;和Memcached类似,它支持存储的value类型相对更多,5种:string(字符串)list(列表)set(集合)zset(sorted set--有序集合)hash(哈希类型);

这些数据类型都支持push/popadd/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的,在此基础上,redis支持各种不同方式的排序;

memcached一样,为了保证效率,数据都是缓存在内存中,区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave同步;

redis 是一个高性能的key-value数据库,redis的出现,很大程度补偿了memcached这类key/value存储的不足,在部分场合可以对关系数据库起到很好的补充作用;

它提供了JavaC/C++C#PHPJavaScriptPerlObject-CPythonRubyErlang等客户端,使用很方便;

redis支持master-slave同步。数据可以从主服务器向任意数量的从服务器上同步,从服务器可以是关联其他从服务器的主服务器,这使得Redis可执行单层树复制,存盘可以有意无意的对数据进行写操作,由于完全实现了PUB/SUB机制,使得从数据库在任何地方同步树时,可订阅一个频道并接收主服务器完整的消息发布记录,同步对读取操作的可扩展性和数据冗余很有帮助;

redis的官网地址是redis.io。(域名后缀io属于国家域名,是british Indian Ocean territory,即英属印度洋领地)

特点(key-value类型存储;内存缓存(同memcached);可持久化(同memcacheDBttserver),支持数据可靠存储及落;数据类型更丰富;支持集群、分布式;单进程、单线程高性能server(可多实例运行);crash safe & recovery slow;单机qps可达到10Wquery per second;适合小数据量高速rw);

若从memcached改为-->redis客户代码要改;

优势(可持久化存储数据;性能很高,支持超10W/srw频率;丰富的数据类型;redis的所有操作都是原子性的,还支持几个操作合并后的原子性支持;丰富的特性,支持publisher/subscriber、通知、key过期等特性;支持异机主从复制);

劣势(系统运行有毛刺;不同命令延迟差别大;内存管理开销大(不要超过物理内存的60%);buffer io造成系统OOM);

 

 

redis持久化(两种:snapshottingaof):

通常redis将数据存储于内存中(或被配置使用虚拟内存,效率不高一般不用),通过两种方式可实现数据持久化(snapshot默认和aofappend only file));

snapshotting是在一定间隔时间做一次持久化,若redis意外挂掉则会丢失最后一次快照的所有修改,如果应用要求严格不能丢失任何数据,要采用aof方式;

更新频繁一致性要求较高,用AOF策略;更新不频繁,可容忍少量数据丢失或错误,用snapshot策略;

 

使用snapshot方式,将内存中的数据以快照方式不断写入到二进制文件中,默认文件名dump.rdb,此种方式性能较高,可能会引起一定程序的数据丢失,可配置自动做快照持久化,例如可配置在n秒内若超过mkey被修改就自动作快照;

[root@server1 ~]# vim /etc/redis.conf

################################SNAPSHOTTING ################################

#  save <seconds> <changes>

save 900 1  #(在900s内若超过1key被修改,则触发快照保存)

save 300 10   #(在300s内若超过10key被修改,则发起快照保存)

save 60 10000

dbfilename dump.rdb

dir /var/lib/redis/6379/

详细过程:

redis调用fork产生子进程;

父进程继续处理client请求,子进程负责将内存内容写入到临时文件,由于oscowcopy on write写时复制机制),父进程和子进程会共享相同的物理页面,当父进程处理w请求时os会为父进程要修改的页面创建副本,而不是写入共享的页面,所以子进程地址空间内的数据是fork这一时刻对整个DB的一个快照;

当子进程将快照写入临时文件完毕后,用临时文件替换原来的快照文件,然后子进程退出;

注:client也可使用savebgsave通知redis server做一次快照持久化,save操作是在主线程中运行的,而redis是用一个主线程来处理所有client的请求,所以在执行save操作时会阻塞当前client的所有请求,不推荐使用,最好用bgsave

每次快照持久化都是将内存数据完整写入到磁盘一次,并不是增量的只同步变更的数据,如果数据量大且写操作很多,会引起大量的disk io,将严重影响性能;数据快照的原理是将整个内存中redis的数据遍历一遍再放到dump.rdb二进制数据文件中;

[root@server1 ~]# ll --full-time /var/lib/redis/6379/

total 4

-rw-r--r--. 1 root root 33 2016-09-18 03:32:38.887828894 -0700 dump.rdb

[root@server1 ~]# redis-cli

127.0.0.1:6379> keys *

1) "key1"

127.0.0.1:6379> set key2 value2

OK

127.0.0.1:6379> bgsave

8790:M 18 Sep 18:20:31.200 * Background saving started by pid 8794

Background saving started

127.0.0.1:6379> 8794:C 18 Sep18:20:31.204 * DB saved on disk

                                                            8794:C 18 Sep 18:20:31.204 * RDB: 0 MB of memory used by copy-on-write

                                                                                                                                  8790:M 18 Sep 18:20:31.229 * Background saving terminated with success

127.0.0.1:6379> quit

[root@server1 ~]# ll --full-time /var/lib/redis/6379/

total 4

-rw-r--r--. 1 root root 46 2016-09-18 18:20:31.199696228 -0700 dump.rdb

 

aofappend only fileredis会将收到的写命令都通过write函数追加到文件中,默认是appendonly.aof,当redis重启时会重新加载这个文件,用以在内存中重建整个DB

类似MySQLbinlog日志方式,记录每次对DB有变化的操作

aof不用于master-slave同步;

os会在kernel中缓存write做的修改,cow机制不会立即同步到disk上,可通过appendfsyncalways强制os写入到disk,性能会受影响,但不会丢数据保证了完全的持久化;appendfsync默认everysec,在性能和持久化方面做了折衷;appendfsync no,完全按os来,性能最好,可能会丢数据;

[root@server1 ~]# vim /etc/redis.conf

save ""

############################## APPEND ONLYMODE ###############################

appendonly yes

appendfilename "appendonly.aof"

appendfsync everysec

auto-aof-rewrite-percentage 100

auto-aof-rewrite-min-size 64mb   #(当appendonly.aof100%达到64m时会自动调用bgrewriteaof命令将此文件中重复的多余的记录压缩)

[root@server1 ~]# ll --full-time /var/lib/redis/6379/

total 4

-rw-r--r--. 1 root root  0 2016-09-1818:48:29.121048063 -0700 appendonly.aof

-rw-r--r--. 1 root root 46 2016-09-1818:48:18.501048935 -0700 dump.rdb

[root@server1 ~]# redis-cli

127.0.0.1:6379> flushall

OK

127.0.0.1:6379> set num 8

OK

127.0.0.1:6379>incr num

(integer) 9

127.0.0.1:6379>incr num

(integer) 10

127.0.0.1:6379> quit

[root@server1 ~]# ll --full-time /var/lib/redis/6379

total 8

-rw-r--r--. 1 root root 116 2016-09-18 18:50:03.100611728 -0700 appendonly.aof

-rw-r--r--. 1 root root  46 2016-09-18 18:48:18.501048935 -0700dump.rdb

[root@server1 ~]# cat /var/lib/redis/6379/appendonly.aof   #redis重启时会加载此文件中内容,若是删除操作,可编辑此文件将del语句删除,重启即可恢复误删操作)

*2

$6

SELECT

$1

0

*1

$8

flushall

*3

$3

set

$3

num

$1

8

*2

$4

incr

$3

num

*2

$4

incr

$3

num

[root@server1 ~]# redis-cli

127.0.0.1:6379>incr num

(integer) 11

127.0.0.1:6379> get num

"11"

127.0.0.1:6379> bgrewriteaof

8826:M 18 Sep 18:54:29.263 * Backgroundappend only file rewriting started by pid 8841

Background append only file rewritingstarted

127.0.0.1:6379> 8826:M 18 Sep18:54:29.324 * AOF rewrite child asks to stop sending diffs.

                                                                                         8841:C18 Sep 18:54:29.324 * Parent agreed to stop sending diffs. Finalizing AOF...

                                        8841:C18 Sep 18:54:29.324 * Concatenating 0.00 MB of AOF diff received from parent.

                                                                                                                           8841:C 18 Sep 18:54:29.325 * SYNC append only file rewrite performed

                                                           8841:C 18 Sep18:54:29.325 * AOF rewrite: 0 MB of memory used by copy-on-write

   8826:M 18 Sep 18:54:29.396 * Background AOF rewrite terminated withsuccess

                                                                              8826:M 18 Sep18:54:29.396 * Residual parent diff successfully flushed to the rewritten AOF(0.00 MB)

                                              8826:M 18 Sep 18:54:29.396 * Background AOF rewrite finishedsuccessfully

127.0.0.1:6379> quit

[root@server1 ~]# cat /var/lib/redis/6379/appendonly.aof   #(比较前后appendonly.aof的变化)

*2

$6

SELECT

$1

0

*3

$3

SET

$3

num

$2

11

 

 

redis应用sina案例:

application-->redis

application-->redis-->MySQL   #redis作缓存用)

二次开发实现MySQLredis互相同步(MySQL-->redis(通过BRB解析binlog同步到redisredis提供特定数据结构的读;实现关系型数据转变成队列数据);redis-->MySQLredis提供特定数据结构的rw;通过replication接口同时写入到MySQL);

 

redis生产经验:

要进行master-slave同步配置,在故障时可切换;

w数据量很大,可在master上禁用持久化,只在slave上配置数据持久化,并在master上禁止redis故障自重启或开机自启动;官方强烈建议master要开启持久化,否则服务重启将把内存空数据传给slave

确保内存可用(物理内存一般64G-128G,磁盘最好用SSD),若内存不足时(物理内存+虚拟内存),执行dump时会无响应甚至server挂掉;

redis进程若占总内存的60%时性能会降低(内存碎片大),尽量不用swap

当达到最大内存时,会清空带有过期时间的key,甚至key未达到过期时间也会清掉;

redisDB同步,要先写DB再写redis

 

key/value

rediskey是二进制安全的,可用任何二进制序列作为key,例如“test”简单的string,或jpg文件的内容都可以,空字符串也是有效的key

key规则(不要用太长的key,如1024byte,不仅消耗内存,而且在数据查找时计算成本很高;也不要用太短的key,尽量见明知义,例如用“user:1000:password”取代“u:1000:pwd”,前者更易阅读,由此增加的空间相对于key objectvalue object很小可忽略;最好坚持用一种模式,例如“object-type:id:field”,“comment:1234:reply.to”,多个单词用点隔开);

key建议(长度10-20,例如object-type:id:field);

vlaue建议(string不超过2000setsorted set不超过5000);

根据内容长度及占用的内存空间规划,若数据量很大,使用不同的instance

#redis-cli set user:test01:passwd test01

#redis-cli get user:test01:passwd

 

wKiom1ffeIiiXA8oAABJIvx_ifo940.jpg

 

redis测试:

[root@server1 ~]# redis-benchmark -h 10.96.20.113 -p 6379 -c 10 -n 1000

 

 

redis优化:

1)内存管理优化:

redis hashvalue内部是一个hash map,若该map的成员数较少,则会采用类似一堆线性紧凑格式来存储该map,即省去了大量指针的内存开销;

相关参数:

hash-max-zipmap-entries 64指当value这个map内部不超过多少个成员时会采用线性紧凑技术,默认64,即value内部有少于64个成员时使用线性紧凑格式存储,超过该值自动转为hash map方式;

hash-max-zipmap-value 512指当value这个map内的每个成员值长度不超过多少个byte就会采用线性紧凑格式存储来节省空间;

以上两个条件任意一个满足都会转为真正的hash map,将不再节省内存,这两个参数值不是越大越好,hash map的优势是查找和操作的时间复杂度都是O1),而线性紧凑格式存储则是On),如果成员很少则影响不大,否则会严重影响性能,要权衡好这两个参数值,总体上根本的是权衡时间成本和空间成本;

2)内存预分配:

memcached相比redis内部没有在内存分配方面做过多优化,在一定程度上会存在内存碎片,不过大多数情况下这不会成为redis的性能瓶颈,如果在redis中存储的大部分数据是数值型的话,redis内部采用了shared integer方式来省去分配内存的开销,即在redis启动时先分配一个从1-10000的数值放在一个pool中,如果存储的数值恰好是默认范围内的,则直接从pool中取出该对象,并通过引用计数的方式共享,这样在redis中若存储了大量数值也能在一定程度上节省内存并提高性能,pool的范围在源码中定义REDIS_SHARED_INTEGERS默认10000,修改后要重新编译;

3)持久化机制:

snapshotredis内部的一个定时器事件,每隔固定时间去检查当前数据发生的改变次数与时间是否满足配置的持久化触发条件,如果满足则通过os fork创建一个子进程,这个子进程默认会与父进程共享相同的内存地址空间,这时可通过子进程来遍历整个内存,进而再进行存储操作,而主进程仍可提供服务,当有写入时由os按内存页page为单位进行cow保证父进程与子进程之间不相互影响,缺点:定时快照只代表一段时间的内存对象,redis重启后会丢失上次快照与出故障时这个时间段的数据;

aof记录使redis内存数据发生改变的命令,保存在appendonly.aof中,类似MySQLbinlog,但又有不同,redis利用backlog同步数据,缺点:appendonly.aof会越来越大,多达几十G,重启redis恢复时会用稍长时间,这个耗时不是因为disk读取速度慢,而主要是读出的所有命令都要在内存中执行一遍,要保证所有数据都不丢失使用appendfsync always,这会带来整个系统性能下降,若数据量很大可考虑将数据保存到不同的redis instance中,这样既可减少缓存失效带来的影响,又可加快数据恢复的速度;

持久化崩溃问题(一般redis占总内存60%时会出现不稳定或崩溃,redis使用物理内存较多,在还没超过实际物理内存总容量时就出现不稳定甚至崩溃,有人认为是基于snapshot方式持久化时,fork系统调用造成内存占用加倍导致,不完全准确,fork调用的cow机制是基于os内存page这个单位的,只有写入dirty page会被复制,但redis系统不会在短时间内所有页都发生了写入而导致复制;redis持久化使用了buffer io造成崩溃,buffer io是指redis对持久化文件的写入和读取操作都会使用物理内存的page cache,而大多数DB系统会使用direct io来绕过这层page cache并自行维护一个数据的cache,当redis持久化的文件过大(尤其是快照文件),并对其进行rw时,disk文件中的数据都会被加载到物理内存中作为os对该文件的一层cache,而这层cache的数据与redis内存中管理的数据是重复的,虽然kernel在物理内存紧张时会做page cache的剔除工作,但kernel很可能认为某块page cache很重要,而让进程swap,这时redis就开始不稳定或崩溃了);

redis优化总结:

根据业务需要选择合适的数据类型,并为不同的应用场景设置相应的紧凑存储参数;

当业务场景不需要数据持久化时,关闭所有的持久化方式可获得最佳的性能和最大的内存使用量;

若需要使用持久化,根据是否可以容忍重启后丢失部分数据,在snapshotaof,这两者中选其一,不要使用虚拟内存vmdisk store方式(diskstoreobsolete);

尽量不要让redis超过占总内存的60%

redis.confmaxmemory,是告诉redis当使用了多少物理内存后就开始拒绝后续的写入请求,该参数能很好的保护redis不会因为使用了过多的物理内存而导致swap,最终严重影响性能开始不稳定甚至崩溃;

大数据量尽量按业务使用多个redis instance把数据分散开;

开发优化(一次性成批量写入(如50w条记录使用hmset需要0.04278秒)比多次循环写入(50w条记录写入需要0.87767秒)效率要高,可适当考虑延迟汇总写入,分布式的redis系统一次性写入只需耗费数据传输的速度(建立连接、传输数据、关闭连接),写入速度太快可忽略;设计数据结构时尽量少占内存,如keycoupon:exterprise:N:consumer:N改为cp:et:N:cm:N;命名方式除了简短,可用后缀s代表set,后缀l代表list,后缀单数则代表统计数据等自定义方法;hash结构存储使用了zipmap会大大压缩数据,50w条记录用string方式大约45m,而使用hash存储降低到不到20m空间,hash设计参数采用了instagram的设计,如50w条数据分成500hash bucket,每个里面存1000个;use hashes when possible

 

 

 

操作:

master10.96.20.113,主机名server1

slave10.96.20.114,主机名server2

http://download.redis.io/releases/redis-3.0.7.tar.gz

 

[root@server1 ~]# uname -rm

2.6.32-431.el6.x86_64 x86_64

[root@server1 ~]# cat /etc/redhat-release

Red Hat Enterprise Linux Server release 6.5(Santiago)

 

[root@server1 ~]# tar xf redis-3.0.7.tar.gz

[root@server1 ~]# cd redis-3.0.7

[root@server1 redis-3.0.7]# less README

[root@server1 redis-3.0.7]# make

[root@server1 redis-3.0.7]# make PREFIX=/usr/local/redis install

 

[root@server1 redis-3.0.7]# ls/usr/local/redis/bin   #redis-serverredisdaemon程序);redis-cliredis命令行操作工具,也可用telnet根据纯文本协议操作);redis-benchmarkredis性能测试工具,测试rw性能);redis-check-aof(对更新日志appendonly.aof检查,类似检查MySQLbinlog的工具);redis-check-dump(用于本地数据库rdb文件的检查))

redis-benchmark  redis-check-aof  redis-check-dump  redis-cli redis-sentinel  redis-server

[root@server1 redis-3.0.7]# vim /etc/profile.d/redis.sh

export PATH=$PATH:/usr/local/redis/bin/

[root@server1 redis-3.0.7]# . !$

. /etc/profile.d/redis.sh

[root@server1 redis-3.0.7]# whichredis-server

/usr/local/redis/bin/redis-server

[root@server1 redis-3.0.7]# cp redis.conf /etc/

[root@server1 redis-3.0.7]# egrep -v "^#|^$" /etc/redis.conf   #redis.conf文件中分段配置,有:includesgeneralsnapshottingreplicationsecuritylimitsappend onlymodelua scriptingredis clusterslow loglatency monitorevent notificationadvanced config

daemonize no

pidfile /var/run/redis.pid

port 6379

tcp-backlog 511   #tcp队列,/proc/sys/net/core/somaxconn/proc/sys/net/ipv4/tcp_max_syn_backlog

timeout 0   #client超时,0表示不超时)

tcp-keepalive 0   #tcp会话保持)

loglevel notice   #(生产环境至少是notice级别)

logfile ""   #(默认是stdout,也可指定具体文件)

databases 16   #set the number ofdatabasesthe default is DB0,使用select <dbid>可切库,dbid is a number between 0 and databases-10-15))

save 900 1  #snapshot保存,会block客户端请求,save ""表示不保存snapshot

save 300 10

save 60 10000

stop-writes-on-bgsave-error yes   #(在bgsave出错时停止w

rdbcompression yes   #(压缩DB

rdbchecksum yes

dbfilename dump.rdb

dir ./  #the working directory,指定dump.rdbappendonly.aof的存放位置,默认启动进程时的当前目录下)

slave-serve-stale-data yes

slave-read-only yes

repl-diskless-sync no

repl-diskless-sync-delay 5

repl-disable-tcp-nodelay no

slave-priority 100

appendonly no

appendfilename "appendonly.aof"   #(不用于master-slave,仅在redis重启时将数据从disk读入到内存)

appendfsync everysec

no-appendfsync-on-rewrite no

auto-aof-rewrite-percentage 100

auto-aof-rewrite-min-size 64mb

aof-load-truncated yes

lua-time-limit 5000

slowlog-log-slower-than 10000

slowlog-max-len 128

latency-monitor-threshold 0

notify-keyspace-events ""

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

hll-sparse-max-bytes 3000

activerehashing yes

client-output-buffer-limit normal 0 0 0

client-output-buffer-limit slave 256mb 64mb60

client-output-buffer-limit pubsub 32mb 8mb60

hz 10

aof-rewrite-incremental-fsync yes

 

注:另redis.conf中注释的命令:maxmemory <bytes>(当存的数据达到此处限定,会根据算法清之前的数据);maxmemory-policy noeviction(默认算法noeviction,算法有:volatile-lru-> remove the key with an expire set using an LRU algorithmallkeys-lru-> remove any key according to the LRU algorithmvolatile-random-> remove a random key with an expire setallkeys-random ->remove a random key, any keyvolatile-ttl -> remove the key with the nearest expire time(minor TTL)noeviction -> don't expire at all, just return an error on writeoperations

 

[root@server1 redis-3.0.7]# redis-server /etc/redis.conf &   #redis启动)

……

8888:M 12 Sep 03:11:22.397# WARNING: The TCP backlog setting of 511 cannot be enforced because/proc/sys/net/core/somaxconn is set to the lower value of 128.

8888:M 12 Sep 03:11:22.397 # Serverstarted, Redis version 3.0.7

8888:M 12 Sep03:11:22.397 # WARNING overcommit_memory is set to 0! Background save may failunder low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to/etc/sysctl.conf and then reboot or run the command 'sysctlvm.overcommit_memory=1' for this to take effect.

8888:M 12 Sep 03:11:22.398 * The server isnow ready to accept connections on port 6379

 

[root@server1 redis-3.0.7]# netstat -tnulp | grep redis

tcp       0      0 0.0.0.0:6379                0.0.0.0:*                   LISTEN      8888/redis-server *

tcp       0      0 :::6379                     :::*                        LISTEN      8888/redis-server *

 

[root@server1 redis-3.0.7]# redis-cli shutdown  #redis关闭)

8888:M 12 Sep 03:12:44.312 # User requestedshutdown...

8888:M 12 Sep 03:12:44.312 * Saving thefinal RDB snapshot before exiting.

8888:M 12 Sep 03:12:44.325 * DB saved ondisk

8888:M 12 Sep 03:12:44.325 # Redis is nowready to exit, bye bye...

[root@server1 redis-3.0.7]# netstat -tnulp|grep redis

 

[root@server1 redis-3.0.7]# vim /etc/sysctl.conf   #/proc/sys/net/core/somaxconn,监听队列的长度,对于一个TCP连接,ServerClient需要通过三次握手来建立网络连接,当三次握手成功后,端口的状态由LISTEN转变为ESTABLISHED,接着这条链路上就可以开始传送数据了,每一个处于监听Listen状态的端口,都有自己的监听队列,对于一个经常处理新连接的高负载 web服务环境来说,默认的 128 太小了,大多数环境这个值建议增加到 1024 或者更多,服务进程(httpdsendmail)会自己限制侦听队列的大小(例如 sendmail(8),常常在它们的配置文件中有设置队列大小的选项,大的侦听队列对防止拒绝服务DoS ***也会有所帮助;/proc/sys/vm/overcommit_memory,默认0(启发式过量,当userspace请求更多的内存时,内核尝试估算出剩余可用的内存),1(内核允许过量使用内存,直到用完为止,主要用于科学计算),2(内核会用一个决不过量使用内存的算法设定,即系统整个内存地址空间不能超过swap+总内存的50%50/proc/sys/vm/overcommit_ratio的默认值,overcommit_memory2时要结合overcommit_ratio设定)

net.core.somaxconn = 1024

vm.overcommit_memory = 1

[root@server1 redis-3.0.7]# sysctl -p

……

[root@server1 redis-3.0.7]# cat /proc/sys/net/core/somaxconn

1024

[root@server1 redis-3.0.7]# cat /proc/sys/vm/overcommit_memory

1

 

[root@server1 redis-3.0.7]# redis-server /etc/redis.conf &

[1] 8985

[root@server1 redis-3.0.7]#                 _._                                                 

          _.-``__ ''-._                                            

     _.-``    `.  `_. ''-._           Redis 3.0.7(00000000/0) 64 bit

 .-`` .-```.  ```\/    _.,_ ''-._                                  

 (   '      ,       .-` | `,    )     Running in standalone mode

 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379

 |   `-._   `._    /    _.-'    |     PID: 8985

 `-._    `-._  `-./ _.-'    _.-'                                  

 |`-._`-._   `-.__.-'    _.-'_.-'|                                 

 |   `-._`-._        _.-'_.-'    |          http://redis.io       

 `-._    `-._`-.__.-'_.-'    _.-'                                  

 |`-._`-._   `-.__.-'    _.-'_.-'|                                 

 |   `-._`-._        _.-'_.-'    |                                 

 `-._    `-._`-.__.-'_.-'    _.-'                                  

     `-._    `-.__.-'    _.-'                                      

         `-._        _.-'                                          

              `-.__.-'                                               

 

8985:M 12 Sep 03:45:24.503 # Serverstarted, Redis version 3.0.7

8985:M 12 Sep 03:45:24.504 * DB loaded fromdisk: 0.000 seconds

8985:M 12 Sep 03:45:24.504 * The server isnow ready to accept connections on port 6379

[root@server1 redis-3.0.7]# ps aux | grepredis

root      8985  0.0  1.0 137448 2344 pts/0    Sl   03:45  0:00 redis-server *:6379        

root      8989  0.0  0.3 103252  824 pts/0    S+   03:45  0:00 grep redis

 

 

连接redis server的方式:

#redis-cli -h IP -p PORT cmd [arg [arg ...]]

#redis-cli -h IP -p PORT [-a PASSWORD]  (进入交互模式下操作)

#telnet IP PORT

#echo "cmd args" | nc IP PORT

 

[root@server1 ~]# redis-cli -h

Usage: redis-cli [OPTIONS] [cmd [arg [arg...]]]

  -h<hostname>      Server hostname(default: 127.0.0.1).

  -p<port>          Server port(default: 6379).

  -s<socket>        Server socket(overrides hostname and port).

  -a<password>      Password to usewhen connecting to the server.

 --stat             Print rollingstats about server: mem, clients, ...

 

[root@server1 ~]# redis-cli help

redis-cli 3.0.7

Type: "help @<group>" toget a list of commands in <group>

     "help <command>" for help on <command>

     "help <tab>" to get a list of possible help topics

     "quit" to exit

[root@server1 ~]# redis-cli help @generic   #(常用的命令:DEL key [key...]EXISTS key [key ...]KEYS patternEXPIRE key secondsEXPIREAT key timestampRENAME key newkeyTTL key

[root@server1 ~]# redis-cli help @string

[root@server1 ~]# redis-cli help @list

[root@server1 ~]# redis-cli help @set

[root@server1 ~]# redis-cli help @hash

[root@server1 ~]# redis-cli info   #(可查有关redis server的所有信息,有:serverclientsmemorypersistencestatsreplicationcpuclusterkeyspace

[root@server1 ~]# redis-cli info replication

 

[root@server1 ~]# redis-cli   #(可直接进入交互模式)

127.0.0.1:6379> help   #(或?,常用的命令setgetquitsavebgsavebgrewriteaofshutdwonshutdwonsavedelflushallinfoinfo cpukeys *dbsizeconfig get parameterconfig set parameter value在交互模式下改配置信息)

redis-cli 3.0.7

Type: "help @<group>" toget a list of commands in <group>

     "help <command>" for help on <command>

     "help <tab>" to get a list of possible help topics

     "quit" to exit

127.0.0.1:6379> set key1 value1

OK

127.0.0.1:6379> get key1

"value1"

127.0.0.1:6379> get key2   #(nil)为空)

(nil)

127.0.0.1:6379> exists key1   #1True0False

(integer) 1

127.0.0.1:6379> keys *   #(取所有key

1) "key1"

127.0.0.1:6379> select 1   #(切库,默认0-1516个库,默认在第0库)

OK

127.0.0.1:6379[1]> keys *

(empty list or set)

127.0.0.1:6379[1]> quit

 

[root@server1 ~]# redis-cli -h 10.96.20.113 -p 6379 set key2 value2   #(非交互模式下直接操作)

OK

[root@server1 ~]# redis-cli -h 10.96.20.113 -p 6379 get key2

"value2"

[root@server1 ~]# redis-cli -h 10.96.20.113 -p 6379 get key1

"value1"

 

[root@server1 ~]# telnet 10.96.20.113 6379   #(可在telnet下操作)

Trying 10.96.20.113...

Connected to 10.96.20.113.

Escape character is '^]'.

set key3 value3

+OK

get key3

$6

value3

^]

telnet> quit          

Connection closed.

 

[root@server1 ~]# echo "set key4 value4" | nc 10.96.20.113 6379   #(可通过nc操作)

+OK

[root@server1 ~]# echo "get key4"| nc 10.96.20.113 6379

$6

value4

 

 

[root@server1 ~]# vim /etc/redis.conf   #requirepass test,为redis设置外部连接密码,redis速度很快,在一台性能很高的server上,一个外部的用户可在1s内进行150K+次密码尝试,所以要指定非常强大的密码防止暴力破解;rename-command set ""用于将认为危险的命令屏蔽,rename-commandget gets用于将认为危险的命令改名)

################################## SECURITY###################################

requirepass test

rename-command set ""

rename-command get gets

[root@server1 ~]# redis-cli shutdown

[root@server1 ~]# redis-server /etc/redis.conf &

……

[root@server1 ~]# redis-cli   #(或直接用#redis-cli -a test进入操作)

127.0.0.1:6379> keys *

(error) NOAUTH Authentication required.

127.0.0.1:6379>auth test

OK

127.0.0.1:6379> keys *

1) "key2"

2) "key3"

3) "key1"

127.0.0.1:6379> get key3

(error) ERR unknown command 'get'

127.0.0.1:6379> gets key3

"value3"

127.0.0.1:6379> set key4 value4

(error) ERR unknown command 'set'

127.0.0.1:6379> quit

[root@server1 ~]# vim /etc/redis.conf

#requirepass test

#rename-command set ""

#rename-command get gets

[root@server1 ~]# redis-cli shutdown

(error) NOAUTH Authentication required.

[root@server1 ~]# redis-cli -a test shutdown

13180:M 17 Sep 18:52:32.692 # Userrequested shutdown...

13180:M 17 Sep 18:52:32.692 * Saving thefinal RDB snapshot before exiting.

13180:M 17 Sep 18:52:32.695 * DB saved ondisk

13180:M 17 Sep 18:52:32.695 # Redis is nowready to exit, bye bye...

[1]+ Done                    redis-server/etc/redis.conf

[root@server1 ~]# redis-server /etc/redis.conf &

 

 

php安装redis扩展(LNMP环境安装见《理解LNMP》):

[root@server1 ~]# cd lnmp

[root@server1 lnmp]# tar xf phpredis-2.2.4.tar.gz   #(下载地址http://redis.googlecode.com/files/redis-2.4.2.tar.gz

[root@server1 lnmp]# cd phpredis-2.2.4

[root@server1 phpredis-2.2.4]# /usr/local/php/bin/phpize

Configuring for:

PHP Api Version:         20100412

Zend Module Api No:      20100525

Zend Extension Api No:   220100525

[root@server1 phpredis-2.2.4]# ./configure --with-php-config=/usr/local/php/bin/php-config

[root@server1 phpredis-2.2.4]# make && make install

……

Installing shared extensions:    /usr/local/php/lib/php/extensions/no-debug-non-zts-20100525/

[root@server1 phpredis-2.2.4]# vim /etc/php.ini   #(在末尾添加如下一行,也可写成两行,extension= redis.soextension_dir = "/usr/local/php/lib/php/extensions/no-debug-non-zts-20100525/"

extension = "/usr/local/php/lib/php/extensions/no-debug-non-zts-20100525/redis.so"

[root@server1 phpredis-2.2.4]# service php-fpm restart

Gracefully shutting down php-fpm . done

Starting php-fpm  done

[root@server1 phpredis-2.2.4]# vim /usr/html/test.php

<?php

 $redis = new Redis();

 $redis ->connect("10.96.20.113",6379);

 /* $redis -> auth("test"); */

 $redis ->set("key8","value8");

 $var = $redis->get("key8");

 echo "$var\n";

?>

[root@server1 phpredis-2.2.4]# /usr/local/php/bin/php /usr/html/test.php   #(在命令行下操作脚本)

value8

wKioL1ffeaHxWnNbAABeH5sJgTY626.jpg

wKiom1ffea6RKbcRAAA-ayChIoE774.jpg

 

 

python操作redis

[root@server1 ~]# wget --no-check-certificate  https://pypi.python.org/packages/source/r/redis/redis-2.10.1.tar.gz

[root@server1 ~]# tar xf redis-2.10.1.tar.gz

[root@server1 ~]# cd redis-2.10.1

[root@server1 redis-2.10.1]# python setup.py install

[root@server1 redis-2.10.1]# python -V

Python 2.6.6

[root@server1 redis-2.10.1]# vim /usr/html/python_redis.py

#!/usr/bin/python

#

import redis

r = redis.Redis(host='10.96.20.113',port=6379,db=0)

r.set('key9','value9')

print r.get('key9')

[root@server1 redis-2.10.1]# python /usr/html/python_redis.py

value9

 

 

redis数据类型(stringlistsetzsethash):

string

字符串类型是redis最简单的数据类型,若只使用stringredis就是一个持久化的memcached server,实际redisstring功能要比memcached多;

1)常规的stringvalue可以是任何类型的string,包括binary,可保存jpg图片,value的长度不能超过1G#redis-cliset mykey "mybinary safe value"#redis-cli get mykey);

2string其它功能,对valuenumerical可进行加减操作:

[root@server1 ~]# redis-cli

127.0.0.1:6379> help @string

……

127.0.0.1:6379> help incr

 INCR key

 summary: Increment the integer value of a key by one

 since: 1.0.0

 group: string

127.0.0.1:6379> help incrby

 INCRBY key increment

 summary: Increment the integer value of a key by the given amount

 since: 1.0.0

 group: string

127.0.0.1:6379> set counter 21

OK

127.0.0.1:6379>incr counter

(integer) 22

127.0.0.1:6379> incr counter

(integer) 23

127.0.0.1:6379> incrby counter 2

(integer) 25

127.0.0.1:6379> decr counter

(integer) 24

127.0.0.1:6379> decrby counter 2

(integer) 22

127.0.0.1:6379> get counter

"22"

3)为key设置新值并返回原值(打印出的是原值,但原值已被覆盖为新设置的值):

127.0.0.1:6379> help getset

 GETSET key value

 summary: Set the string value of a key and return its old value

 since: 1.0.0

 group: string

127.0.0.1:6379> getset key2 value2.2

"value2"

127.0.0.1:6379> get key2

"value2.2"

4)支持批量rw操作:

127.0.0.1:6379> help mset

 MSET key value [key value ...]

 summary: Set multiple keys to multiple values

 since: 1.0.1

 group: string

127.0.0.1:6379> help mget

 MGET key [key ...]

 summary: Get the values of all the given keys

 since: 1.0.0

 group: string

127.0.0.1:6379> mset key4 value4 key5 value5 key6 value6

OK

127.0.0.1:6379> mget key4 key5 key6

1) "value4"

2) "value5"

3) "value6"

5)支持部分修改操作:

127.0.0.1:6379> help append

 APPEND key value

 summary: Append a value to a key

 since: 2.0.0

 group: string

127.0.0.1:6379> append key6 6

(integer) 7

127.0.0.1:6379> get key6

"value66"

6)查看value的长度:

127.0.0.1:6379> help strlen

 STRLEN key

 summary: Get the length of the value stored in a key

 since: 2.2.0

 group: string

127.0.0.1:6379> strlen key6

(integer) 7

127.0.0.1:6379> strlen key5

(integer) 6

 

listredis lists are implemented with linked lists because for a database system it is crucial to be able to add elements to a very logn list in a very fast way,another strong advantage is,as you'll see in a moment,that redis lists can be taken atconstant length in constant time.):

redis listslinked lists实现的原因:能快速的在很大的列表上添加元素,这是DB系统的重要特性,redis list能在常数时间取得常数长度;

redis list可用于程序中的聊天功能;

127.0.0.1:6379> help @list

……

127.0.0.1:6379> flushall

OK

127.0.0.1:6379> keys *

(empty list or set)

127.0.0.1:6379> lpush k1234 v1 v2 v3

(integer) 3

127.0.0.1:6379> lpush k1234 v4

(integer) 4

127.0.0.1:6379> llen k1234

(integer) 4

127.0.0.1:6379> lrange k1234 0 3

1) "v4"

2) "v3"

3) "v2"

4) "v1"

127.0.0.1:6379> lrem k1234 1 v4

(integer) 1

127.0.0.1:6379> lrange k1234 0 3

1) "v3"

2) "v2"

3) "v1"

127.0.0.1:6379> lrem k1234 1 v2

(integer) 1

127.0.0.1:6379> lrange k1234 0 1

1) "v3"

2) "v1"

127.0.0.1:6379> lpop k1234   #(删除并弹出列表的第一个element

"v3"

127.0.0.1:6379> linsert k1234 before v1v4

(integer) 2

127.0.0.1:6379> lrange k1234 0 8

1) "v4"

2) "v1"

 

setset是未排序的集合,元素是二进制安全的string,操作有:saddsmemberssismembersremsdiff差集、sinter交集、sunion并集等,set可实现标签功能,开发可用于保存关系;sorted_set中的数据有score属性,并在写入时会按score进行排序,操作有:zaddzrangezcorezrangebyscorezcount等):

127.0.0.1:6379> help @set

……

127.0.0.1:6379> help @sorted_set

……

127.0.0.1:6379> flushall

OK

127.0.0.1:6379> sadd k1234 v1 v2 v3 v4   #(向set中添加多个新元素)

(integer) 4

127.0.0.1:6379> smembers k1234

1) "v3"

2) "v4"

3) "v2"

4) "v1"

127.0.0.1:6379> sismember k1234 v3   #(判断set中的某个element是否存在,1True0False

(integer) 1

127.0.0.1:6379> srem k1234 v4 v3

(integer) 2

127.0.0.1:6379> smembers k1234

1) "v2"

2) "v1"

127.0.0.1:6379> flushall

OK

127.0.0.1:6379> zadd week 1 mon 2 tue

(integer) 2

127.0.0.1:6379> zadd week 4 thu 6 sat

(integer) 2

127.0.0.1:6379> zadd week 3 wed 5 fri 7sun

(integer) 3

127.0.0.1:6379> zrange week 0 6

1) "mon"

2) "tue"

3) "wed"

4) "thu"

5) "fri"

6) "sat"

7) "sun"

127.0.0.1:6379> zscore week sat   #(查看value在集合中的index位置)

"6"

127.0.0.1:6379> zrangebyscore week 2 5

1) "tue"

2) "wed"

3) "thu"

4) "fri"

127.0.0.1:6379> zadd hacker 1940 'alankay' 1953 'richard stallman' 1965 'yukihiro matsumto' 1969 'linus torvalds'1912 'alan turing'

(integer) 5

127.0.0.1:6379> zrange hacker 0 4

1) "alan turing"

2) "alan kay"

3) "richard stallman"

4) "yukihiro matsumto"

5) "linus torvalds"

127.0.0.1:6379> zrevrange hacker 0 4

1) "linus torvalds"

2) "yukihiro matsumto"

3) "richard stallman"

4) "alan kay"

5) "alan turing"

127.0.0.1:6379> zremrangebyscore hacker-inf 1950   #(删除key1950年之前的记录)

(integer) 2

127.0.0.1:6379> zrange hacker 0 4

1) "richard stallman"

2) "yukihiro matsumto"

3) "linus torvalds"

 

hash(存储key对多个属性的数据):

127.0.0.1:6379> help @hash

……

127.0.0.1:6379> flushall

OK

127.0.0.1:6379> help hset

 HSET key field value

 summary: Set the string value of a hash field

 since: 2.0.0

 group: hash

127.0.0.1:6379> hset test name jowin

(integer) 1

127.0.0.1:6379> hset test age 18

(integer) 1

127.0.0.1:6379> hset test sex male

(integer) 1

127.0.0.1:6379> hvals test

1) "jowin"

2) "18"

3) "male"

127.0.0.1:6379> hgetall test

1) "name"

2) "jowin"

3) "age"

4) "18"

5) "sex"

6) "male"

127.0.0.1:6379> hdel test sex

(integer) 1

127.0.0.1:6379> hvals test

1) "jowin"

2) "18"

127.0.0.1:6379> hmget test name age

1) "jowin"

2) "18"

127.0.0.1:6379> quit

[root@server1 ~]# redis-cli shutdown

 

pub/sub,发布/订阅是一种消息通信模式,主要目的解耦消息发布者和消息订阅者之间的耦合,也解决两者在物理部署上的耦合,与设计模式中的观察者模式类似;

redis作为一个pub/subserver,在订阅者和发布者之间起到了消息路由的功能,订阅者可通过publishsubscribe命令向redis server订阅自己感兴趣的消息类型,redis将消息类型称为channel,当发布者通过publish命令向redis server发送特定类型的消息时,订阅该消息的所有client都会收到此消息,消息的传递是多对多的,一个client可订阅多个channel,也可向多个channel发送消息;

redis可将数据推到某个信息管道中,其它人可通过订阅这些管道来获取推送过来的消息;

127.0.0.1:6379> subscribe channelone  

Reading messages... (press Ctrl-C to quit)

1) "subscribe"

2) "channelone"

3) (integer) 1

1) "message"

2) "channelone"

3) "hello"

1) "message"

2) "channelone"

3) "world"

127.0.0.1:6379> publish channelone hello   #(在另一终端执行)

(integer) 1

127.0.0.1:6379> publish channelone world

(integer) 1

127.0.0.1:6379> psubscribe channel*   #psubscribe监听多个信道)

Reading messages... (press Ctrl-C to quit)

1) "psubscribe"

2) "channel*"

3) (integer) 1

1) "pmessage"

2) "channel*"

3) "channelone"

4) "hello world"

1) "pmessage"

2) "channel*"

3) "channeltwo"

4) "welcome"

127.0.0.1:6379> publish channelone "hello world"   #(在另一终端执行)

(integer) 1

127.0.0.1:6379> publish channeltwo "welcome"

(integer) 1

 

过期机制:

redis对过期key采用了lazy expiration,两种方式:passive way(在访问key时判定key是否过期,如果过期则进行过期处理);active way(每秒对volatile keys进行抽样测试,如果有过期key,将对所有过期key进行处理);

127.0.0.1:6379> flushall

6144:M 18 Sep 03:20:04.618 * DB saved ondisk

OK

127.0.0.1:6379> keys *

(empty list or set)

127.0.0.1:6379> set key1 value1

OK

127.0.0.1:6379> exists key1

(integer) 1

127.0.0.1:6379> ttl key1   #-1为永不过期,-2为过期)

(integer) -1

127.0.0.1:6379> help expire

 EXPIRE key seconds

 summary: Set a key's time to live in seconds

 since: 1.0.0

 group: generic

127.0.0.1:6379> help expireat

 EXPIREAT key timestamp

 summary: Set the expiration for a key as a UNIX timestamp

 since: 1.2.0

 group: generic

127.0.0.1:6379> expire key1 5

(integer) 1

127.0.0.1:6379> ttl key1

(integer) 2

127.0.0.1:6379> ttl key1

(integer) -2

127.0.0.1:6379> get key1

(nil)

 

transaction

redis支持简单的组合型的命令,例如以nx结尾的命令用于判断key所对应的value没有时也进行某个命令;

127.0.0.1:6379> help @transactions

……

127.0.0.1:6379> keys *

(empty list or set)

127.0.0.1:6379> setnx key1 value1

(integer) 1

127.0.0.1:6379> get key1

"value1"

127.0.0.1:6379> multi

OK

127.0.0.1:6379> set key1 value2

QUEUED

127.0.0.1:6379> set key1 value3

QUEUED

127.0.0.1:6379> set key1 value4   #(当事务中有错误时将都不执行)

QUEUED

127.0.0.1:6379> exec   #(执行以上事务,discard为取消事务)

1) OK

2) OK

3) OK

127.0.0.1:6379> get key1

"value4"

 

 

redis多实例:

MySQL的多实例,两种方式(不同的配置文件,不同的程序目录,不同的数据目录;同一个配置文件,不同的程序目录,不同的数据目录);

redis的多实例(不同的配置文件,同一个程序目录,不同的数据目录);

[root@server1 ~]# mkdir -pv /var/lib/redis/{6379,6380}

mkdir: created directory `/var/lib/redis'

mkdir: created directory`/var/lib/redis/6379'

mkdir: created directory`/var/lib/redis/6380'

[root@server1 ~]# mv /etc/redis.conf/etc/redis_6379.conf

[root@server1 ~]# cp /etc/redis_6379.conf/etc/redis_6380.conf

[root@server1 ~]# vim /etc/redis_6379.conf

pidfile /var/run/redis_6379.pid

port 6379

dir /var/lib/redis/6379/

[root@server1 ~]# vim /etc/redis_6380.conf

pidfile /var/run/redis_6380.pid

port 6380

dir /var/lib/redis/6380/

[root@server1 ~]# redis-server /etc/redis_6379.conf &

[1] 5998

[root@server1 ~]#                 _._                                                 

          _.-``__ ''-._                                            

     _.-``    `.  `_. ''-._           Redis 3.0.7(00000000/0) 64 bit

 .-`` .-```.  ```\/    _.,_ ''-._                                  

 (   '      ,       .-` | `,    )     Running in standalone mode

 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379

 |    `-._  `._    /     _.-'   |     PID: 5998

 `-._    `-._  `-./ _.-'    _.-'                                  

 |`-._`-._   `-.__.-'    _.-'_.-'|                                 

 |   `-._`-._        _.-'_.-'    |          http://redis.io       

 `-._    `-._`-.__.-'_.-'    _.-'                                  

 |`-._`-._   `-.__.-'    _.-'_.-'|                                 

 |   `-._`-._        _.-'_.-'    |                                 

 `-._    `-._`-.__.-'_.-'    _.-'                                  

     `-._    `-.__.-'    _.-'                                      

         `-._        _.-'                                          

              `-.__.-'                                               

 

5998:M 18 Sep 01:06:36.665 # Serverstarted, Redis version 3.0.7

5998:M 18 Sep 01:06:36.665 * The server isnow ready to accept connections on port 6379

[root@server1 ~]# redis-server /etc/redis_6380.conf &

[2] 6001

[root@server1 ~]#                 _._                                                 

          _.-``__ ''-._                                            

     _.-``    `.  `_. ''-._           Redis 3.0.7(00000000/0) 64 bit

 .-`` .-```.  ```\/    _.,_ ''-._                                   

 (   '      ,       .-` | `,    )     Running in standalone mode

 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6380

 |   `-._   `._    /    _.-'    |     PID: 6001

 `-._    `-._  `-./ _.-'    _.-'                                   

 |`-._`-._   `-.__.-'    _.-'_.-'|                                 

 |   `-._`-._        _.-'_.-'    |          http://redis.io       

 `-._    `-._`-.__.-'_.-'    _.-'                                  

 |`-._`-._   `-.__.-'    _.-'_.-'|                                 

 |   `-._`-._        _.-'_.-'    |                                 

 `-._    `-._`-.__.-'_.-'    _.-'                                  

     `-._    `-.__.-'    _.-'                                      

         `-._        _.-'                                          

              `-.__.-'                                              

 

6001:M 18 Sep 01:07:03.936 # Serverstarted, Redis version 3.0.7

6001:M 18 Sep 01:07:03.936 * The server isnow ready to accept connections on port 6380

[root@server1 ~]# netstat -tnlp | grep redis

tcp       0      0 0.0.0.0:6379                0.0.0.0:*                   LISTEN      5998/redis-server *

tcp       0      0 0.0.0.0:6380                0.0.0.0:*                   LISTEN      6001/redis-server *

tcp       0      0 :::6379                     :::*                        LISTEN      5998/redis-server *

tcp       0      0 :::6380                     :::*                        LISTEN      6001/redis-server *

 

[root@server1 ~]# redis-cli -p 6379

127.0.0.1:6379> keys *

(empty list or set)

127.0.0.1:6379> set k1 v1

OK

127.0.0.1:6379> get k1

"v1"

127.0.0.1:6379> quit

[root@server1 ~]# redis-cli -p 6380

127.0.0.1:6380> keys *

(empty list or set)

127.0.0.1:6380> set k1 v1

OK

127.0.0.1:6380> get k1

"v1"

127.0.0.1:6380> quit

 

 

redis主从同步(http://www.redis.io/topics/replication):

wKioL1ffer-SiujnAAAeykOYouw962.jpg

Redis replication is a very simple to useand configure master-slave replication that allows slave Redis servers to beexact copies of master servers. The following are some very important factsabout Redis replication:

1Redis uses asynchronous replication. Starting with Redis 2.8,however, slaves periodically acknowledge the amount of data processed from thereplication stream.

2A master can have multiple slaves.

3Slaves are able to accept connections from other slaves. Aside fromconnecting a number of slaves to the same master, slaves can also be connectedto other slaves in a cascading-like structure.

4Redis replication is non-blocking on the master side. This meansthat the master will continue to handle queries when one or more slaves performthe initial synchronization.

5Replication is also non-blocking on the slave side. While the slaveis performing the initial synchronization, it can handle queries using the oldversion of the dataset, assuming you configured Redis to do so in redis.conf.Otherwise, you can configure Redis slaves to return an error to clients if thereplication stream is down. However, after the initial sync, the old datasetmust be deleted and the new one must be loaded. The slave will block incomingconnections during this brief window (that can be as long as many seconds forvery large datasets).

6Replication can be used both for scalability, in order to havemultiple slaves for read-only queries (for example, slow O(N) operations can beoffloaded to slaves), or simply for data redundancy.

7It is possible to use replication to avoid the cost of having themaster write the full dataset to disk: a typical technique involves configuringyour master redis.conf to avoid persisting to disk at all, then connect a slaveconfigured to save from time to time, or with AOF enabled. However this setupmust be handled with care, since a restarting master will start with an emptydataset: if the slave tries to synchronized with it, the slave will be emptiedas well.

 

In setups where Redis replication is used,it is strongly advised to have persistence turned on inthe masterEvery time data safety is important, andreplication is used with master configured without persistence, auto restart of instances should be disabled.

 

How Redis replication works

1If you set up a slave, upon connection it sends a PSYNC command.

2If this is a reconnection and the master has enough backlog, onlythe difference (what the slave missed) is sent. Otherwise what is called a fullresynchronization is triggered.

3When a full resynchronization is triggered, the master starts abackground saving process in order to produce an RDB file. At the same time itstarts to buffer all new write commands received from the clients. When thebackground saving is complete, the master transfers the database file to theslave, which saves it on disk, and then loads it into memory. The master willthen send all buffered commands to the slave. This is done as a stream ofcommands and is in the same format of the Redis protocol itself.

You can try it yourself via telnet. Connectto the Redis port while the server is doing some work and issue the SYNCcommand. You'll see a bulk transfer and then every command received by themaster will be re-issued in the telnet session.

Slaves are able to automatically reconnectwhen the master-slave link goes down for some reason. If the master receivesmultiple concurrent slave synchronization requests, it performs a singlebackground save in order to serve all of them.

 

Partial resynchronization

1Starting with Redis 2.8, master and slave are usually able tocontinue the replication process without requiring a full resynchronizationafter the replication link went down.

2This works by creating an in-memory backlog of the replicationstream on the master side. The master and all the slaves agree on a replicationoffset and a master run ID, so when the link goes down, the slave willreconnect and ask the master to continue the replication. Assuming the masterrun ID is still the same, and that the offset specified is available in thereplication backlog, replication will resume from the point where it left off.If either of these conditions are unmet, a full resynchronization is performed(which is the normal pre-2.8 behavior). As the run ID of the connected masteris not persisted to disk, a full resynchronization is needed when the slaverestarts.

3The new partial resynchronization feature uses the PSYNC commandinternally, while the old implementation uses the SYNC command. Note that aRedis slave is able to detect if the server it is talking with does not supportPSYNC, and will use SYNC instead.

 

slave-side

[root@server2 ~]# vim /etc/redis.conf   #salveof指定masterIPPORT;若在master上开启了requirepass选项,要在slave上开启masterauth,后跟master上设置的密码;slave-server-stale-datawhen a salveloses its connection with the master,or when the replication is still inprogress,the slave can act in two different ways:(1)default yes,the slave willstill reply to client requests,possibly with out of date data,or the data setmay just be empty if this is the first synchronization.(2)no,the slave will replywith an error "sync with master in progress" to all the kind ofcommands but to INFO and SLAVEOF

#################################REPLICATION #################################

salveof 10.96.20.113 6379

# masterauth <master-password>

slave-serve-stale-data yes

slave-read-only yes

#-------------------------------------------------------

# WARNING: DISKLESS REPLICATION ISEXPERIMENTAL CURRENTLY

#-------------------------------------------------------

repl-diskless-sync no

repl-diskless-sync-delay 5

# repl-ping-slave-period 10

# repl-timeout 60

repl-disable-tcp-nodelay no

# repl-backlog-size 1mb

# repl-backlog-ttl 3600

slave-priority 100

# min-slaves-to-write 3

# min-slaves-max-lag 10

 

master-side

[root@server1 ~]# redis-server /etc/redis.conf &

 

slave-side

[root@server2 ~]# redis-server /etc/redis.conf &

[1] 11669

[root@server2 ~]#                 _._                                                 

          _.-``__ ''-._                                            

     _.-``    `.  `_. ''-._           Redis 3.0.7(00000000/0) 64 bit

 .-`` .-```.  ```\/    _.,_ ''-._                                  

 (   '      ,       .-` | `,    )     Running in standalone mode

 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379

 |   `-._   `._    /     _.-'   |     PID: 11669

 `-._    `-._  `-./ _.-'    _.-'                                  

 |`-._`-._   `-.__.-'    _.-'_.-'|                                 

 |   `-._`-._        _.-'_.-'    |          http://redis.io       

 `-._    `-._`-.__.-'_.-'    _.-'                                  

 |`-._`-._   `-.__.-'    _.-'_.-'|                                 

 |   `-._`-._        _.-'_.-'    |                                 

 `-._    `-._`-.__.-'_.-'    _.-'                                   

     `-._    `-.__.-'    _.-'                                      

         `-._        _.-'                                          

              `-.__.-'                                              

 

11669:S 18 Sep 02:34:33.529 # Serverstarted, Redis version 3.0.7

11669:S 18 Sep 02:34:33.530 * The server isnow ready to accept connections on port 6379

11669:S 18 Sep 02:34:34.522 * Connecting toMASTER 10.96.20.113:6379

11669:S 18 Sep 02:34:34.522 * MASTER<-> SLAVE sync started

11669:S 18 Sep 02:34:34.523 * Non blockingconnect for SYNC fired the event.

11669:S 18 Sep 02:34:34.523 * Masterreplied to PING, replication can continue...

11669:S 18 Sep 02:34:34.523 * Partialresynchronization not possible (no cached master)

11669:S 18 Sep 02:34:34.524 * Full resyncfrom master: 545af66a04371d0fee71b683b2ad358adfd0d6a8:1

11669:S 18 Sep 02:34:34.629 * MASTER<-> SLAVE sync: receiving 18 bytes from master

11669:S 18 Sep 02:34:34.629 * MASTER<-> SLAVE sync: Flushing old data

11669:S 18 Sep 02:34:34.629 * MASTER<-> SLAVE sync: Loading DB in memory

11669:S 18 Sep 02:34:34.629 * MASTER<-> SLAVE sync: Finished with success

 

测试:

[root@server1 ~]# redis-cli   #(在masterw数据,到slave上查看)

127.0.0.1:6379> keys *

(empty list or set)

127.0.0.1:6379> set key1 value1

OK

127.0.0.1:6379> get key1

"value1"

 

[root@server2 ~]# redis-cli

127.0.0.1:6379> keys *

1) "key1"

127.0.0.1:6379> get key1

"value1"

127.0.0.1:6379> set key2 value2   #slaver不能w

(error) READONLY You can't write against a readonly slave.

127.0.0.1:6379>monitor   #(交互模式下在slave上开启监控,再在masterw数据,将会实时显示)

OK

1474191743.144953 [0 10.96.20.113:6379]"PING"

1474191753.242462 [0 10.96.20.113:6379]"PING"

1474191756.972746 [0 10.96.20.113:6379]"set" "key2" "value2"

1474191763.340888 [0 10.96.20.113:6379]"PING"