<服务>Redis

时间:Feb. 28, 2017 分类:

目录:

Redis

NoSQL

主要是因为关系型数据库的并发大概普遍低于一千,不能满足当前高并发的场景。NoSQL就是支持的高并发,高性能的存在,抛弃了其他方面的功能。

可以满足的优点

  1. 高并发读写需求
  2. 海量数据的高效存储与访问需求
  3. 高扩展和高可用性需求

缺点为

  1. 事务一致性
  2. 读写的实时性
  3. 复杂的SQL查询,特别是多表关联查询

应用的场景

  1. 数据模型比较简单
  2. 需要灵活性的系统
  3. 对数据库性能要求较高
  4. 不需要高度的数据一致性
  5. 对于给定key,可以映射到复杂值的场景

例如存储用户信息,会话,配置文件,参数,购物车等,计数,统计,粉丝关注的一般与ID挂钩的情况下使用会比较好。

不适用的场景,例如事务回滚,通过值查询,两个或两个以上键进行关联查询等。

Redis介绍

官网https://redis.io/

redis是一个基于key-value的持久化数据库存储系统。redis支持更多的数据存储类型,例如字符串,hash,列表,集合,有序集合等。这些数据支持push/pop,add/remove和取差集,取并集,取交集等更复杂的操作。

持久化包括更新到磁盘和写入binlog进行记录,支持主从同步。

开发语言为C,支持各种的API,自2010年3月15日由VMWare主持开发。

官方的介绍为,Redis是基于BSD许可开源的高级key-value存储系统, 自从可以支持字符串,列表,集合,有序集合等,经常被作为数据存储服务器。

Redis的优缺点

主要特点:

  1. key-value键值对存储
  2. 支持数据可靠存储及落地(牺牲性能)
  3. 单进程,单线程高性能服务器
  4. 单机qps可以达到10W
  5. 有预热过程
  6. 数据类型丰富
  7. 操作原子性,并且可以合并几个操作
  8. 支持主从同步,支持消息队列

缺点:

  1. 内存开销较大,一般不要配置超过内存的60%
  2. buffer IO造成OOM
  3. 不同命令的延迟差别极大

更多的可以查阅官方文档https://redis.io/documentation

Redis持久化

持久化一般通过快照的方式定期写入数据(snapshot)和类似MySQLbinlog的方式写入磁盘(AOF)

Snapshot模式可选择的配置

save 900 1          #每900秒1次写进行快照
save 300 10
save 60 10000

AOF模式可选择的配置

appendfsync always          #每条都进行写入磁盘
appendfsync everysec        #每秒写入一次磁盘
appendfsync no              #从不写入磁盘
  • 一般更新频繁,一致性要求比较高的使用AOF策略为主;
  • 更新不频繁,可以允许数据丢失的错误以Snapshot策略为主。

如果用Snapshot的模式,在生产快照时,会堵塞客户端的请求,另外AOF模式下的日志只用于读入内存,并非用于主从同步。

Memcache和MySQL的组合,最大的问题就是数据一致性和Memcache预热,这些更需要程序来进行调控,不过如果需要除key-value的数据类型的时候,就更符合redis了。

Redis应用场景

在微博中的应用,包括微博(转发,评论,阅读,点赞等),用户(粉丝,关注,收藏,双向关注)好友关系,计数器(每秒百万级的请求,高速响应,有一定空值记录,历史和新增数据量比较大)

应用方式 直接访问Redis Redis数据不存在或访问失败时访问MySQL 二次开发实现MySQL和Redis数据同步,

当时我们选择Redis的原因

  1. 数据结构越来越复杂,但是memcache中没有,影响开发效率
  2. 性能需求,读操作的量上升需要解决,经历的历程有数据库读写分离,数据库用多个slave,增加cache转到redis
  3. 解决写问题,水平拆分,对表的拆分,将一部分用户放在这个表,另一部分放在另外的表中
  4. 可靠性需求cache的宕机恢复问题
  5. cache和DB之间的数据一致性维护成本过高,开发跟不上产品的需求,例如先清理DB再清理cache效果太慢,IO密集型成本还比较高
  6. 维护性复杂,一致性维护,文件归档等需要定期做,会有宕机时间

生产教训

  1. 一定要主从同步,可以在服务故障时进行切换
  2. Master禁用数据持久化,在slave上做
  3. 物理内存和虚拟内存不足,这时候dump一直挂掉,时间久了机器挂掉,起码要64~128G内存才可以使用redis,并且ssd持久化
  4. Redis物理内存一般超过60%的时候就开始做swap,内存碎片过多,所以最好禁用swap
  5. 达到最多内存的时候会清空带有过期时间的key,即使key未到达过期时间
  6. redis和DB同时写的时候,最好先写DB,后写Redis

安装Redis

[root@why ~]# wget http://download.redis.io/releases/redis-2.8.9.tar.gz
--2017-02-22 16:12:50--  http://download.redis.io/releases/redis-2.8.9.tar.gz
Resolving download.redis.io... 109.74.203.151
Connecting to download.redis.io|109.74.203.151|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1097369 (1.0M) [application/x-gzip]
Saving to: “redis-2.8.9.tar.gz”

100%[=====================================================================================================================================================>] 1,097,369    465K/s   in 2.3s    

2017-02-22 16:12:56 (465 KB/s) - “redis-2.8.9.tar.gz” saved [1097369/1097369]

[root@why ~]# tar xf redis-2.8.9.tar.gz 
[root@why ~]# cd redis-2.8.9
[root@why redis-2.8.9]# make
[root@why redis-2.8.9]# make PREFIX=/usr/local/redis-2.8.9 install
[root@why redis-2.8.9]# ln -s /usr/local/redis-2.8.9 /usr/local/redis
[root@why redis-2.8.9]# tree /usr/local/redis/bin/
/usr/local/redis/bin/
├── redis-benchmark         #Redis测试工具
├── redis-check-aof         #Redis aof更新日志检查
├── redis-check-dump        #Redis本地数据库rdb检查
├── redis-cli               #Redis命令行操作工具
└── redis-server            #Redis Server启动

0 directories, 5 files

配置Redis

[root@why redis-2.8.9]# echo "PATH=/usr/local/redis/bin/:$PATH" >> /etc/profile
[root@why redis-2.8.9]# . !$
. /etc/profile
[root@why redis-2.8.9]# redis-server --help
Usage: ./redis-server [/path/to/redis.conf] [options]
       ./redis-server - (read config from stdin)
       ./redis-server -v or --version
       ./redis-server -h or --help
       ./redis-server --test-memory <megabytes>

Examples:
       ./redis-server (run the server with default conf)
       ./redis-server /etc/redis/6379.conf
       ./redis-server --port 7777
       ./redis-server --port 7777 --slaveof 127.0.0.1 8888
       ./redis-server /etc/myredis.conf --loglevel verbose

Sentinel mode:
       ./redis-server /etc/sentinel.conf --sentinel
[root@why redis-2.8.9]# mkdir /usr/local/redis/conf
[root@why redis-2.8.9]# pwd
/root/redis-2.8.9
[root@why redis-2.8.9]# cp redis.conf /usr/local/redis/conf/

启动和关闭Redis

[root@why ~]# redis-server /usr/local/redis/conf/redis.conf &
[1] 17757
[root@why ~]#                 _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 2.8.9 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                   
 (    '      ,       .-`  | `,    )     Running in stand alone mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379
 |    `-._   `._    /     _.-'    |     PID: 17757
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           http://redis.io        
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               

[17757] 23 Feb 10:16:23.859 # Server started, Redis version 2.8.9
[17757] 23 Feb 10:16:23.860 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
[17757] 23 Feb 10:16:23.860 * The server is now ready to accept connections on port 6379

overcommit_memory现在被设置为0,在此情况下在内存不足的情况下后台保存可能会失败。可以用后边的命令进行修改

[root@why ~]# sysctl vm.overcommit_memory=1
vm.overcommit_memory = 1
[root@why ~]# vi /etc/sysctl.conf
添加vm.overcommit_memory = 1
[root@why ~]# sysctl -p

[root@why ~]# redis-cli shutdown
[17757] 23 Feb 10:24:50.493 # User requested shutdown...
[17757] 23 Feb 10:24:50.493 * Saving the final RDB snapshot before exiting.
[17757] 23 Feb 10:24:50.499 * DB saved on disk
[17757] 23 Feb 10:24:50.499 # Redis is now ready to exit, bye bye...
[1]+  Done                    redis-server /usr/local/redis/conf/redis.conf

Redis操作

[root@why ~]# redis-server /usr/local/redis/conf/redis.conf &
[root@why ~]# redis-cli
127.0.0.1:6379> set id 001 
OK
127.0.0.1:6379> get id
"001"
127.0.0.1:6379> del id
(integer) 1                     #1代表成功
127.0.0.1:6379> get id
(nil)                           
127.0.0.1:6379> set user001 why
OK
127.0.0.1:6379> set user002 mabiao
OK
127.0.0.1:6379> set user003 yanwei
OK
127.0.0.1:6379> set user004 pqt
OK
127.0.0.1:6379> keys *
1) "user002"
2) "user004"
3) "user003"
4) "user001"
127.0.0.1:6379> select 1        #Redis的库区分,一共有16个,默认为0
OK
127.0.0.1:6379[1]> keys *
(empty list or set)
127.0.0.1:6379[1]> select 0
OK
127.0.0.1:6379> keys *
1) "user002"
2) "user004"
3) "user003"
4) "user001"
127.0.0.1:6379> select 16
(error) ERR invalid DB index
127.0.0.1:6379[16]> quit
[root@why ~]# redis-cli -h 127.0.0.1 -p 6379 set user005 anxiang
OK
[root@why ~]# redis-cli -h 127.0.0.1 -p 6379 get user005 
"anxiang"
[17772] 23 Feb 10:45:21.012 * 1 changes in 900 seconds. Saving...
[17772] 23 Feb 10:45:21.013 * Background saving started by pid 17797
[17797] 23 Feb 10:45:21.026 * DB saved on disk
[17797] 23 Feb 10:45:21.026 * RDB: 6 MB of memory used by copy-on-write
[17772] 23 Feb 10:45:21.114 * Background saving terminated with success
^]
telnet> quit   
Connection closed.
[root@why ~]# echo "set user007 liuyiping"|nc 127.0.0.1 6379
+OK
[root@why ~]# echo "get user007 "|nc 127.0.0.1 6379
$9
liuyiping

更多的操作可以通过一下方式获取

[root@why ~]# redis-cli --help
[root@why ~]# redis-cli
127.0.0.1:6379> ?
redis-cli 2.8.9
Type: "help @<group>" to get 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

在输入help后可以通过tab键进行列出,每次会显示一个

Redis安全

和MySQL类似只有一个密码。

[root@why ~]# vi /usr/local/redis/conf/redis.conf
################################## SECURITY ###################################
# requirepass foobared                  #此处为配置密码
[root@why ~]# redis-cli save
[17772] 23 Feb 11:37:38.311 * DB saved on disk
OK
[root@why ~]# redis-cli shutdown
[17772] 23 Feb 11:37:43.360 # User requested shutdown...
[17772] 23 Feb 11:37:43.360 * Saving the final RDB snapshot before exiting.
[17772] 23 Feb 11:37:43.367 * DB saved on disk
[17772] 23 Feb 11:37:43.367 # Redis is now ready to exit, bye bye...
[1]+  Done                    redis-server /usr/local/redis/conf/redis.conf
[root@why ~]# redis-server /usr/local/redis/conf/redis.conf &
[1] 17854

客户端验证

[root@why ~]# redis-cli 
127.0.0.1:6379> set user008 lihongsheng
(error) NOAUTH Authentication required.
127.0.0.1:6379> auth why123456
OK
127.0.0.1:6379> set user008 lihongsheng
OK
127.0.0.1:6379> quit
[root@why ~]# redis-cli -a why123456
127.0.0.1:6379> set user009 haoyingbin
OK

redis的授权

Redis的授权没有MySQL那么全面

[root@why ~]# vi /usr/local/redis/conf/redis.conf
################################## SECURITY ###################################
rename-command set ""
[root@why ~]# redis-cli shutdown
(error) NOAUTH Authentication required.
[root@why ~]# redis-cli -a why123456 shutdown
[root@why ~]# redis-server /usr/local/redis/conf/redis.conf &
[root@why ~]# redis-cli -a why123456
127.0.0.1:6379> set user010 dongpengcheng
(error) ERR unknown command 'set'

也可以改名,示例

rename-command set why

这只是演示一下,这边我取消配置并重启

PHP调用Redis

nginx+php环境自行准备,可以参考博客中nginx和php环境

下载编译php的redis模块

[root@why-3 ~]# wget https://codeload.github.com/phpredis/phpredis/zip/master
[root@why-3 ~]# mv master phpredis-master.zip
[root@why-3 ~]# unzip phpredis-master.zip 
[root@why-3 ~]# cd phpredis-master
[root@why-3 phpredis-master]# /usr/local/php/bin/phpize
Configuring for:
PHP Api Version:         20090626
Zend Module Api No:      20090626
Zend Extension Api No:   220090626
[root@why-3 phpredis-master]# ./configure -with-php-config=/usr/local/php/bin/php-config
[root@why-3 phpredis-master]# make
[root@why-3 phpredis-master]# make install
[root@why-3 phpredis-master]# echo "extension = redis.so" >> /usr/local/php/lib/php.ini 
[root@why-3 phpredis-master]# echo "extension_dir = "/usr/local/php5.3.27/lib/php/extensions/no-debug-non-zts-20090626/"" >> /usr/local/php/lib/php.ini 
[root@why-3 phpredis-master]# tail -2 /usr/local/php/lib/php.ini
extension = redis.so
extension_dir = /usr/local/php5.3.27/lib/php/extensions/no-debug-non-zts-20090626/

通过phpinfo函数查看

[root@why-3 html]# vi index.php
[root@why-3 html]# cat index.php
<?php
    $redis = new Redis();
    $redis -> connect('192.168.0.203',6379);
    $redis -> auth('why123456');
    $redis -> set('user010','chengsong');
    $var = $redis -> get('user010');
    echo $var;
?>

重启php-fpm服务

Python调用Redis

编译Python的redis模块

[root@why-3 ~]# wget https://pypi.python.org/packages/source/r/redis/redis-2.10.1.tar.gz
[root@why-3 ~]# cd redis-2.10.1
[root@why-3 redis-2.10.1]# python setup.py install
[root@why-3 redis-2.10.1]# python
Python 2.6.6 (r266:84292, Sep  4 2013, 07:46:00) 
[GCC 4.4.7 20120313 (Red Hat 4.4.7-3)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import redis
>>> r = redis.Redis(host='192.168.0.203',port=6379,password='why123456',db=0)
>>> r.get('user010')
'chengsong'
>>> r.set('user011','daiye')
True
>>> r.get('user011')
'daiye'
>>> r.dbsize()              #查看总数
10L
>>> r.keys()
['user004', 'user005', 'user003', 'user007', 'user002', 'user008', 'user001', 'user011', 'user009', 'user010']
>>> r.flushdb()             #删除0号库
True
>>> r.keys()
[]
>>> r.flushall()            #删除所有库
True

编写python脚本

[root@why-3 local]# vi why.py
[root@why-3 local]# cat why.py 
import redis
r = redis.Redis(host='192.168.0.203',port=6379,password='why123456',db=0)
r.set('user012','qiaojun')
print r.get('user012')
[root@why-3 local]# python why.py 
qiaojun

如果在root目录下会出现这个情况

[root@why-3 ~]# python why.py 
Traceback (most recent call last):
  File "why.py", line 1, in <module>
    import redis
  File "/root/redis.py", line 2, in <module>
    r = redis.Redis(host='192.168.0.203',port=6379,password='why123456',db=0)
AttributeError: 'module' object has no attribute 'Redis'

通过搭建web服务器实现

[root@why-3 local]# vi web.py
[root@why-3 local]# cat web.py 
from wsgiref.simple_server import make_server
import redis
def get_redis():
    r = redis.Redis(host='192.168.0.203',port=6379,password='why123456',db=0)       #连接redis
    r.set('user013','daixun')
    return r.get('user013')                                                         #返回结果
def hello_world_app(environ,start_response):
    status = '200 OK'                                                               #HTTP状态
    headers = [('Content-type','text/plain')]                                       #HTTPheader
    start_response(status,headers)                                                  
    return get_redis()
httpd = make_server('',8000,hello_world_app)    
print "Serving on port 8000"
httpd.serve_forever()
[root@why-3 local]# python web.py 
Serving on port 8000

Redis配置文件详解

[root@why-3 ~]# vim /usr/local/redis/conf/redis.conf
################################## INCLUDES ###################################
# include /path/to/local.conf                       #包含配置文件
################################ GENERAL  #####################################
daemonize no                                        #是否后台运行
pidfile /var/run/redis.pid                          #pid文件
port 6379                                           #端口
tcp-backlog 511                                     #tcp队列,在每秒请求过大的时候,可以调大该值,调整的时候也需要调整内核参数/proc/sys/net/core/somaxconn
# bind 192.168.1.100 10.0.0.1                       #监听的IP地址,默认监听所有
# unixsocket /tmp/redis.sock                        #sock文件
# unixsocketperm 755                                #sock文件权限
timeout 0                                           #超时时间,0代表不超时
tcp-keepalive 0                                     #tcp回话保持时间,0代表一直保持
loglevel notice                                     #日志级别,生产环境下最低是notice
logfile ""                                          #指定日志文件,默认打印达到终端
# syslog-enabled no                                 #syslog接收
# syslog-ident redis                                #
# syslog-facility local0                            #日志收集器
databases 16                                        #数据库数量
################################ SNAPSHOTTING  ################################
save 900 1                                          #900s内有至少一个key进行save
save 300 10                                         #300s内有至少10个key进行save
save 60 10000                                       #60s内有至少10000个key进行save,save ""代表不save,因为save会阻塞客户端写入请求
stop-writes-on-bgsave-error yes                     #如果bgsave出错,是否停止写入
rdbcompression yes                                  #在dump的时候是否进行压缩,压缩会占用cpu
rdbchecksum yes                                     #rdb的检查
dbfilename dump.rdb                                 #db保存文件名
dir ./                                              #db保存目录
################################# REPLICATION #################################
                                                    #此处负责主从同步
################################## SECURITY ###################################
# requirepass foobared                              #密码
# rename-command CONFIG ""                          #授权和改名
################################### LIMITS ####################################
# maxclients 10000                                  #并发
maxmemory <bytes>                                   #内存限制
maxmemory-policy volatile-lru                       #内存超出算法
maxmemory-samples 3                                 #LRU算法和最小TTL算法的样本数
############################## APPEND ONLY MODE ###############################
appendonly no                                       #aof是否启动
appendfilename "appendonly.aof"                     #aof文件,用户redis启动时读取到内存
# appendfsync always
appendfsync everysec                                #写入aof,每秒
# appendfsync no
auto-aof-rewrite-percentage 100                     #大于100写入
auto-aof-rewrite-min-size 64mb                      #大于64MB写入
################################ LUA SCRIPTING  ###############################
                                                    #LUA脚本
################################## SLOW LOG ###################################
slowlog-log-slower-than 10000                       #慢查询
slowlog-max-len 128
############################# Event notification ##############################
# PUBLISH __keyspace@0__:foo del                    #事件通知
# PUBLISH __keyevent@0__:del foo
############################### ADVANCED CONFIG ###############################
                                                    #高级参数

Redis数据类型

value包含字符串,列表,hash,集合,排序集合和特殊数据类型。

Redis的作者建议,对于key可以设置为object-type:id:field,即对象类型:id:字段,长度在10~20,而对于value来说string不超过2k,Set SortedSet元素不要超过5000,或者根据长度启动不同的实例存储

redis的string类型(字符串)

字符串可以是任意字符串,二进制也可以(例如保存一个不超过1G的图片)

基本的SET和GET

[root@why-3 ~]# redis-cli -a why123456
127.0.0.1:6379> set user01 laoda
OK
127.0.0.1:6379> get user01
"laoda"

计数counter

这个counter是我自己定义的key

127.0.0.1:6379> set counter 1
OK
127.0.0.1:6379> incr counter        #自增
(integer) 2
127.0.0.1:6379> incr counter 
(integer) 3
127.0.0.1:6379> incrby counter 2    #以指定步长自增
(integer) 5
127.0.0.1:6379> incrby counter 3    
(integer) 8
127.0.0.1:6379> decr counter        #自减
(integer) 7
127.0.0.1:6379> decr counter 
(integer) 6
127.0.0.1:6379> decrby counter 2    #以指定步长自减
(integer) 4

值更新

127.0.0.1:6379> get user01
"laoda"
127.0.0.1:6379> getset user01 laoer
"laoda"
127.0.0.1:6379> get user01
"laoer"

批量设置

127.0.0.1:6379> mset name why age 24 sex boy
OK
127.0.0.1:6379> mget name age sex
1) "why"
2) "24"
3) "boy"
127.0.0.1:6379> get age
"24"

追加

127.0.0.1:6379> append name wanghongyu
(integer) 13
127.0.0.1:6379> get name
"whywanghongyu"

获取长度

127.0.0.1:6379> strlen name
(integer) 13

获取特定区间

127.0.0.1:6379> substr name 0 2
"why"
127.0.0.1:6379> substr name 3 12
"wanghongyu"

帮助

127.0.0.1:6379> help get

  GET key
  summary: Get the value of a key
  since: 1.0.0
  group: string

可以看到get属于string组,我们可以查看整个组来看有什么方法

127.0.0.1:6379> help @string

列表

这里的列表是指有序元素的序列。

帮助

127.0.0.1:6379> help @list

添加元素到列表

127.0.0.1:6379> lpush peoples guowei
(integer) 1
127.0.0.1:6379> lpush peoples lvdongdong
(integer) 2
127.0.0.1:6379> lpush peoples xujiang
(integer) 3
127.0.0.1:6379> llen peoples                #列表长度
(integer) 3
127.0.0.1:6379> lrange peoples 0 2          #查看列表
1) "xujiang"
2) "lvdongdong"
3) "guowei"
127.0.0.1:6379> lpop peoples                #删除随后一个列表元素
"xujiang"
127.0.0.1:6379> lrange peoples 0 1
1) "lvdongdong"
2) "guowei"

删除指定元素

127.0.0.1:6379> lrem peoples 2 guowei
(integer) 1
127.0.0.1:6379> lrange peoples 0 0
1) "lvdongdong"
127.0.0.1:6379> lrange peoples 0 1
1) "lvdongdong"

插入元素

127.0.0.1:6379>  lpush peoples wangshuai xuening suidafei
(integer) 4
127.0.0.1:6379> lrange peoples 0 3
1) "suidafei"
2) "xuening"
3) "wangshuai"
4) "lvdongdong"
127.0.0.1:6379> linsert peoples before wangshuai linjia 
(integer) 5
127.0.0.1:6379> lrange peoples 0 4 
1) "suidafei"
2) "xuening"
3) "linjia"
4) "wangshuai"
5) "lvdongdong"

列表可以做聊天系统,不同进程之间的传递消息队列,每次都以原先添加的顺序进行访问数据。

集合

集合是未排列的集合,其元素为二进制安全的字符串,并且可以检测是否存在,是否实现了交集,并集,差集等。

帮助

127.0.0.1:6379> help @set

集合添加元素并查看

127.0.0.1:6379> sadd users a b c
(integer) 3
127.0.0.1:6379> smembers users
1) "c"
2) "b"
3) "a"

判断元素是否存在

127.0.0.1:6379> sismember users b
(integer) 1
127.0.0.1:6379> sismember users d
(integer) 0

依靠集合获取对象标签

我们有一个ID为1000的新闻,标签对应1,2和5。

127.0.0.1:6379> sadd news:1000:tags 1
(integer) 1
127.0.0.1:6379> sadd news:1000:tags 2
(integer) 1
127.0.0.1:6379> sadd news:1000:tags 5
(integer) 1
127.0.0.1:6379> sadd tags:5:news 1000
(integer) 1
127.0.0.1:6379> sadd tags:2:news 1000
(integer) 1
127.0.0.1:6379> sadd tags:1:news 1000
(integer) 1
127.0.0.1:6379> smembers news:1000:tags
1) "1"
2) "2"
3) "5"
127.0.0.1:6379> smembers tags:5:news
1) "1000"

这一功能就可以反映为对象之间的关系,例如标签,通过标签为key,其内用户或者人员为value的集合,通过判断用户是否在标签内。

获取交集

我们就可以获取同时包含在标签1,标签2和标签5的新闻

127.0.0.1:6379> sinter tags:1:news tags:2:news tags:5:news
1) "1000"

当然子集,交集,并集,补集都是支持的,然后通过后续将的hash把新闻的IP指向新闻的名称,标签也指向对应的标签即可。

删除集合元素

127.0.0.1:6379> srem users b
(integer) 1
127.0.0.1:6379> smembers users
1) "c"
2) "a"

有序集合

帮助

127.0.0.1:6379> help @sorted_set

有序集合添加元素

127.0.0.1:6379> zadd days 0 mon
(integer) 1
127.0.0.1:6379> zadd days 1 tue
(integer) 1
127.0.0.1:6379> zadd days 2 wed
(integer) 1
127.0.0.1:6379> zadd days 3 thu
(integer) 1
127.0.0.1:6379> zadd days 4 fri
(integer) 1
127.0.0.1:6379> zadd days 5 sat 
(integer) 1
127.0.0.1:6379> zadd days 6 sun
(integer) 1
127.0.0.1:6379> zrange days 0 6
1) "mon"
2) "tue"
3) "wed"
4) "thu"
5) "fri"
6) "sat"
7) "sun"

查找有序集合元素顺序

127.0.0.1:6379> zscore days mon
"0"
127.0.0.1:6379> zscore days wed
"2"

根据顺序查找

127.0.0.1:6379> zrangebyscore days 3 5
1) "thu"
2) "fri"
3) "sat"

详解有序

127.0.0.1:6379> zadd hackers 1940 "1940-Alan Key"
(integer) 1
127.0.0.1:6379> zadd hackers 1953 "1953-Richard Stallman"
(integer) 1
127.0.0.1:6379> zadd hackers 1965 "1965-Yukihiro Matsumoto"
(integer) 1
127.0.0.1:6379> zadd hackers 1969 "1969-Linus Torvalds"
(integer) 1
127.0.0.1:6379> zadd hackers 1912 "1912-Alan Turing"
(integer) 1
127.0.0.1:6379> zrange hackers 0 4                  #正序
1) "1912-Alan Turing"
2) "1940-Alan Key"
3) "1953-Richard Stallman"
4) "1965-Yukihiro Matsumoto"
5) "1969-Linus Torvalds"
127.0.0.1:6379> zrevrange hackers 0 4               #倒序
1) "1969-Linus Torvalds"
2) "1965-Yukihiro Matsumoto"
3) "1953-Richard Stallman"
4) "1940-Alan Key"
5) "1912-Alan Turing"
127.0.0.1:6379> zrangebyscore hackers -inf 1950     #查找score在负无穷到1950区间内的元素
1) "1912-Alan Turing"
2) "1940-Alan Key"
127.0.0.1:6379> zremrangebyscore hackers 1940 1960  #删除score在1940到1960区间内的元素
(integer) 2
127.0.0.1:6379> zrevrange hackers 0 4
1) "1969-Linus Torvalds"
2) "1965-Yukihiro Matsumoto"
3) "1912-Alan Turing"

HASH类型

存储key对多个属性的数据

帮助

127.0.0.1:6379> help @hash

HASH插入数据

127.0.0.1:6379> hset test name why
(integer) 1
127.0.0.1:6379> hset test age 24
(integer) 1
127.0.0.1:6379> hset test sex boy
(integer) 1
127.0.0.1:6379> hvals test
1) "why"
2) "24"
3) "boy"
127.0.0.1:6379> hgetall test
1) "name"
2) "why"
3) "age"
4) "24"
5) "sex"
6) "boy"

Redis多实例

生成配置文件

Redis多实例需要有两套配置文件,配置文件中端口,pid,dbdir同即可。

[root@why-3 ~]# cd /usr/local/redis/conf
[root@why-3 conf]# ll
total 32
-rw-r--r-- 1 root root 31204 Feb 23 15:38 redis.conf
[root@why-3 conf]# mkdir -p /data/6378
[root@why-3 conf]# mkdir -p /data/6377
[root@why-3 conf]# cp redis.conf /data/6378
[root@why-3 conf]# cp redis.conf /data/6377
[root@why-3 conf]# vi /data/6377/redis.conf
修改
pidfile /var/run/redis6377.pid
port 6377
dir /data/6377/
[root@why-3 conf]# vi /data/6378/redis.conf
修改
pidfile /var/run/redis6378.pid
port 6378
dir /data/6378/

验证

[root@why-3 conf]# redis-server /data/6377/redis.conf &
[root@why-3 conf]# redis-server /data/6378/redis.conf &
[root@why-3 conf]# redis-cli -p 6377 -a why123456
127.0.0.1:6377> set user01 why
OK
127.0.0.1:6377> save
[6565] 24 Feb 16:27:03.217 * DB saved on disk
OK
127.0.0.1:6377> quit
[root@why-3 conf]# redis-cli -p 6378 -a why123456
127.0.0.1:6378> set user01 wanghongyu
OK
127.0.0.1:6378> save
[6568] 24 Feb 16:27:21.344 * DB saved on disk
OK
127.0.0.1:6378> quit
[root@why-3 conf]# ll /data/6377/dump.rdb /data/6378/dump.rdb 
-rw-r--r-- 1 root root 32 Feb 24 16:27 /data/6377/dump.rdb
-rw-r--r-- 1 root root 39 Feb 24 16:27 /data/6378/dump.rdb
[root@why-3 conf]# netstat -lptun | egrep "6377|6378|6379"
tcp        0      0 0.0.0.0:6379                0.0.0.0:*                   LISTEN      5021/redis-server * 
tcp        0      0 0.0.0.0:6377                0.0.0.0:*                   LISTEN      6565/redis-server * 
tcp        0      0 0.0.0.0:6378                0.0.0.0:*                   LISTEN      6568/redis-server * 
tcp        0      0 :::6379                     :::*                        LISTEN      5021/redis-server * 
tcp        0      0 :::6377                     :::*                        LISTEN      6565/redis-server * 
tcp        0      0 :::6378                     :::*                        LISTEN      6568/redis-server * 

当然,实际情况需要根据情况调整其他的参数,例如内存,save等。

Redis主从同步

官方文档https://redis.io/topics/replication

  1. Redis主从同步配置简单,允许slave成为master的拷贝
  2. 异步的同步机制,开始于2.8版本
  3. 在同步的时候slave会周期性的告知master复制的数据流收到
  4. master端可以有多个slave端,slave端也可以接受其他slave的同步
  5. redis的同步在master端是非堵塞的,同步过程中,master端可以接受客户端请求,在slave端也是非堵塞的,也可以接受客户端请求
  6. 在初始化同步的过程中,老的数据被删除,加载新的数据,在加载期间可能会堵塞客户端请求
  7. 同步可以用来进行扩展,通过多个slave进行数据冗余,复杂查询处理等
  8. 可以配置主库不进行save只进行aof,而在从库进行save

同步过程

  1. salve端向master端进行连接
  2. slave端向master端发送sync请求
  3. master端进行备份.rdb文件
  4. master端向slave端同步.rdb文件
  5. slave端导入.rdb文件
  6. master端将新写入的数据存在buffer中继续向slave端以数据流的方式发送数据
  7. 当slave端宕机后重启可以向master端进行请求,master会根据生成的backlog的运行id和偏移量记录可以则同步后续的内容(2.8版本后)
  8. 如果master端宕机后重启就会重新向slave端推送.rdb文件

配合redis主从同步

[root@why-3 ~]# vi /data/6377/redis.conf 
################################# REPLICATION #################################
# slaveof <masterip> <masterport>
# masterauth <master-password>
添加
slaveof 192.168.0.203 6378
masterauth why123456
[root@why-3 ~]# redis-cli -p 6377 -a why123456 shutdown
[root@why-3 ~]# redis-server /data/6377/redis.conf &
[1] 9046
[root@why-3 ~]# [9046] 25 Feb 04:57:50.426 * Increased maximum number of open files to 10032 (it was originally set to 1024).
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 2.8.9 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                   
 (    '      ,       .-`  | `,    )     Running in stand alone mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6377
 |    `-._   `._    /     _.-'    |     PID: 9046
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           http://redis.io        
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               

[9046] 25 Feb 04:57:50.427 # Server started, Redis version 2.8.9                                    #开始服务
[9046] 25 Feb 04:57:50.428 * DB loaded from disk: 0.000 seconds                                     #从磁盘读取数据
[9046] 25 Feb 04:57:50.428 * The server is now ready to accept connections on port 6377             #提供6377端口服务
[9046] 25 Feb 04:57:51.427 * Connecting to MASTER 192.168.0.203:6378                                #向MASTER进行连接
[9046] 25 Feb 04:57:51.522 * MASTER <-> SLAVE sync started                                          #MASTER向SLAVE同步数据开始
[9046] 25 Feb 04:57:51.522 * Non blocking connect for SYNC fired the event.                         #提交第一次同步请求
[9046] 25 Feb 04:57:51.523 * Master replied to PING, replication can continue...                    #MASTER应答该PING操作,同步可以继续
[9046] 25 Feb 04:57:51.524 * Partial resynchronization not possible (no cached master)              #重新同步MASTER没有拒绝
[9046] 25 Feb 04:57:51.525 * Full resync from master: fb267e31be290d4fc6fedc5fd7aef8694fa634f3:1    #从MASTER全量同步
[9046] 25 Feb 04:57:51.568 * MASTER <-> SLAVE sync: receiving 39 bytes from master                  #收到了39个字节
[9046] 25 Feb 04:57:51.568 * MASTER <-> SLAVE sync: Flushing old data                               #覆盖旧数据
[9046] 25 Feb 04:57:51.568 * MASTER <-> SLAVE sync: Loading DB in memory                            #导入rdb文件中的内容到内存
[9046] 25 Feb 04:57:51.568 * MASTER <-> SLAVE sync: Finished with success                           #同步完成

主库方面

[9081] 25 Feb 06:58:04.717 * Slave asks for synchronization                                 #SLAVE请求同步请求
[9081] 25 Feb 06:58:04.717 * Full resync requested by slave.                                #SLAVE请求了全量同步
[9081] 25 Feb 06:58:04.717 * Starting BGSAVE for SYNC                                       #开始为同步进行BGSAVE
[9081] 25 Feb 06:58:04.718 * Background saving started by pid 9432                          #备份保存通过pid为9432的进程进行
[9432] 25 Feb 06:58:04.731 * DB saved on disk                                               #db数据保存到磁盘
[9432] 25 Feb 06:58:04.733 * RDB: 0 MB of memory used by copy-on-write                      #0MB被拷贝占用
[9081] 25 Feb 06:58:04.826 * Background saving terminated with success                      #db数据保存到磁盘结束
[9081] 25 Feb 06:58:04.827 * Synchronization with slave succeeded                           #同步完成

在slave端使用monitor,在master端写入数据

[root@why-3 ~]# redis-cli -p 6377 -a why123456
127.0.0.1:6377> monitor
OK
1487982988.001953 [0 192.168.0.203:6378] "PING"
1487982998.108387 [0 192.168.0.203:6378] "PING"
1487983008.213958 [0 192.168.0.203:6378] "PING"
1487983009.863326 [0 192.168.0.203:6378] "SELECT" "0"
1487983009.863348 [0 192.168.0.203:6378] "set" "user03" "yanwei"
[9623] 25 Feb 08:36:49.931 * 1 changes in 900 seconds. Saving...
[9623] 25 Feb 08:36:49.931 * Background saving started by pid 9833
[9833] 25 Feb 08:36:49.936 * DB saved on disk
[9833] 25 Feb 08:36:49.939 * RDB: 0 MB of memory used by copy-on-write
[9623] 25 Feb 08:36:50.032 * Background saving terminated with success
1487983018.325454 [0 192.168.0.203:6378] "PING"
1487983028.392343 [0 192.168.0.203:6378] "PING"
1487983038.517250 [0 192.168.0.203:6378] "PING"
1487983039.200014 [0 192.168.0.203:6378] "SELECT" "1"
1487983039.200300 [1 192.168.0.203:6378] "set" "user01" "zhangsan"
1487983048.722199 [1 192.168.0.203:6378] "PING"

其他主从同步参数

  • slave-serve-stale-data yes 当slave无法正常连接Master或者在进行同步的时候是否进行client应答

  • slave-read-only yes 从库无法写入数据

  • # repl-ping-slave-period 10 slave确认主库存活的间隔时间,即上边用monitor命令显示的PING

  • # repl-timeout 60 超时时间

repl-disable-tcp-nodelay no tcp连接设置,如果选择yes,Master就会使用较少的带宽发送数据给Slave,但是延迟会高一些,会达到40ms,在Linux内核中设置的一个参数,选择no就会减少延迟,但是会占用更多带宽,推荐使用no,如果是多个slave线性同步可以选yes

  • # repl-backlog-size 1mb 主库的参数,在slave断开连接的时候,主库的backlog队列大小,可以在从库重连后更好的增量恢复。如果设置过小就会造成无法正常增量恢复,需要进行全量恢复

  • # repl-backlog-ttl 3600 主库的参数,在slave断开连接后多久backlog释放内存

  • slave-priority 100 slave的优先级,如果master宕机后,优先级高的slave会成为新的master

Redis状态查看

[root@why-3 ~]# redis-cli -p 6378 -a why123456
127.0.0.1:6378> info
# Server
redis_version:2.8.9
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:d1aae9c00200e2c1
redis_mode:standalone
os:Linux 2.6.32-431.el6.x86_64 x86_64
arch_bits:64
multiplexing_api:epoll
gcc_version:4.4.7
process_id:9623
run_id:3efa33670758b6651e504fb14955b8d303375d34
tcp_port:6378
uptime_in_seconds:1853
uptime_in_days:0
hz:10
lru_clock:11586967
config_file:/data/6378/redis.conf

# Clients
connected_clients:1
client_longest_output_list:0
client_biggest_input_buf:0
blocked_clients:0

# Memory
used_memory:1879112
used_memory_human:1.79M
used_memory_rss:2293760
used_memory_peak:1879112
used_memory_peak_human:1.79M
used_memory_lua:33792
mem_fragmentation_ratio:1.22
mem_allocator:jemalloc-3.2.0

# Persistence
loading:0
rdb_changes_since_last_save:0
rdb_bgsave_in_progress:0
rdb_last_save_time:1487980193
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:0
rdb_current_bgsave_time_sec:-1
aof_enabled:0
aof_rewrite_in_progress:0
aof_rewrite_scheduled:0
aof_last_rewrite_time_sec:-1
aof_current_rewrite_time_sec:-1
aof_last_bgrewrite_status:ok
aof_last_write_status:ok

# Stats
total_connections_received:4
total_commands_processed:1777
instantaneous_ops_per_sec:1
rejected_connections:0
sync_full:1
sync_partial_ok:0
sync_partial_err:0
expired_keys:0
evicted_keys:0
keyspace_hits:0
keyspace_misses:0
pubsub_channels:0
pubsub_patterns:0
latest_fork_usec:532

# Replication
role:master
connected_slaves:1
slave0:ip=192.168.0.203,port=6377,state=online,offset=2479,lag=0
master_repl_offset:2479
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:2478

# CPU
used_cpu_sys:0.81
used_cpu_user:0.51
used_cpu_sys_children:0.00
used_cpu_user_children:0.00

# Keyspace
db0:keys=2,expires=0,avg_ttl=0
127.0.0.1:6378> info cpu                #单独查看一项状态
# CPU
used_cpu_sys:0.82
used_cpu_user:0.53
used_cpu_sys_children:0.00
used_cpu_user_children:0.00
127.0.0.1:6378> info replication        #单独查看一项状态
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.0.203,port=6377,state=online,offset=2535,lag=1
master_repl_offset:2535
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:2534

Redis发布订阅

pub/sub是一种消息通信模式,主要的目的是解耦消息发布者和消息订阅者之间的耦合。

注意要在同一redis实例上

开始订阅

127.0.0.1:6378> subscribe redoop
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "redoop"
3) (integer) 1

在另一终端redis client

[root@why-3 ~]# redis-cli -p 6378 -a why123456
127.0.0.1:6378> publish redoop guowei
(integer) 1
127.0.0.1:6378> publish redoop sundafei
(integer) 1

订阅端可以看到

127.0.0.1:6378> subscribe redoop
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "redoop"
3) (integer) 1
1) "message"
2) "redoop"
3) "guowei"
[9651] 25 Feb 08:51:51.045 * 1 changes in 900 seconds. Saving...
[9651] 25 Feb 08:51:51.045 * Background saving started by pid 9885
[9885] 25 Feb 08:51:51.056 * DB saved on disk
[9885] 25 Feb 08:51:51.056 * RDB: 0 MB of memory used by copy-on-write
[9651] 25 Feb 08:51:51.146 * Background saving terminated with success
1) "message"
2) "redoop"
3) "sundafei"

订阅多个,注意和订阅一个命令不同

[root@why-3 ~]# redis-cli -p 6378 -a why123456
127.0.0.1:6378> psubscribe redoop*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "redoop*"
3) (integer) 1

在另一终端redis client

[root@why-3 ~]# redis-cli -p 6378 -a why123456
127.0.0.1:6378> publish redoop01 wanghongyu
(integer) 1
127.0.0.1:6378> publish redoop02 guoxiaopei
(integer) 1

订阅端可以看到

[root@why-3 ~]# redis-cli -p 6378 -a why123456
127.0.0.1:6378> psubscribe redoop*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "redoop*"
3) (integer) 1
1) "pmessage"
2) "redoop*"
3) "redoop01"
4) "wanghongyu"
1) "pmessage"
2) "redoop*"
3) "redoop02"
4) "guoxiaopei"

Redis数据过期

127.0.0.1:6378> set user04 pqt
OK
127.0.0.1:6378> ttl user04              #查看过期时间
(integer) -1
127.0.0.1:6378> exists user04           #查看key是否存在
(integer) 1
127.0.0.1:6378> expire user04 5         #设置过期时间,单位为秒,也可以在set的时候指定,也可以expireat指定过期的时间戳
(integer) 1
127.0.0.1:6378> ttl user04
(integer) -2
127.0.0.1:6378> exists user04 
(integer) 0
127.0.0.1:6378> get user04
(nil)

通过指定过期时间

[root@why-3 ~]# date +%s                            #查看当前时间的时间戳
1487984966
[root@why-3 ~]# date
Sat Feb 25 09:10:45 CST 2017
[root@why-3 ~]# date +%s -d"2017-02-25 09:15:00"    #查看指定时间的时间戳
1487985300

查看过期数据

127.0.0.1:6378> expireat user04 1487985300
(integer) 1
127.0.0.1:6378> ttl user04
(integer) 115
127.0.0.1:6378> ttl user04
(integer) 114
127.0.0.1:6378> ttl user04
(integer) 113
127.0.0.1:6378> ttl user04
(integer) 113

可以看到还有多少秒过期

redis数据过期机制

Redis的过期机制是在访问key的时候判定是否过期,如果过期,则按过期处理,如果key过期后如果不被访问,可能会很久才被删除,因为Redis每秒对keys进行抽样测试,每秒10次,每次10个,如果有过期key,就进行删除

Redis事务

正常操作

127.0.0.1:6378> set user04 pqt
OK
127.0.0.1:6378> setnx user04 panqiutong
(integer) 0
127.0.0.1:6378> setnx user05 panqiutong
(integer) 1
127.0.0.1:6378> get user05
"panqiutong"

在正常操作下,set给一个有value的key的时候不生效。

启动事务

127.0.0.1:6378> multi
OK
127.0.0.1:6378> set user04 panqiutong
QUEUED
127.0.0.1:6378> set user04 liuyiping
QUEUED
127.0.0.1:6378> exec
1) OK
2) OK
127.0.0.1:6378> get user04
"liuyiping"

事务中有错误操作

127.0.0.1:6378> multi
OK
127.0.0.1:6378> set user04 pqt
QUEUED
127.0.0.1:6378> sahdka
(error) ERR unknown command 'sahdka'
127.0.0.1:6378> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6378> get user04
"liuyiping"

当事务中有错误操作的时候,事务不执行

  • discard是取消事务
  • exec是执行事务

Redis持久化

Redis的数据持久化可以通过两种方式实现,一个是数据快照,另一个是AOF方式 数据快照是Redis的默认持久化形式,将内存中的数据以快照的方式写入到rdb文件中,通过save参数指定,save 900 1代表如果超过1个key被修改,则发起快照保存。

快照的保存过程

  1. redis调用fork,产生一个子进程
  2. 父进程继续处理client请求,子进程负责将内存写入到临时文件,由于os的实时复制机制,父进程和子进程会共享相同的物理内存,当父进程处理写入请求的时候os会为父进程要修改的页面创建副本,而不是写共同的物理内存页面,所以子进程的地址空间内的数据是fork时刻的数据快照。
  3. 当子进程将快照写入临时文件完毕后,用临时文件替换原来的快照文件,然后子进程退出
  4. client也可以用save或者bgsave命令通知redis做一次快照持久化,save操作是在主进程中保存快照,这样会堵塞client请求,所以推荐使用,另外每一次快照持久化都是将内存的数据完整的写入磁盘,而并非增量同步,如果数据量大会照成大量的磁盘IO,可能会影响性能。另外在shutdown的时候会先触发一个save。

另外快照的方式在意外down掉的情况下只会保留上次快照的数据,会产生数据的丢失。而aof方式可以持久化存储,是由于aof在持久化的时候会将每一个收到的命令都写入到aof文件中,redis重启的时候会通过执行文件中的写操作来在内存中重建整个数据库的内容。

aof可以指定参数always收到就写入磁盘,这样效率比较慢,everysec每秒写入一次,在性能和持久化方面做了很好的折中。

当然如果调用自增函数incr自增100次,就会有100次写入,非常占内存空间,所以可以用bgrewriteaof压缩aof持久化文件,此时redis将使用与快照类似的方式将内存的数据以命令的方式保存到临时文件最后替换原来的文件,与快照不同的是,父进程在子进程写入临时文件过程中缓存,如果子进程失败也没有影响,等子进程写入完毕通知父进程把缓存内存中的写入到临时文件,并用临时文件替换原来的aof文件。

也有自动bgrewriteaof的配置,auto-aof-rewrite-percentage百分比和auto-aof-rewrite-min-size。

redis基准性能测试

redis-benchmark -h localhost -p 6379 -c 100 -n 100000

redis配置临时生效

config set
config get
config get *
config resetstat

Redis优化

紧凑格式

Redis在使用的时候最好使用hash,在value内部为一个hashmap,如果map的成员比较少,则会采用类似一维线性的紧凑格式来存储该map,则省去了大量的指针开销,这个参数控制对应redis配置中的

hash-max-ziplist-entries 512        #hash格式value的成员的字节不超过512字节的时候使用紧凑格式  
hash-max-ziplist-value 64           #hash格式value的成员不超过64个时候,使用紧凑格式

当然还有别的格式的

list-max-ziplist-entries 512        #list格式
list-max-ziplist-value 64           #list格式

可以设定不同的参数产生多个实例,把数据长度相近的数据类型放在同一个实例

内存预分配

Redis在内部会采用一个shared integer的方式来省去分配内存的开销,即在系统启动的时候事先分配一个从1~n那么多数值对象放在一个池子中,如果存储的数据恰好在这个数值范围内,则直接在池子中取出对象,并且通过引用计数的方式来共享存储,这样在系统存储大量数据的下,就会在一定程度上节省内存并提高性能,这个参数在源码中设置,REDIS_SHARED_INTEGERS,默认为10000,可以修改后重新编译。

持久化方式

尽量使用aof方式进行持久化,每个实例的大小尽量控制在2G左右,以减少缓存失效给系统带来的影响,并且加速数据恢复的速度。

优化总结

  1. 选择合适数据类型并进行紧凑格式存储,分不同配置格式多实例存储
  2. 不需要持久化存储的尽量不持久化存储,另外数据持久化选用ssd硬盘进行aof数据存储,根据数据丢失的容忍度选着择模式
  3. 不要使用虚拟内存,设置vm-enabled为no
  4. 不要让redis使用的内存超过指定内存的60%,不过即使指定了60%,也会超过。不过可以使用conf中的maxmemory配置,使用内存超过该配置参数,拒绝写入。
  5. 大数据量也根据业务进行分开
  6. 在主从架构中,主库不进行持久化,由从库进行持久化