Redis基础入门

2018/02/01 Redis

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/

Search

    微信好友

    博士的沙漏

    Table of Contents