Redis基础入门
Redis是一个优秀的高性能分布式缓存服务器。
Redis简介
Redis是目前最流行的键值对(key-value)数据库,以出色的性能著称,官方提供的数据是可以支持100000以上的+QPS。Redis具有高性能的主要原因如下。
- Redis是基于内存的存储数据库,绝大部分的命令处理只是纯粹的内存操作,内存的读写速度非常快。
- Redis是单进程线程的服务(实际上一个正在运行的Redis Server肯定不止一个线程,但只有一个线程来处理网络请求),避免了不必要的上下文切换,同时不存在加锁/释放锁等同步操作。
- Redis使用多路I/O复用模型(select、poll、epoll),可以高效处理大量并发连接。
- Redis中的数据结构是专门设计的,增、删、改、查等操作相对简单。
Redis历史与发展
Redis(REmote DIctionary Server)是一个使用ANSI C编写的、开源的、支持网络的、基于内存的、可选持久化的键值对存储系统。
2008年,意大利的一家创业公司Merzia1推出了一款基于MySQL的网站实时统计系统LLOOGG2,然而没过多久该公司的创始人Salvatore Sanfilippo便开始对MySQL的性能感到失望,于是他决定亲自为LLOOGG量身定做一个数据库,并于2009年开发完成,这个数据库就是Redis。
不过Salvatore Sanfilippo并不满足只将Redis用于LLOOGG这一款产品,而是希望让更多的人使用它,于是在同一年Salvatore Sanfilippo将Redis开源发布,并开始和Redis的另一名主要的代码贡献者Pieter Noordhuis一起继续着Redis的开发,直到今天。
在2013年5月之前,Redis的开发由VMware赞助;2013年5月至2015年6月,由Pivotal赞助;从2015年6月起,Redis的开发由Redis Labs赞助。
Redis特性
存储结构
Redis是REmote DIctionary Server(远程字典服务器)的缩写,它以字典结构存储数据,并允许其他应用通过TCP协议读写字典中的内容。 Redis字典中的键值除了可以是字符串,还可以是其他数据类型。 Redis支持的键值数据类型如下:
- 字符串类型
- 散列类型
- 列表类型
- 集合类型
- 有序集合类型 这种字典形式的存储结构与常见的MySQL等关系数据库的二维表形式的存储结构有很大的差异。
内存存储与持久化
Redis数据库中的所有数据都存储在内存中。由于内存的读写速度远快于硬盘,因此Redis在性能上对比其他基于硬盘存储的数据库有非常明显的优势,在一台普通的笔记本电脑上,Redis可以在一秒内读写超过10万个键值。
将数据存储在内存中也有问题,比如程序退出后内存中的数据会丢失。不过Redis提供了对持久化的支持,即可以将内存中的数据异步写入到硬盘中,同时不影响继续提供服务。
功能丰富
Redis虽然是作为数据库开发的,但由于其提供了丰富的功能,越来越多的人将其用作缓存、队列系统等。Redis可谓是名副其实的多面手。
Redis可以为每个键设置生存时间(Time To Live,TTL),生存时间到期后键会自动被删除。这一功能配合出色的性能让Redis可以作为缓存系统来使用。
简单稳定
Redis直观的存储结构使得通过程序与Redis交互十分简单。在Redis中使用命令来读写数据。
Redis使用C语言开发,代码量只有3万多行。这降低了用户通过修改Redis源代码来使之更适合自己项目需要的门槛。
Redis可以用来做什么
Redis 是互联网技术领域使用最为广泛的存储中间件,它是「R emote Di ctionary S ervice」的首字母缩写,也就是「远程字典服务」。Redis 以其超高的性能、完美的文档、 简洁易懂的源码和丰富的客户端库支持在开源中间件领域广受好评。
国内外很多大型互联网公司都在使用 Redis,比如 Twitter、YouPorn、暴雪娱乐、Github、StackOverflow、腾讯、 阿里、京东、华为、新浪微博等等,很多中小型公司也都有应用。也可以说,对 Redis 的了解和应用实践已成为当下中高级后端开发者绕不开的必备技能。
由 Redis 面试想到的在面试后端工程师 Redis 技能的时候,面试官通常问的第一个问题就是“Redis 能用来做什么?”,第一个回答往往都会是「缓存」。缓存确实是 Redis 使用最多的领域,它相比 Memcache 而言更加易于理解、使用和控制。
可是如果再进一步问“还有呢?”,大多数同学就会开始皱眉头,只有一小部分人会回答「分布式锁」。如果你就分布式锁再深入问下去,他们基本就会开始摇头:我们项目里面 Redis 的锁方法都是别人(应该是架构师)封装好的,拿过来直接使用,内部细节没有去了 解过,也没有必要了解。
对类似的场景,我深有体会。因为关于 Redis 的面试题,之前准备了很多,但是真正能用上的却很少。当面试的同学频繁地回复「不知道、没用过」的时候,再继续深入追问已经
毫无意义,这时候就需要切换话题了。偶尔遇上几个能持续很多回合的同学,他们总能使人眼前一亮。如果再拓展一下周边知识点,就会发现这些人往往也会有所涉猎,这时我在心中已经暗暗地对这位同学伸出了大拇指。
这样的面试经历事后也让我深刻反思:架构师的技能很高,对提升团队研发效率很有帮助,我们非常钦佩和羡慕。但是普通开发者如果习惯于在架构师封装好的东西之上,只专注
于做业务开发,那久而久之,在技术理解和成长上就会变得迟钝甚至麻木。从这个角度看,架构师也可能成为普通开发者的“敌人”,他的强大能力会让大家变成“温室的花朵”,一旦遇到环境变化就会不知所措。
其实很多业务场景,如果仅仅是会使用某项技术、框架,那是再简单不过了。但随着业务发展,系统的用户量、并发量涨上来之后,现有系统的问题就会层出不穷地暴露出来。如果不能深入地了解系统、技术和框架背后的深层原理,很多问题根本无法理解到本质,更谈不上解决,临时抱佛脚也于事无补。
所谓「授人以鱼不若授人以渔」,本小册的初衷和目标就是帮助后端开发者较为深入的理解 Redis 背后的原理和实践经验,做到知其然也知其所以然,为未来进阶成长为架构师做好准备。
Redis安装与调试
我们以Linux环境为例来进行安装。
通过网址http://download.redis.io/releases/ 可以获得各个版本的Redis源码,本书以Redis 5.0为例,下载源码包并编译安装(源码包URL为http://download.redis.io/releases/redis-5.0.0.tar.gz )。
$ wget http://download.redis.io/releases/redis-5.0.0.tar.gz
$ tar -zxvf redis-5.0.0.tar.gz
$ cd redis-5.0.0
$ make
$ cd src
$make install
到此,我们完成了Redis 5.0的编译安装,生成的可执行文件在/usr/local/bin目录中:
redis-benchmark redis-check-aof redis-check-rdb redis-cli
redis-sentinel redis-server
其中redis-benchmark是官方自带的Redis性能测试工具;当AOF文件或者RDB文件出现语法错误时,可以使用redis-check-aof或者redis-check-rdb修复;redis-cli是客户端命令行工具,可以通过命令redis-cli-h{host}-p{port}连接到指定Redis服务器;redis-sentinel是Redis哨兵启动程序;redis-server是Redis服务端启动程序。
例如,使用redis-server启动服务端程序(默认监听端口是6379):
$ /usr/local/bin/redis-server
使用redis-cli连接Redis服务器并添加键值对:
$ redis-cli -h 127.0.0.1 -p 6379
127.0.0.1:6379> set name zhangsan
OK
127.0.0.1:6379> get name
"zhangsan"
GDB是一个由GNU开源组织发布的、UNIX/Linux操作系统下的、基于命令行的、功能强大的程序调试工具。下面我们演示如何通过GDB来调试Redis。
$ gdb /usr/local/bin/redis-server
(gdb)
使用b命令在main函数入口增加断点:
(gdb) b main
使用r命令运行:
(gdb) r
从上面的输出中可以看到,代码执行到main函数停止。接下来,使用n命令执行下一步:
(gdb) n
使用p命令查看某个变量的信息:
(gdb) p argc
Redis配置详解
redis.conf是Redis核心的配置文件,我们来看一下redis.conf中一些常用配置:
- bind 绑定redis服务器网卡IP,默认为127.0.0.1,即本地回环地址。 这样的话,访问redis服务只能通过本机的客户端连接,而无法通过远程连接。如果bind选项为空的话,那会接受所有来自于可用网络接口的连接。
- port 指定redis运行的端口,默认是6379。 由于Redis是单线程模型,因此单机开多个Redis进程的时候会修改端口。
- timeout 设置客户端连接时的超时时间,单位为秒。 当客户端在这段时间内没有发出任何指令,那么关闭该连接。默认值为0,表示不关闭。
- tcp-keepalive 单位是秒,表示将周期性的使用SO_KEEPALIVE检测客户端是否还处于健康状态,避免服务器一直阻塞,官方给出的建议值是300s,如果设置为0,则不会周期性的检测。
具体配置详解:
- daemonize 设置为yes表示指定Redis以守护进程的方式启动(后台启动)。默认值为 no
- pidfile 配置PID文件路径,当redis作为守护进程运行的时候,它会把 pid 默认写到 /var/redis/run/redis_6379.pid 文件里面
- loglevel
定义日志级别。默认值为notice,有如下4种取值:
- debug (记录大量日志信息,适用于开发、测试阶段)
- verbose (较多日志信息)
- notice (适量日志信息,使用于生产环境)
- warning(仅有部分重要、关键信息才会被记录)
- logfile 配置log文件地址,默认打印在命令行终端的窗口上
- databases
设置数据库的数目。默认的数据库是DB 0 ,可以在每个连接上使用select
命令选择一个不同的数据库,dbid是一个介于0到databases - 1 之间的数值。默认值是 16,也就是说默认Redis有16个数据库。 - save
这里是用来配置触发 Redis的持久化条件,也就是什么时候将内存中的数据保存到硬盘。默认如下配置:
save 900 1:表示900 秒内如果至少有 1 个 key 的值变化,则保存 save 300 10:表示300 秒内如果至少有 10 个 key 的值变化,则保存 save 60 10000:表示60 秒内如果至少有 10000 个 key 的值变化,则保存
当然如果你只是用Redis的缓存功能,不需要持久化,那么你可以注释掉所有的 save 行来停用保存功能。可以直接一个空字符串来实现停用:save “”
- stop-writes-on-bgsave-error 默认值为yes。当启用了RDB且最后一次后台保存数据失败,Redis是否停止接收数据。这会让用户意识到数据没有正确持久化到磁盘上,否则没有人会注意到灾难(disaster)发生了。如果Redis重启了,那么又可以重新开始接收数据了
- rdbcompression 默认值是yes。对于存储到磁盘中的快照,可以设置是否进行压缩存储。如果是的话,redis会采用LZF算法进行压缩。如果你不想消耗CPU来进行压缩的话,可以设置为关闭此功能,但是存储在磁盘上的快照会比较大。
- rdbchecksum 默认值是yes。在存储快照后,我们还可以让redis使用CRC64算法来进行数据校验,但是这样做会增加大约10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能。
- dbfilename 设置快照的文件名,默认是 dump.rdb
-
dir 设置快照文件的存放路径,这个配置项一定是个目录,而不能是文件名。使用上面的 dbfilename 作为保存的文件名。
- slave-serve-stale-data
默认值为yes。当一个 slave 与 master 失去联系,或者复制正在进行的时候,slave 可能会有两种表现:
- 如果为 yes ,slave 仍然会应答客户端请求,但返回的数据可能是过时,或者数据可能是空的在第一次同步的时候
- 如果为 no ,在你执行除了 info he salveof 之外的其他命令时,slave 都将返回一个 “SYNC with master in progress” 的错误
- slave-read-only 配置Redis的Slave实例是否接受写操作,即Slave是否为只读Redis。默认值为yes。
- repl-diskless-sync 主从数据复制是否使用无硬盘复制功能。默认值为no。
- repl-diskless-sync-delay 当启用无硬盘备份,服务器等待一段时间后才会通过套接字向从站传送RDB文件,这个等待时间是可配置的。 这一点很重要,因为一旦传送开始,就不可能再为一个新到达的从站服务。从站则要排队等待下一次RDB传送。因此服务器等待一段 时间以期更多的从站到达。延迟时间以秒为单位,默认为5秒。要关掉这一功能,只需将它设置为0秒,传送会立即启动。默认值为5。
- repl-disable-tcp-nodelay 同步之后是否禁用从站上的TCP_NODELAY 如果你选择yes,redis会使用较少量的TCP包和带宽向从站发送数据。但这会导致在从站增加一点数据的延时。 Linux内核默认配置情况下最多40毫秒的延时。如果选择no,从站的数据延时不会那么多,但备份需要的带宽相对较多。默认情况下我们将潜在因素优化,但在高负载情况下或者在主从站都跳的情况下,把它切换为yes是个好主意。默认值为no。
- rename-command
命令重命名,对于一些危险命令例如:
flushdb(清空数据库) flushall(清空所有记录) config(客户端连接后可配置服务器) keys(客户端连接后可查看所有存在的键)
作为服务端redis-server,常常需要禁用以上命令来使得服务器更加安全,禁用的具体做法是是:
rename-command FLUSHALL "" -- 也可以保留命令但是不能轻易使用,重命名这个命令即可: rename-command FLUSHALL abcdefg -- 这样,重启服务器后则需要使用新命令来执行操作,否则服务器会报错unknown command。
- requirepass 设置redis连接密码 比如: requirepass 123 表示redis的连接密码为123.
- maxclients 设置客户端最大并发连接数,默认无限制,Redis可以同时打开的客户端连接数为Redis进程可以打开的最大文件。 描述符数-32(redis server自身会使用一些),如果设置 maxclients为0 。表示不作限制。当客户端连接数到达限制时,Redis会关闭新的连接并向客户端返回max number of clients reached错误信息
- maxmemory 设置Redis的最大内存,如果设置为0 。表示不作限制。通常是配合下面介绍的maxmemory-policy参数一起使用。
- maxmemory-policy
当内存使用达到maxmemory设置的最大值时,redis使用的内存清除策略。有以下几种可以选择:
- volatile-lru 利用LRU算法移除设置过过期时间的key (LRU:最近使用 Least Recently Used )
- allkeys-lru 利用LRU算法移除任何key
- volatile-random 移除设置过过期时间的随机key
- allkeys-random 移除随机ke
- volatile-ttl 移除即将过期的key(minor TTL)
- noeviction noeviction 不移除任何key,只是返回一个写错误 ,默认选项
- maxmemory-samples LRU 和 minimal TTL 算法都不是精准的算法,但是相对精确的算法(为了节省内存)。随意你可以选择样本大小进行检,redis默认选择3个样本进行检测,你可以通过maxmemory-samples进行设置样本数。
- appendonly 默认redis使用的是rdb方式持久化,这种方式在许多应用中已经足够用了。但是redis如果中途宕机,会导致可能有几分钟的数据丢失,根据save来策略进行持久化,Append Only File是另一种持久化方式, 可以提供更好的持久化特性。Redis会把每次写入的数据在接收后都写入appendonly.aof文件,每次启动时Redis都会先把这个文件的数据读入内存里,先忽略RDB文件。默认值为no。
- appendfilename aof文件名,默认是”appendonly.aof”
- appendfsync aof持久化策略的配置;no表示不执行fsync,由操作系统保证数据同步到磁盘,速度最快;always表示每次写入都执行fsync,以保证数据同步到磁盘;everysec表示每秒执行一次fsync,可能会导致丢失这1s数据
- no-appendfsync-on-rewrite 在aof重写或者写入rdb文件的时候,会执行大量IO,此时对于everysec和always的aof模式来说,执行fsync会造成阻塞过长时间,no-appendfsync-on-rewrite字段设置为默认设置为no。如果对延迟要求很高的应用,这个字段可以设置为yes,否则还是设置为no,这样对持久化特性来说这是更安全的选择。 设置为yes表示rewrite期间对新写操作不fsync,暂时存在内存中,等rewrite完成后再写入,默认为no,建议yes。Linux的默认fsync策略是30秒。可能丢失30秒数据。默认值为no。
- auto-aof-rewrite-percentage 默认值为100。aof自动重写配置,当目前aof文件大小超过上一次重写的aof文件大小的百分之多少进行重写,即当aof文件增长到一定大小的时候,Redis能够调用bgrewriteaof对日志文件进行重写。当前AOF文件大小是上次日志重写得到AOF文件大小的二倍(设置为100)时,自动启动新的日志重写过程。
- auto-aof-rewrite-min-size 64mb。设置允许重写的最小aof文件大小,避免了达到约定百分比但尺寸仍然很小的情况还要重写。
- aof-load-truncated aof文件可能在尾部是不完整的,当redis启动的时候,aof文件的数据被载入内存。重启可能发生在redis所在的主机操作系统宕机后,尤其在ext4文件系统没有加上data=ordered选项,出现这种现象 redis宕机或者异常终止不会造成尾部不完整现象,可以选择让redis退出,或者导入尽可能多的数据。如果选择的是yes,当截断的aof文件被导入的时候,会自动发布一个log给客户端然后load。如果是no,用户必须手动redis-check-aof修复AOF文件才可以。默认值为 yes。
- lua-time-limit 一个lua脚本执行的最大时间,单位为ms。默认值为5000.
- cluster-enabled 集群开关,默认是不开启集群模式。
- cluster-config-file 集群配置文件的名称,每个节点都有一个集群相关的配置文件,持久化保存集群的信息。 这个文件并不需要手动配置,这个配置文件有Redis生成并更新,每个Redis集群节点需要一个单独的配置文件。请确保与实例运行的系统中配置文件名称不冲突。默认配置为nodes-6379.conf。
- cluster-node-timeout 可以配置值为15000。节点互连超时的阀值,集群节点超时毫秒数
- cluster-slave-validity-factor 可以配置值为10。在进行故障转移的时候,全部slave都会请求申请为master,但是有些slave可能与master断开连接一段时间了, 导致数据过于陈旧,这样的slave不应该被提升为master。该参数就是用来判断slave节点与master断线的时间是否过长。判断方法是:比较slave断开连接的时间和(node-timeout * slave-validity-factor) + repl-ping-slave-period 如果节点超时时间为三十秒, 并且slave-validity-factor为10,假设默认的repl-ping-slave-period是10秒,即如果超过310秒slave将不会尝试进行故障转移
- cluster-migration-barrier 可以配置值为1。master的slave数量大于该值,slave才能迁移到其他孤立master上,如这个参数若被设为2,那么只有当一个主节点拥有2 个可工作的从节点时,它的一个从节点会尝试迁移。
- cluster-require-full-coverage 默认情况下,集群全部的slot有节点负责,集群状态才为ok,才能提供服务。 设置为no,可以在slot没有全部分配的时候提供服务。不建议打开该配置,这样会造成分区的时候,小分区的master一直在接受写请求,而造成很长时间数据不一致。
参考资料
Redis官网:https://redis.io/
源码地址:https://github.com/redis/redis
Redis在线测试:http://try.redis.io/
Redis命令参考:http://doc.redisfans.com/