目录
数据库按照数据库的结构可以分为关系型数据库与其他数据库,而这些其他
数据库我们将其统称为非关系型数据库。
关系型数据库是一个结构化的数据库,创建在关系模型基础上,一般面向于
记录。它借助于集合代数等数学概念和方法来处理数据库中的数据。关系模型就
是指二维表格模型,因而一个关系型数据库就是由二维表及其之间的联系组成的
一个数据组织。现实世界中,各种实体与实体之间的各种联系都可以用关系模型
来表示。SQL语句(标准数据查询语言)就是一种基于关系型数数据库的语言,用
于执行对关系型数据库中数据的检索和操作。主流的关系型数据库包括Oracle、MySQL、SQL Server、Micrrosoft AccessDB2等等
NoSQL(NoSQL = Not Only SQL),意思是"不仅仅是SQL",是非关系型数
据库的总称。主流的NoSQL数据库有Redis、MongBD、Hbase、CouhDB等等。
以上这些非关系型数据库,他们的存储方式、存储结构以及使用的的场景都是完全
不同的。所以我们认为它是一个非关系型数据库的集合,而不是像关系型数据库
一样,是一个统称。换言之,除了主流的关系型数据库以外的数据库,都可以认
为是非关系型的。NoSQL数据库凭借着其非关系型、分布式、开源及横向扩展等
优势,被认为是下一代数据库产品
随着Web2.0网站的兴起,关系型数据库在应应对Web2.0网站,特别是海量
数据和高并发的SNS(Social Networking Services,即社交网网络服务)类型的
Web2.0纯动态网站时,暴露出很多难以解决的问题,例如三高高问题
(1)High performance--对数据库高并发读写需求
Web2.0网站会根据用户的个性化信息来实时生成动态页面和提供动态信息,
因此无法使用动态页面静态化技术。所以数据库的并发负载非?常高,一般会达到
10000次/s以上的读写请求。关系型数据库对于上万次的查询请求还是可以勉
强支撑的,但出现上万次的写数据请求,硬盘I0就已经无法承受了。对于普通
的BBS网站,往往也会存在高并发的写数据请求。
(2)HugeStorage--对海量数据高效存储与访问需求
类似于Facebook、Friendfeed这样的SNS网站,每天会产生生大量的用户
动态信息。如Friendfeed,一个月就会产生不少于2.5亿条用户动态信息,对
于关系型数据库来说,在一个包含2.5亿条记录的表中执行SQL查询,查询效
率是非常低的。(3) High Scalability & High Availability-
对数据库高可扩展性与高
可用性需求在Web架构中,数据库是最难进行横向扩展的。当应用系统的用
户量与访问量与日俱增时,数据库是没办法像Web服务一样,简单地通过添加
硬件和服务器节点来扩展其性能和负载能力的。尤其对于一些需要24小时对外
提供服务的网站来说,数据库的升级与扩展往往伴随着停机维护与数据迁移,其
工作量是非常庞大的。关系型数据库和非关系型数据库都有各自的特点与应用场景,两有的紧密结合将会给Web2.0的数据库发展带来新的思路。让关系数据库关注在关系上,非关系型数据库关注在存储上。例如,在读写分离的MySQL数据库环境中,可以把
经常访问的数据存储在非关系型数据库中,提升访问速度。
Redis服务器程序是单进程模型,也就是在一台服务器上可以同时启动多个
Redis进程,而Redis的实际处理速度则是完全依靠于主进程的执行效率。若
在服务器上只运行一个Redis进程,当多个客户端同时访问时,服务器的处理
能力是会有一定程度的下降;若在同一台服务器上开启多个Redis进程,Redis
在提高并发处理能力的同时会给服务器的CPU造成很大压力。即:在实际生产
环境中,需要根据实际的需求来决定开启多少个Redis进程。若对高并发要求
更高一些,可能会考虑在同一台服务器上开启多个进程。若CPU资源比较紧张,
采用单进程即可。具有极高的数据读写速度,数据读取的速度最高可达到1100000次/s,数据写入速度最高可达到81000次/s。支持丰富的数据类型,不仅仅支持简单的key-value类型的数据,还支持Strings,Lists,Hashes,Sets及Ordered Sets等数据类型操作
支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再
次加载进行使用。原子性,Redis所有操作都是原子性的。支持数据备份,即master-salve模式的数据备份。Redis作为基于内存运行的数据库,缓存是其最常应用的场景之一。除比之外,Redis常见应用场景还包括获取最新N个数据的操作、排行于榜类应用、计
数器应用、存储关系、实时分析系统、日志记录。
Redis的安装相对于其他服务来说比较简单。首先需要到Redis官网(htt
ps://www.redis.io)下载相应的源码软件包,然后上传至Linux系统的服务器
中进行解压、安装。本章中以redis-4.0.9.tar.gz为例进行Redis服务的安
装和配置讲解。
通常情况下,在Linux系统中进行源码编译安装,需要先执行亍./configure
进行环境检查与配置,从而生成Makefile文件,再执行makte & make install
命令进行编译安装。而Redis源码包中直接提供了Makefile文件,所以在解
压完软件包后,可直接进入解压后的软件包目录,执行make 与make install
命令进行安装。
[root@localhost src]# dnf -y install tar gcc make
[root@localhost src]# tar xvzf redis-4.0.9. tar. gz
[root@localhost src]# cd redis-4.0.9/
[root@localhost_redis-4.0.9]# make
[root@localhost redis-4.0.9]# make PREFIX=/usr/local/redis install
[root@localhost `]# ln -s /usr/local/redis/bin/* /ussr/local/bin/
makeinstall只是安装了二进制文件到系统中,并没有启动脚本和配置文件。
软件包中默认提供了一个install_server.sh脚本文件,通过该脚本文件可以
设置Redis服务所需要的相关配置文件。当脚本运行完毕,Redis服务就已经
启动,默认侦听端口为6379。
[root@localhost redis-4.0.9]# cd utils,
[root@localhost utils]# ./install_server.sh
[root@localhost_utils]# netstat -lnupt | grep redis
tcp 0 0 127.0.0.1:6379 0.0.0.0.0:* LISTEN 5494/rediis-server 1
Redis主配置文件为/etc/redis/6379.conf,由注释行与设置置行两部分组成
与大多数Linux配置文件一样,注释性的文字以"#"开始,包含了对相关配置
内容进行的说明和解释。除了注释行与空行以外的内容即为设置行。可根据生产
环境的需求调整相关参数,如下:
[root@localhost]# vi /etc/redis/6379.conf
bind 127.0.0.1 192.168.10.161//监听的主机地址
port 6379//端口
daemonizeyes//启用守护进程
pidfile/var/run/redis_6379.pid//指定PID文件
loglevel notice//日志级别
logfile/var/log/redis_6379.log//指定日志文件
[root@localhost~]#/etc/init.d/redis 6379 restart
Stopping...
Redis stopped
Starting Redis server...
参数 | 作用 |
timeout 300 | 当客户端闲置多长时间后关闭连接,如果指定为0, 表示关闭该功能 |
dbfilename dump.rdb | 指定本地数据库文件名,默认值为dump.rdb |
dir/var/lib/redis/6379 | 指定本地数据库存放目录 |
maxclients 10000 | 设置同一时间最大客户端连接数,默认为10000。 Redis可以同时打开的客户端连接数为Redis进程可 以打开的最大文件描述符数,如果设置maxclients 0,表示不限制。当客户端连接数到达限制时,Redis 会关闭新的连接并向客户端返回maxnumberof clients reached 错误信息 |
rdbcompression yes | 指定存储至本地数据库时是否压缩数据,默认为yes, Redis采用LZF压缩,如果为了节省CPU资源,可 以关闭该选项,但会导致数据库文件变的巨大 |
slaveof <masterip><masterport><masterport> | 当本机为从服务器时,设置主服务的IP地址及端口。 在Redis启动时,从服务器会自动从主服务进行数据 同步 |
masterauth <master-password> | 当主服务设置了密码保护时,从服务连接主服务的密码 |
requirepass foobared | 设置 Redis连接密码,如果配置了连接密码,客户端 在连接Redis时需要通过AUTH<password>命令提 供密码,默认关闭 |
maxmemory <bytes> | 指定Redis最大内存限制。Redis在启动时会把数据 加载到内存中,达到最大内存后,Redis会先尝试清 除已到期或即将到期的Key,当此方法处理后,仍然 到达最大内存设置,将无法再进行写入操作,但仍然 |
可以进行读取操作。Redis新VM机制,会把Key存 放内存,Value会存放在Swap分区 | |
appendonly no | 指定是否在每次更新操作后进行日志记录,Redis在 默认情况下是异步地把数据写入磁盘,如果不开启, 可能会在断电时导致一段时间内的数据丢失。因为 Redis本身同步数据文件是按上面save条件来同步 的,所以有的数据会在一段时间内只存在于内存中。 默认为no |
appendfilename appendonly.aof | 指定更新日志文件名,默认为appendonly.aof |
appendfsync everysec | 指定更新日志条件,共有3个可选值: no:表示等操作系统进行数据缓存同步到磁盘(快) always:表示每次更新操作后手动调用fsync()将数据 写到磁盘(慢,安全 everysec:表示每秒同步一次(折衷,默认值) |
activerehashing yes | 指定是否激活重置哈希,默认为开启 |
include/path/to/local.conf | 指定包含其它的配置文件,可以在同一主机上多个 Redis实例之间使用同一份配置文件,而同时各个实 例又拥有自己的特定配置文件 |
redis-server:用于启动 Redis的工具;
redis-benchmark:用于检测Redis在本机的运行效率;
redis-check-aof:修复AOF持久化文件;
redis-check-rdb:修复RDB持久化文件;
redis-cli:Redis 命令行工具。
[root@localhost ^]# /usr/local/redis/bin/redis-cli //连接本机Redis数
据库
127.0.0.1:6379>ping//检测 redis服务是否启动
PONG
127.0.0.1:6379)
[root@localhost ^]#redis-cli -h 192.168.10.161 -p 6379
192.168.10.161:6379>info
# Server
redis_version:4.0.9
redis_git_shal:00000000
redis_git_dirty:0
redis_build_id:7f55f2cla630cbe5
192.168.10.161:6379>exit
-h:指定服务器主机名;
-p:指定服务器端口;
-s:指定服务器socket;
-c:指定并发连接数;
-n:指定请求数;
-d:以字节的形式指定SET/GET值的数据大小;
-k: 1=keep alive 0=reconnect;
-r:SET/GET/INCR使用随机 key,SADD使用随机值;
-P:通过管道传输<numreq>请求;
-q:强制退出redis。仅显示query/sec值;
--csv:以CSV格式输出;
-1:生成循环,永久执行测试;
-t:仅运行以逗号分隔的测试命令列表;
-I:Idle模式。仅打开 N个 idle连接并等待。
结合上述选项,可以针对某台Redis服务器进行性能检测,如执行
redis-benchmark -h 192.168. 10.161 -p 6379 -c 100 -n 100000命令即可向IP
地址为192.168.10.161、端口为6379的Redis服务器发送100个并发连接
与100000个请求测试性能。
[root@localhost ^]# redis-benchmark -h 192.168.10.161 -p 6379 -c 100 -n
100000
8225.04 requests per second
MSET (10 keys) =======
100000 requests completed in 1.57 seconds
100 parallel clients
3 bytes payload
keep alive: 1
24.75% <= 1 milliseconds
<= 2 milliseconds
99.02%
99.57% <= 3 milliseconds
99.90% <= 4 milliseconds
99.98% <= 5 milliseconds
100.00% <= 5 milliseconds
63653.72 requests per second
执行redis-benchmark-h 192.168.10.161-p 6379 -p -d 100命令的作用是
测试存取大小为100字节的数据包的性能。
[root@localhost *]#redis-benchmark -h 192.168. 168. 10.161 -p 6379 -q -d 100
PING_INLINE: 88261.25 requests per second
PING_BULK: 90991.81 requests per second
SET: 83612.04 requests per second
GET: 84961.77 requests per second
INCR: 83682.01 requests per second
LPUSH: 76745.97 requests per second
RPUSH: 78247.26 requests per second
LPOP: 77519.38 requests per second
RPOP: 79681.27 requests per second
SADD: 83125.52 requests per second
SPOP: 85543.20 requests per second
LPUSH (needed to benchmark LRANGE) : 78864.35 requestsper second
(first 100 elements): 30931.02 requests per second
LRANGE 100
LRANGE 300
(first 300 elements): 9437.52 requests per second
LRANGE_500 (first 450 elements): 5541.39 requests persecond
LRANGE_600 (first 600 elements): 3824.38 requests persecond
MSET (10 keys) : 64184.86 requests per second
set:存放数据,基本的命令格式为setkeyvalue。
get:获取数据,基本的命令格式为 get key。
keys exists del type rename renamenx dbsize
Redis支持多数据库,Redis在没有任何改动的情况下默认包含16个数据
库,数据库名称是用数字0-15来依次命名的。使用select命令可以进行
Redis的多数据库之间的切换,命令格式为selectindex,其中index表示数据库的序号。而使用redis-cli连接Redis数据库后,默认使用的是序号为0的
数据库。如下所示,使用select命令切换数据库后,会在前端的提示符中显示当前
所在的数据库序号如"127.0.0.1:6379[10]>"表示当前使用的是序号为10的
数据库。若当前使用的数据库是序号为0的数据库,提示符中则不显示序号,
如"127.0.0.1:6379>"表示当前使用的是序号为0的数据库。Redis的多数据库在一定程度上是相对独立的,例如在数据库0上面存放
kl的数据,在其它1-15的数据库上是无法查看到的。Redis数据库提供了一个move的命令,可以进行多数据库之间的数据移动。命
令的基本语法格式为"movekeydbindex"。其中"key"表示当前数据库的目
标键,"dbindex"表示目标数据库的序号,具体操作方法如下所示。(3)清除数据库内数据
Redis数据库的整库数据删除主要分为两个部分:清空当前数据库数据,使用
FLUSHDB命令实现;清空所有数据库的数据,使用FLUSHALL命令实现。但是,
数据清空操作比较危险,生产环境下一般不建议使用。
Redis是一种高级key-value数据库。它跟Memcached类似,不过数据可
以持久化,而且支持的数据类型很丰富,有字符串、列表、集合和有序集合。支
持在服务器端计算集合(difference)等,还支持多种排序功能。所以Redis也
可以被看成是一个数据结构服务器。
Redis的所有数据都是保存在内存中,然后不定期的通过异步方式保存到磁
盘上(这称为"半持久化模式");也可以把每一次数据变化都写有人到一个 append
onlyfile(aof)里面(这称为"全持久化模式")。
由于Redis的数据都存放在内存中,如果没有配置持久化,Redis重启后
数据就全丢失了。所以,需要开启Redis的持久化功能,将数据保存到磁盘上,
当Redis重启后,可以从磁盘中恢复数据。Redis提共两种方式进行持久化,
一种是RDB持久化(原理是将Reids在内存中的数据库记录定时dump到磁盘
上的RDB持久化),另外一种是AOF(appendonlyfile)持久化(原理是将Reids
的操作日志以追加的方式写入文件)。那么这两种持久化方式有什么区别呢?在
实际使用的时候该如何选择呢?下面简单介绍一下二者的区别。
RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,实际
操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件AOF持久化以日志的形式记录服务器所处理的每一个写、册删除操作,查询操
作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录
一旦采用该方式,那么整个Redis数据库将只包含一个文件,这对于文件
备份而言是非常完美的。比如,计划每个小时归档一次最近24小时的数据,同
时还要每天归档一次最近30天的数据。通过这样的备份策略,一旦系统出现灾
难性故障,可以非常容易地进行恢复。
对于灾难恢复而言,RDB是非常不错的选择。可以非常轻松的将一个单独的
文件压缩后再转移到其它存储介质上。
性能最大化,对于Redis的服务进程而言,在开始持久化时,它唯一需要做
的只是fork出子进程,之后再由子进程完成这些持久比的工作,这样就可以极
大的避免服务进程执行 10操作了。
相比于AOF机制,如果数据集很大,RDB的启动效率会更高。
如果想保证数据的高可用性,即最大限度的避免数据丢失,那么RDB将不是
一个很好的选择。因为系统一旦在定时持久化之前出现宕机现象象,此前没有来得
及写入磁盘的数据都将丢失。
由于RDB是通过fork子进程来协助完成数据持久化工作的,因此当数据集
较大时,可能会导致整个服务器停止服务几百毫秒,甚至是1秒钟。
AOF机制可以带来更高的数据安全性,即数据持久性。Redis中提供了3种
同步策略,即每秒同步、每次修改同步和不同步。事实上,每秒同步也是异步完
成的,其效率也是非常高的,弊端是一旦系统出现宕机现象,那么这一秒钟之内
修改的数据将会丢失。而每次修改同步,可以将其视为同步寺久化,即每次发生
的数据变化都会被立即记录到磁盘中,这种方式在效率上是:最低的。
由于该机制对日志文件的写入操作采用的是append模式,因此在写入过程
中即使出现 宕机现象,也不会破坏日志文件中已经存在的内容。然而如果本次
操作只是写入了一半数据就出现了系统崩溃问题,那么在Redis下一次启动之
前,可以通过 redis-check-aof工具来解决数据一致性的问题。
如果日志过大,Redis可以自动启用 rewrite机制。即Redis以append模
式不断地将修改数据写入到老的磁盘文件中,同时Redis记还会创建一个新的文
件用于记录此期间有哪些修改命令被执行。因此在进行rewrite切换时可以更
好的保证数据安全性。
AOF包含一个格式清晰、易于理解的日志文件用于记录所有的修改操作。事
实上,也可以通过该文件完成数据的重建。
对于相同数量的数据集而言,AOF文件通常要大于RDB文件牛。RDB在恢复大
数据集时的速度比AOF的恢复速度要快。
根据同步策略的不同,AOF在运行效率上往往会慢于RDB。每秒同步策略的
效率是比较高的,同步禁用策略的效率和RDB一样高效。
二者选择的标准,就是看系统是愿意牺牲一些性能,换取更高的缓存一致性
(AOF),还是愿意写操作频繁的时候,不启用备份来换耶双更高的性能,待手动
运行save的时候,再做备份(RDB)。
Redis会将数据集的快照dump到dump.rdb文件中。此外,也可以通过配
置文件来修改Redis服务器dump快照的频率。在打开6379.conf文件之后,
搜索save,可以看到如下所示配置信息。
save 900 1:在900秒(15分钟)内,如果至少有1个 key发生变化
则dump内存快照。
save30010:在300秒(5分钟)内,如果至少有10个key发生变化,
则dump内存快照。save60 10000:在60秒(1分钟)内,如果至少有10000个key艺生变
化,则dump内存快照。
appendonlyno改为yes开启aof。
在Redis的配置文件中存在三种同步方式,它们分别是:
appendfsyncalways:每次有数据修改发生时都会写入AOF文件#。
appendfsynceverysec:每秒钟同步一次,该策略为AOF的每快省策略。
appendfsync no:从不同步,高效但是数据不会被持久化。
Redis会不断地将被执行的命令记录到AOF文件里面,所以随着Redis不
断运行,AOF文件的体积也会不断增长。在极端情况下,体积不不断增大的AOF文
件甚至可能会用完硬盘的所有可用空间。Redis在重启之后需要通过重新执行
AOF文件记录的所有写命令来还原数据集,所以如果AOF文件的体积非常大,
那么还原操作执行的时间就可能会非常长。
为了解决 AOF 文件体积不断增大的问题,用户可以向Redis发送
BGREWRITEAOF命令。BGREWRITEAOF 命令会通过移除AOF 文件中的冗余命令来
重写(rewrite)AOF文件,使AOF文件的体积尽可能地变小。
BGREWRITEAOF的工作原理和BGSAVE创建快照的工作原理非常相似:Redis
会创建一个子进程,然后由子进程负责对AOF文件进行重写。因为AOF文件重
写也需要用到子进程,所以快照持久化因为创建子进程而导致的性能问题和内存
占用问题,在AOF持久化中也同样存在。
与快照持久化通过设置save选项来自动执行BGSAVE样,AOF持久化也
可以通过设置auto-aof-rewrite-percentage选项和auto-aof-rewrite
min-size选项来自动执行BGREWRITEAOF。
Redis性能管理需要关注的数据指标有内存使用率、内存碎片率、回收 key
等。这其中有些数据可以通过进入info命令进行查看。需要查看某一项的值就
后面跟具体参数,执行以下命令查看Redis使用内存值直
192.168.9.236:7001>infomemory
# Memory
used_memory:1789108864
used_memory human:1.67G
used_memory_rss: 1834389504
used_memory_rss_human:1.71G
used_memory_peak:4657473880
used_memory_peak_human:4.34G
used_memory_peak_perc:38.41%
used_memory overhead:626859900
used_memory_startup:791432
used_memory dataset:1162248964
used_memory_dataset_perc:64.99%
allocator_allocated:1789471472
allocator_active:1804185600
allocator resident:1853542400
total_system_memory: 100953268224
total system_memory human:94.02G
used_memory_lua:37888
used_memory_lua_human:37.00K
used_memory scripts:0
used_memory_scripts_human:OB
number_of_cached_scripts:0
maxmemory:17179869184
maxmemory_human: 16.00G
maxmemory_policy:allkeys-lru
allocator_frag_ratio:1.01
allocator_frag_bytes:14714128
allocator_rss_ratio:1.03
allocator_rss_bytes:49356800
rss_overhead ratio:0.99
rss_overhead_bytes:-19152896
mem_fragmentation_ratio:1.03
mem_fragmentation_bytes:45321664
mem_not_counted_for_evict:0
mem_replication backlog:536870912
mem_clients_slaves:16922
mem_clients_normal:218914
mem aof buffer:0
mem allocator: jemalloc-5.1.0
active_defrag_running:0
lazyfree_pending objects:0
除了用户定义的数据和内部开销以外,used_memory_rss指标还包含了内存
碎片的开销,内存碎片是由操作系统低效的分配/回收物理内存导致的操作系
统负责分配物理内存给各个应用进程,Redis使用的内存与物理内存的映射是由
操作系统上虚拟内存管理分配器完成的。
举例来说:Redis需要分配连续内存块来存储1G的数据集。如果物理内存
上没有超过1G的连续内存块,那操作系统就不得不使用多个不连续的小内存块
来分配并存储这1G数据,该操作就会导致内存碎片的产生三。
内存分配器另一个复杂的层面是,系统会预先分配一下内存区块应用程序
这样可以加块应用程序运行速度,但会产生另外一个问题,就是预先分配内存区
块大小的问题。比如运行一个5G大小应用,需要5个1G大小的内存区块,
结果系统就分配了5个2G的内存区块,这样就浪费了5G的运行内存空间,
同样就产生内存碎片率。
内存碎片率对理解Redis实例的资源性能是非常重要的。内存碎片率稍大
于1是合理的,这个值表示内存碎片率比较低,也说明Redis没有发生内存交
换。但如果内存碎片率超过1.5,那就说明Redis消耗了实际需要物理内存的
150%,其中50%是内存碎片率。若是内存碎片率低于1的话,说明Redis内存
分配超出了物理内存,操作系统正在进行内存交换。内存交换会引起非常明显的
响应延迟。若生产环境内存碎片率过高,会导致Redis性能降低。解决该情况的常见方案有三种。内存碎片率超过1.5重启Redis服务器可以让额外产生的内存碎片失效并重新作为新内存来使用,使操作系统恢复高效的内存管理。额外碎片的产生是由于Redis释放了内存块,但内存分配器并没有返回内存给操作系统,这个内存分配器是在编译时指
定的,可以是libc、jemalloc或者tcmalloc 。
通过比较used_memory_peak, used_memory_rss #fa used_memory_metrics p的数据指标值可以检查额外内存碎片的占用。从名字上可以看出,used_memory_peak是过去
Redis内存使用的峰值,而不是当前使用内存的值。如果useed_memory_peak和
used_memory_rss的值大致上相等,而且二者明显超过
了used_memory值,这说明额外的内存碎片正在产生。在重启服务器之前,需
要在redis-cli工具上输入shutdownsave命令,表示强制让Redis数据库
执行保存操作并关闭Redis服务,这样做能保证在Redis关闭时不不丢失任何数
据。在重启后Redis会从硬盘上加载持久化的文件,以确保数据集持续可用。
如果内存碎片率低于1
Redis实例可能会把部分数据交换到硬盘上。内存交换会严重影响Redis的
性能,所以应该增加可用物理内存或减少Redis内存占用。
修改内存分配Redis支持 libc、jemallocll、tcmalloc 三种不同的内存分配器,每个分
配器在内存分配和碎片上都有不同的实现。不建议运维人员修改Redis默认内
存分配器,因为这需要完全理解这几种内存分配器的差异,也要重新编译Redis。
这个方法更多的是让其了解Redis内存分配器所做的工作。
内存使用率是Redis服务最关键的一部分。如果一个Redis实例的内存使
用率超过可用最大内存,那么操作系统开始进行内存与swap空间交换,把内存
中旧的或不再使用的内容写入硬盘上(硬盘上的这块空间叫swap分区),以便
腾出新的物理内存给新页或活动页(page)使用。
used_memory字段数据表示的是由Redis分配器分配的内存总量,以字节
为单位。共中used_memory_human上的数据和used_memory是一样的值,它以
M为单位显示,目的是为了方便阅读。
used_memory是Redis使用的内存总量,它包含了实际缓存占用的内存和
Redis自身运行所占用的内存(如元数据、lua)。它是由Redis使用内存分配器
分配的内存,所以这个数据并没有把内存碎片浪费掉的内存给统计进去。Redis
默认最大使用内存是可用物理内存剩余的所有内存,0代表没有限限制
在硬盘上进行读写操作要比在内存上进行读写操作慢很多。如果Redis进
程上发生内存交换,那么Redis和依赖Redis上数据的应用用会受到严重的性能
影响。通过查看used_memory指标可知道Redis正在使用用的内存情况,如果
used_memory大于可用最大内存,那就说明Redis实例正在进行内存交换或者
已经完成内存交换。运维人员应该根据这个情况执行相应的应急措施。
如果缓存数据小于4GB,就使用32位的Redis实例。因为32位实例上
的指针大小只有64位的一半,它的内存空间占用空间会更少些。这样有一个
坏处就是,假设物理内存超过4GB,那么32位实例能使用的内存仍然会被限制
在4GB以下。要是实例同时也共享给其他一些应用使用的话,那可能需要更高
效的64位Redis实例,这种情况下切换到32位是不可取的。不管使用哪种
方式,Redis的dump文件在32位和64位之间是互相兼容的,因此倘若有减
少占用内存空间的需求,可以尝试先使用32位,后面再切换了到64位上。
因为Redis在储存小于100个字段的Hash结构上,其存储效率是非常高
的。所以在不需要集合(set)操作或list的push/pop操作时,房可能的使用
Hash结构。例如在一个Web应用程序中,需要存储一个对象表示用户信息,使
用单个key表示一个用户,其每个属性存储在Hash的字段里,这样要比给每
个属性单独设置一个key-value要高效的多。通常情况下下倘若有数据使用
string结构,用多个key存储时,那么应该转换成单key多字段的Hash结
构。如上述例子中介绍的Hash结构应包含单个对象的属性或者单个用户各种各
样的资料。Hash结构的操作命令是HSET(key,fields,vallue)和HGET(key,
field),使用它可以存储或从Hash中取出指定的字段。
一个减少内存使用率的简单方法就是,每当存储对象时确保设置key的过
期时间。倘若key在明确的时间周期内使用或者旧key不大可能皮使用时,就
可以用Redis过期时间命令(expire,expireat,pexpire,pexpireat)去设置过
期时间,这样Redis会在key过期时自动删除key。假如知道每秒钟有多少
个新key-value被创建,那可以调整 key的存活时间,并指定阀值去限制
Redis使用的最大内存。
当内存使用达到设置的最大阀值时,需要选择一种 key的回收策略,可在
redis.conf配置文件中修改"maxmemory-policy"属性值。默认情况下回收策
略是禁止删除,若是Redis数据集中的 key都设置了过期时间,那么
"volatile-ttl"策略是比较好的选择。但如果key在达到最大内存限制时没
能够迅速过期,或者根本没有设置过期时间。那么设置为"allkeys-1ru"
值比较合适,它允许Redis从整个数据集中挑选最近最少使用的key进行删除
(LRU淘汰算法)。Redis还提供了一些其他淘汰策略。
volatile-lru:使用LRU算法从已设置过期时间的数据集合中淘汰数据:volatile-ttl:从已设置过期时间的数据集合中挑选即将过期的数据淘汰;
volatile-random:从已设置过期时间的数据集合中随机挑选数据淘汰:
allkeys-lru:使用LRU算法从所有数据集合中淘汰数据:
allkeys-random:从数据集合中任意选择数据淘汰;
no-enviction:禁止淘汰数据。
infostats信息中的evicted_keys字段显示的是因为maxmemory限制导
致key被回收删除的数量。当Redis由于内存压力需要回收女一个key时,
Redis首先考虑的不是回收最旧的数据,而是在最近最少使用的key或即将过期的key中随机选择一个key,从数据中删除
192.168.9.236:7001> info stats
# Stats
total_connections_received:473156108
total_commands_processed:4180290178
instantaneous_ops_per_sec:375
total_net_input_bytes:14676967575477
total_net_output_bytes:102221322391862
instantaneous_input_kbps:1465.97
instantaneous_output_kbps:7011.15
rejected_connections:0
sync_full:1
sync_partial_ok:0
sync_partial_err:1
expired_keys:34158591
expired_stale_perc:0.40
expired_time_cap_reached_count:0
evicted_keys:0
keyspace_hits:3089647498
keyspace_misses:94699798
pubsub_channels:3
pubsub_patterns:2
latest_fork_usec:51400
migrate_cached_sockets:0
slave_expires_tracked_keys:0
active defrag hits:0
active defrag_misses:0
active defrag_key_hits:0
active defrag_key_misses:0
通过以上文字描述和参数对比,可以根据key的回收来定位性能问题,通
过回收key可以保证合理分配Redis有限的内存资源。因此evicted_keys值
经常超过0,那应该会看到客户端命令响应延迟时间增加,因为Redis不但要
处理客户端过来的命令请求,还要频繁的回收满足条件的key
需要注意的是,回收 key对性能的影响远没有内存交换严重,若是在强制
内存交换和设置回收策略做一个选择的话,则放弃强制内存交换是比较合理的,因为把内存数据交换到硬盘上对性能影响非常大。既然频繁的回收key也会导
致性能问题,需要减少回收 key来提升性能,根据经驯脸如果开启快照功能
maxmemory需要设置成物理内存的45%,这几乎不会有引发内不存交换的危险。若
是没有开启快照功能,设置系统可用内存的95%是比较合理的。
另外一种分片技术是把数据分割成合适大小,分别存放在不同的Redis实
例上,每一个实例都包含整个数据集的一部分。通过分片可以把很多服务器联合
起来存储数据,相当于增加总的物理内存,使其在没有内存交换和回收key的
策略下也能存储更多的keyg假如有一个非常大的数据集,maxmeemory已经设置,
实际内存使用也已经超过了推荐设置的阀值,通过数据分片能明显减少key的
回收,从而提高Redis的性能。当然Redis性能管理远远
比上面列出的几种复杂的多,需要多加学习。