Redis慢查询
许多存储系统(例如MySQL)提供慢查询日志帮助开发和运维人员定位系统存在的慢操作。所谓慢查询日志就是系统在命令执行前后计算每条命令的执行时间,当超过预设阈值, 就将这条命令的相关信息(例如:发生时间,耗时,命令的详细信息)记录下来,Redis也提供了相似的功能。
redis命令的执行过程
慢查询日志的参数
slowlog-log-slower-than:指定执行时间超过多少微秒(1秒等于1000000微秒)的命令请求会被记录到日志上面
*举例说明:如果这个选项值为100,那么执行时间超过100微秒的命令就会被记录到慢查询日志中,如果是500,那么执行时间超过500的命令就会被记录在慢查询日志中。*
slowlog-max-len:指定服务器最多保存多少条慢查询操作,服务器先进先出的方式保存多条慢查询日志,当服务器存储的慢查询数量等于slowlog-log-len选项值时,服务器在添加一条新的慢查询日志之前,会先将旧的一条慢日志删除。
举例说明:如果服务器slowlog-log-len的值为100,并且假设服务器已经存储了100条慢查询日志,那么如果服务器打算添加一条新的慢查询日志的话,他就必须先删除目前保存的最旧的那条日志,然后再添加新日志。
实际现象如下:
先使用config set命令将slow-log-slower-than参数的设置为0微秒,这样redis服务器执行的任何命令都会记录到慢查询日志中。
接着把slowlog-max-len 参数的值设置为5,让服务器最多只保存5条慢查询记录
config set slowlog-log-slower-than 0 config set slowlog-max-len 5
下面我们使用redis客户端发送几个请求:
set msg "hello" set number 100 set database "redis" set sql mysql set nosql memcache
然后使用slowlog get 来查看服务器所保存的慢查询日志:
slowlog get 1) 1) (integer) 6 #日志的唯一标识符 2) (integer) 1610158493 #命令执行时的Unix时间戳 3) (integer) 11 #命令执行的时长,以微秒计算 4) 1) "set" #命令以及命令参数 2) "nosql" 3) "memcache" 5) "127.0.0.1:40024" 6) "" 2) 1) (integer) 5 2) (integer) 1610158478 3) (integer) 10 4) 1) "set" 2) "sql" 3) "mysql" 5) "127.0.0.1:40024" 6) "" 3) 1) (integer) 4 2) (integer) 1610158457 3) (integer) 9 4) 1) "set" 2) "database" 3) "Redis" 5) "127.0.0.1:40024" 6) "" 4) 1) (integer) 3 2) (integer) 1610158437 3) (integer) 20 4) 1) "set" 2) "number" 3) "10086" 5) "127.0.0.1:40024" 6) "" 5) 1) (integer) 2 2) (integer) 1610158422 3) (integer) 21 4) 1) "set" 2) "msg" 3) "hello world" 5) "127.0.0.1:40024" 6) ""
慢查询日志的保存
服务器状态中包含了几个慢查询日志功能有关的属性:
struct redisServer { //... //下一个慢查询日志的ID long long slowlog_entry_id; //保存了所有查询日志的链表 list * slowlog; //服务器配置slowlog-log-slower-than选项的值 long long slowlog_logslower_than; //服务器配置slowlog-max-len的值 unsigned long slowlog_max_len; //... }
slowlog_entry_id 属性的初始值为0,每当创建一天新的慢查询日志的时候,这个属性值就会作用到新日志的id值,之后程序会对这个属性的值增一。
pipeline(管道)
我们知道redis的客户端和服务器之间是通过tcp协议连接的,不论是客户端向redis发送命令还是客户端接收redis的执行结果,都需要网络通信,都需要一定时间,由于网络性能的不同往返时间也不同,这个时间相当于redis处理一条简单命令(比如插入一个值到链表)的时间。如果我们执行较多的命令,一来一回,这个往返时间累加起来还是对性能有一定的影响。
由于redis是单线程,所以在执行多个命令时,都需要等待上一条命令执行完,才能执行下一条命令。因此,redis底层通信协议提供了对管道技术的支持。通过管道可以一次性发送多条命令并在执行完之后一次性将结果返回,当一组命令中每条命令都不依赖于之前命令的执行结果时,就可以将这组命令一起通过管道发出。管道通过减少客户端与redis的通信次数来实现降低往返时延累计值的目的。
不使用管道
<?php
$stime = microtime(true);//获取程序开始执行时间
echo '开始内存:'.memory_get_usage().'';
echo PHP_EOL;
$redis = new \Redis();
$redis -> connect('127.0.0.1',6379);
$redis -> auth('root');
$t1 = time();
for ($i = 0 ; $i<10000;$i++){
$redis->set("key::$i".str_pad($i,4,'0',0));
$redis->get("key::$i");
}
$etime = microtime(true);//获取程序执行结束的时间
$total = ($etime-$stime);//计算时间差
echo "[页面执行时间:{$total}]s";
echo PHP_EOL;
echo '运行后内存:'.memory_get_usage()."";
echo PHP_EOL;
使用管道
<?php
$stime = microtime(true);
echo "开始内存:".memory_get_usage().'';
echo PHP_EOL;
$redis = new Redis();
$redis -> connect('127.0.0.1',6379);
$redis-> auth('root');
//$pipe = $redis->multi($redis::PIPELINE);将多个操作当成一个事务执行
$pipe = $redis->pipeline();//(多条)执行命令简单的,更加快速的发送给服务器,但是没有任何的原子性保证
for ($i = 0; $i < 10000 ; $i++){
$pipe->set("key::$i".str_pad($i,4,'0',0));
$pipe->get("key::$i");
}
$replies = $pipe->exec();
$etime = microtime(true);
$total = ($etime-$stime);
echo "[页面执行时间:{$total}]s";
echo PHP_EOL;
echo '运行后内存:'.memory_get_usage().'';
echo PHP_EOL;