与mysql同样:当执行时间超过阀值,会将发生时间 耗时 命令记录java
redis命令生命周期:发送 排队 执行 返回mysql
慢查询只统计第3个执行步骤的时间redis
预设阀值:两种方式,默认为10毫秒sql
1.动态设置6379:> config set slowlog-log-slower-than 10000 //10毫秒10000微秒数组
使用config set完后,若想将配置持久化保存到redis.conf,要执行config rewrite 服务器
2.redis.conf修改:找到slowlog-log-slower-than 10000 ,修改保存便可微信
注意:slowlog-log-slower-than =0记录全部命令 -1命令都不记录网络
原理:慢查询记录也是存在队列里的,slow-max-len 存放的记录最大条数,好比设置的slow-max-len=10,当有第11条慢查询命令插入时,队列的第一条命令就会出列,第11条入列到慢查询队列中, 能够config set动态设置,也能够修改redis.conf完成配置。数据结构
命令:并发
获取队列里慢查询的命令:slowlog get 查询返回的结构以下
获取慢查询列表当前的长度:slowlog len //以上只有1条慢查询,返回1
对慢查询列表清理(重置):slowlog reset //再查slowlog len 此时返回0 清空
对于线上slow-max-len配置的建议:
线上可加大slow-max-len的值,记录慢查询存长命令时redis会作截断,不会占用大量内存,线上可设置1000以上
对于线上slowlog-log-slower-than配置的建议:
默认为10毫秒,根据redis并发量来调整,对于高并发比建议为1毫秒
注意:
1 慢查询只记录命令在redis的执行时间,不包括排队、网络传输时间
2 慢查询是先进先出的队列,访问日志记录出列丢失,需按期执行slow get,将结果存储到其它设备中(如mysql)
./redis-cli -r 3 -h 192.168.1.111 -a 12345678 ping //返回pong表示127.0.0.1:6379能通,r表明次数
./redis-cli -r 100 -i 1 info |grep used_memory_human //每秒输出内存使用量,输100次,i表明执行的时间间隔
./redis-cli -p 6379 -h 192.168.1.111 -a 12345678
对于咱们来讲,这些经常使用指令以上可知足,但若是要了解更多执行redis-cli --help, 可百度
./redis-server ./redis.conf & //指定配置文件启动
./redis-server --test-memory 1024 //检测操做系统可否提供1G内存给redis, 经常使用于测试,想快速占满机器内存作极端条件的测试,可以使用这个指令
redis上线前,作一次测试
redis-benchmark -h 192.168.1.111 -c 100 -n 10000 //100个客户端同时请求redis,共执行10000次
会对各种数据结构的命令进行测试
测试命令事例:
一、redis-benchmark -h 192.168.1.111 -p 6379 -c 100 -n 100000
100个并发链接,100000个请求,检测host为localhost 端口为6379的redis服务器性能
二、redis-benchmark -h 192.168.1.111 -p 6379 -q -d 100
测试存取大小为100字节的数据包的性能
三、redis-benchmark -h 192.168.1.111 -t set,lpush -n 100000 -q
只测试 set,lpush操做的性能,-q只显示每秒钟能处理多少请求数结果
四、redis-benchmark -h 192.168.1.111 -n 100000 -q script load "redis.call('set','foo','bar')"
只测试某些数值存取的性能, 好比说我在慢查询中发现,大部分为set语句比较慢,咱们本身能够测一下Set是否是真的慢,
pipeline 出现的背景:
redis客户端执行一条命令分4个过程:
发送命令-〉命令排队-〉命令执行-〉返回结果
这个过程称为Round trip time(简称RTT, 往返时间),mget mset有效节约了RTT,但大部分命令(如hgetall,并无mhgetall)不支持批量操做,须要消耗N次RTT ,这个时候须要pipeline来解决这个问题
1,未使用pipeline执行N条命令
2,使用pipeline执行N条命令,后面会讲到JAVA的jedis如何使用pipeline功能
3,使用pipeline和未使用pipeline的性能对比:
小总结:使用pipeline执行速度比逐条执行要快,客户端与服务端的网络延迟越大,性能体现越明显
4,原生的批命令(mset, mget等)与pipeline的对比:
1.原生批命令是原子性,pipeline是非原子性, (原子性概念:一个事务是一个不可分割的最小工做单位,要么都成功要么都失败。原子操做是指你的一个业务逻辑必须是不可拆分的. 处理一件事情要么都成功要么都失败,其实也引用了生物里概念,分子-〉原子,原子不可拆分)
2.原生批命令一命令多个key, 但pipeline支持多命令(存在事务),非原子性
3.原生批命令是服务端实现,而pipeline须要服务端与客户端共同完成
5,pipeline正确使用方式:
使用pipeline组装的命令个数不能太多,否则数据量过大,增长客户端的等待时间,还可能形成网络阻塞,能够将大量命令的拆分多个小的pipeline命令完成
刚你们知道,pipeline是多条命令的组合,为了保证它的原子性,redis提供了简单的事务,什么是事务?事务是指一组动做的执行,这一组动做要么成功,要么失败。
1.redis的简单事务,将一组须要一块儿执行的命令放到multi和exec两个命令之间,其中multi表明事务开始,exec表明事务结束
2.中止事务discard
3.命令错误,语法不正确,致使事务不能正常结束
4.运行错误,语法正确,但类型错误,事务能够正常结束
能够看到redis不支持回滚功能
watch命令
redis提供了简单事务,之因此说简单,不支持事务回滚
LUA脚本语言是C开发的,相似存储过程
使用脚本的好处以下:
•1.减小网络开销:原本5次网络请求的操做,能够用一个请求完成,原先5次请求的逻辑放在redis服务器上完成。使用脚本,减小了网络往返时延。
•2.原子操做:Redis会将整个脚本做为一个总体执行,中间不会被其余命令插入。
•3.复用:客户端发送的脚本会永久存储在Redis中,意味着其余客户端能够复用这一脚本而不须要使用代码完成一样的逻辑。
6379>eval "return redis.call('get',KEYS[1])" 1 name //eval+脚本+KEYS[1]+键个数+键
eval script numkeys key [key ...]
语法1:
local int sum = 0 local int i =0 while i <= 100 do sum = sum+i i = i+1 end print(sum)
语法2:
local tables myArray={“james”,”java”,false,34} //定义 local int sum = 0 print(myArray[3]) //返回false for i = 1,100 do sum = sum+1 end print(sum) for j = 1,#myArray //遍历数组 do print(myArray[j]) if myArray[j] == “james” then print(“true”) break else print(“false”) end end
案例-实现访问频率限制: 实现访问者 $ip 127.0.0.1在必定的时间 $time 20S内只能访问 $limit 10次.使用JAVA语言实现:
private boolean accessLimit(String ip, int limit, int time, Jedis jedis) { boolean result = true; String key = "rate.limit:" + ip; if (jedis.exists(key)) { long afterValue = jedis.incr(key); if (afterValue > limit) { result = false; } } else { Transaction transaction = jedis.multi(); transaction.incr(key); transaction.expire(key, time); transaction.exec(); } return result; }
以上代码有两点缺陷
可能会出现竞态条件: 解决方法是用 WATCH 监控 rate.limit:$IP 的变更, 但较为麻烦;
以上代码在不使用 pipeline 的状况下最多须要向Redis请求5条指令, 传输过多.
使用lua脚原本处理,包括了原子性:以下
../redis-cli -p 6379 -a 12345678 --eval ipCount.lua 192.168.1.111, 10 20
ttl rate.limit:127.0.0.1
其中 keys[1] = rate.limit:127.0.0.1 argv[1]=10次, argv[2]=20S失效
ipCount.lua内容:
local key = KEYS[1] local limit = tonumber(ARGV[1]) local expire_time = ARGV[2] local is_exists = redis.call("EXISTS", key) if is_exists == 1 then if redis.call("INCR", key) > limit then return 0 else return 1 end else redis.call("SET", key, 1) redis.call("EXPIRE", key, expire_time) return 1 end
执行逻辑:使用redis-cli --eavl时,客户端把lua脚本字符串发给redis服务端,将结果返回客户端,以下图
redis对lua脚本的管理:
1,redis-cli -h 192.168.1.111 -a 12345678 script load "$(cat random.lua)"
将LUA脚本内容加载到redis, 获得 返回的sha1值:afe90689cdeec602e374ebad421e3911022f47c0
redis-cli -h localhost -p 6379 EVALSHA afe90689cdeec602e374ebad421e3911022f47c0 0
再执行获得的值调用
2, 6379〉script exists afe90689cdeec602e374ebad421e3911022f47c0
检查sha1值的LUA脚本是否加载到redis中, 返回1 已加载成功
3, 6379〉script flush
清空加载的lua脚本内容
4,6379〉script kill
杀掉正在执行的LUA脚本,好比LUA比较耗时阻塞,杀掉
redis提供了“发布、订阅”模式的消息机制,其中消息订阅者与发布者不直接通讯,发布者向指定的频道(channel)发布消息,订阅该频道的每一个客户端均可以接收到消息
redis主要提供发布消息、订阅频道、取消订阅以及按照模式订阅和取消订阅
发布消息 publish channel:test "hello world"
订阅消息 subscrible channel:test
此时另外一个客户端发布一个消息:publish channel:test "james test"当前订阅者客户端会收到以下消息:
和不少专业的消息队列(kafka rabbitmq),redis的发布订阅显得很lower, 好比没法实现消息规程和回溯, 但就是简单,若是能知足应用场景,用这个也能够
查看订阅数 pubsub numsub channel:test //频道channel:test的订阅数
取消订阅 unsubscribe channel:test //客户端能够经过unsubscribe命令取消对指定频道的订阅,取消后,不会再收到该频道的消息
按模式订阅和取消订阅 psubscribe ch* //订阅以ch开头的全部频道
punsubscribe ch* //取消以ch开头的全部频道
应用场景:
一、今日头条订阅号、微信订阅公众号、新浪微博关注、邮件订阅系统
二、即便通讯系统
三、群聊部落系统(微信群)
测试实践:微信班级群 class:20170101
一、学生C订阅一个主题叫 :class:20170101
>subscribe class:20170101
二、学生A针对class:20170101主体发送消息,那么全部订阅该主题的用户都可以接收到该数据。
>publish class:20170101 "hello world! I am A"
三、学生B针对class:20170101主体发送消息,那么全部订阅该主题的用户都可以接收到该数据。
>publish class:20170101 "hello world! I am B"
展现学生C接受到的A\B同窗发送过来的消息信息
1) "subscribe"
2) "class:20170101"
3) (integer) 1
1) "message"
2) "class:20170101"
3) "hello world! I am A"
1) "message"
2) "class:20170101"
3) "hello word! I am B"
代码例子见:redis-publish-subscribe工程