性能测试
redis-benchmark :官方自带的性能测试工具
例如测试100个 并发连接数,10W请求
redis-benchmark -h localhost -p 6379 -c 100 -n 100000
[root@localhost bin]# redis-benchmark -h localhost -p 6379 -c 100 -n 100000
====== PING_INLINE ======
100000 requests completed in 2.03 seconds
100 parallel clients
3 bytes payload
keep alive: 1
35.19% <= 1 milliseconds
99.11% <= 2 milliseconds
99.96% <= 3 milliseconds
99.99% <= 4 milliseconds
100.00% <= 4 milliseconds
49164.21 requests per second
====== PING_BULK ======
100000 requests completed in 2.00 seconds
100 parallel clients
3 bytes payload
keep alive: 1
35.62% <= 1 milliseconds
99.06% <= 2 milliseconds
99.93% <= 3 milliseconds
100.00% <= 3 milliseconds
50125.31 requests per second
====== SET ======
100000 requests completed in 1.71 seconds //对我们的10万个请求进行写入测试
100 parallel clients //100个并发客户端
3 bytes payload //每次写入3个字节
keep alive: 1 //只有一台服务器来处理这些请求,单机性能
89.18% <= 1 milliseconds
98.98% <= 2 milliseconds
99.97% <= 3 milliseconds
100.00% <= 3 milliseconds //所有请求在3ms处理完成
58582.31 requests per second //每秒处理5W+请求
====== GET ======
100000 requests completed in 1.84 seconds
100 parallel clients
3 bytes payload
keep alive: 1
68.02% <= 1 milliseconds
99.03% <= 2 milliseconds
100.00% <= 3 milliseconds
100.00% <= 3 milliseconds
54288.82 requests per second
====== INCR ======
100000 requests completed in 2.06 seconds
100 parallel clients
3 bytes payload
keep alive: 1
21.52% <= 1 milliseconds
99.84% <= 2 milliseconds
100.00% <= 3 milliseconds
100.00% <= 3 milliseconds
48520.13 requests per second
====== LPUSH ======
100000 requests completed in 2.07 seconds
100 parallel clients
3 bytes payload
keep alive: 1
14.13% <= 1 milliseconds
98.97% <= 2 milliseconds
99.98% <= 3 milliseconds
100.00% <= 3 milliseconds
48309.18 requests per second
====== RPUSH ======
100000 requests completed in 1.79 seconds
100 parallel clients
3 bytes payload
keep alive: 1
81.44% <= 1 milliseconds
99.69% <= 2 milliseconds
99.99% <= 3 milliseconds
100.00% <= 3 milliseconds
55834.73 requests per second
====== LPOP ======
100000 requests completed in 1.92 seconds
100 parallel clients
3 bytes payload
keep alive: 1
63.40% <= 1 milliseconds
99.31% <= 2 milliseconds
99.97% <= 3 milliseconds
100.00% <= 4 milliseconds
52110.47 requests per second
====== RPOP ======
100000 requests completed in 1.87 seconds
100 parallel clients
3 bytes payload
keep alive: 1
68.30% <= 1 milliseconds
99.47% <= 2 milliseconds
100.00% <= 2 milliseconds
53418.80 requests per second
====== SADD ======
100000 requests completed in 2.01 seconds
100 parallel clients
3 bytes payload
keep alive: 1
31.33% <= 1 milliseconds
99.28% <= 2 milliseconds
99.98% <= 3 milliseconds
100.00% <= 3 milliseconds
49850.45 requests per second
====== HSET ======
100000 requests completed in 1.71 seconds
100 parallel clients
3 bytes payload
keep alive: 1
92.71% <= 1 milliseconds
99.14% <= 2 milliseconds
99.87% <= 3 milliseconds
100.00% <= 3 milliseconds
58548.01 requests per second
====== SPOP ======
100000 requests completed in 1.73 seconds
100 parallel clients
3 bytes payload
keep alive: 1
92.11% <= 1 milliseconds
99.67% <= 2 milliseconds
99.99% <= 3 milliseconds
100.00% <= 3 milliseconds
57703.40 requests per second
====== LPUSH (needed to benchmark LRANGE) ======
100000 requests completed in 1.69 seconds
100 parallel clients
3 bytes payload
keep alive: 1
91.87% <= 1 milliseconds
99.51% <= 2 milliseconds
99.98% <= 3 milliseconds
100.00% <= 3 milliseconds
59241.71 requests per second
====== LRANGE_100 (first 100 elements) ======
100000 requests completed in 1.91 seconds
100 parallel clients
3 bytes payload
keep alive: 1
55.47% <= 1 milliseconds
99.28% <= 2 milliseconds
100.00% <= 3 milliseconds
100.00% <= 3 milliseconds
52219.32 requests per second
====== LRANGE_300 (first 300 elements) ======
100000 requests completed in 1.69 seconds
100 parallel clients
3 bytes payload
keep alive: 1
97.01% <= 1 milliseconds
99.80% <= 2 milliseconds
99.99% <= 3 milliseconds
100.00% <= 3 milliseconds
59171.59 requests per second
====== LRANGE_500 (first 450 elements) ======
100000 requests completed in 1.69 seconds
100 parallel clients
3 bytes payload
keep alive: 1
92.69% <= 1 milliseconds
99.40% <= 2 milliseconds
100.00% <= 3 milliseconds
100.00% <= 3 milliseconds
59241.71 requests per second
====== LRANGE_600 (first 600 elements) ======
100000 requests completed in 1.97 seconds
100 parallel clients
3 bytes payload
keep alive: 1
40.08% <= 1 milliseconds
99.65% <= 2 milliseconds
100.00% <= 2 milliseconds
50761.42 requests per second
====== MSET (10 keys) ======
100000 requests completed in 2.03 seconds
100 parallel clients
3 bytes payload
keep alive: 1
24.35% <= 1 milliseconds
98.12% <= 2 milliseconds
99.83% <= 3 milliseconds
99.99% <= 4 milliseconds
100.00% <= 4 milliseconds
49212.60 requests per second
redis单线程为什么这么快(官方说是10W的QPS)
速度:CPU>内存>硬盘
多线程(CPU上下文会切换1500纳秒,耗时的操作),对于内存系统来说。如果没有上下文切换效率就是最高的,多次读写都是在一个CPU上的,在内存情况下,这个就是最佳的方案!
基本命令
查看所有键:keys *
键是否存在:exists 【key】
移动键:move 【key】【目标库】
移除键:del 【key】
设置键过期时间:expire 【key】【过期时间:秒】
查看过期时间:ttl 【key】
查看类型:type【key】
查看信息:info redis info命令详解
基本类型
string
设置步长
字符串范围
分布式锁
setex(set with expire):设置过期时间
setnx(set if not exist):不存在再设置,再分布式锁中经常使用
同时设置多个
getset
list
lpush lrange lpop rpush rpop插入和移除
lindex根据下标获取
llen长度
lrem 移除指定的值(个数)
ltrim修剪、截取
rpoplpush移动元素到新list中
lset(要求list和index必须存在) 替换list指定下标的值
linsert 将某个具体的value插入到某个元素的前面或后面
list实际上是一个链表,before node after ,left ,right 都可以插入值
如果key不存在,创建新的链表
如果key存在,新增内容
如果移除了所有值,空链表,也代表不存在!
在两边插入或者改动值,效率最高!中间元素,相对来说效率低一些
set
sadd smembers sismember
scard 获取个数
srem 移除元素
srandmember 随机取数
spop 随机移除元素
smove移动元素
sinter 交集 sdiff差集 sunion并集(重要)
例如共同关注
hash
用户的信息,经常变动的信息,改对象
存取
获取hash表的key数、判断key是否存在、 只获取key、 只获取值
增减、是否存在再设置(分布式)
zset(有序集合)
zadd【name】【score】【value】
zrangebyscore 按【score】排序
zrange myzset 0 -1 //升序
zrevrange myzset 0 -1 //降序
zrangebyscore myzset 50 250 //升序
zrevrange myzset 250 50 //降序
zrem 移除
zcount 获取指定区间的元素个数
应用
成绩排序,工资排序
普通消息,1,重要消息,2,带权重进行判断
排行榜,取出TopN:zrangebyscore myzset 50 150 withscores limit 0 2
获取某人的排名:ZRANK key member (返回有序集合中指定成员的索引)
geospatial 地理位置
朋友的定位,附近的人,打车距离计算,只有6个命令
geoadd key lng lat member 设置坐标
geopos key member unit获取指定坐标
geodist key member1 member2 两点坐标距离
附近的人?通过半径来查询 :georadius
通过元素的查找给定范围内的元素:georadiusbymember
可以查看不同城市的动态
geohash
将二维的经纬度转为一个字符串,字符串越接近,则距离越近
j
其底层基于zset,所以可用zset命令查看全部元素和移除元素
hyperloglog
允许容错,可用,大数据时速度快,占用内存固定的12kb就能计算2^64个不同元素的基数
转自菜鸟教程
Redis 在 2.8.9 版本添加了 HyperLogLog 结构。Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog
的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的、并且是很小的。在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基
数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。但是,因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog
不能像集合那样,返回输入的各个元素。
bitmaps (位储存)
场景:统计用户信息,活跃or未活跃,登录or未登陆,签到,打卡,凡是两个状态的都可使用bitmaps
bitmaps,数据结构,都是操作二进制位来进行记录,就只有0和1两个状态
365天 = 365bit,1字节 = 8bit,46个字节
setbit key offset value
设置张三各个日期的签到,value只能是0或1
getbit key offset
获取值
bitcount 统计1的个数
布隆过滤器 (大数据去重)
原理
当一个元素加入布隆过滤器中的时候,会进行如下操作:
- 使用布隆过滤器中的哈希函数对元素值进行计算,得到哈希值(有几个哈希函数得到几个哈希值)。
- 根据得到的哈希值,在位数组中把对应下标的值置为 1。
当我们需要判断一个元素是否存在于布隆过滤器的时候,会进行如下操作:
- 对给定元素再次进行相同的哈希计算;
- 得到值之后判断位数组中的每个元素是否都为 1,如果值都为 1,那么说明这个值在布隆过滤器中,如果存在一个值不为
,说明该元素不在布隆过滤器中。
如图所示,当字符串存储要加入到布隆过滤器中时,该字符串首先由多个哈希函数生成不同的哈希值,然后在对应的位数组的下表的元素设置为 1(当位数组初始化时 ,所有位置均为0)。当第二次存储相同字符串时,因为先前的对应位置已设置为1,所以很容易知道此值已经存在(去重非常方便)。
如果我们需要判断某个字符串是否在布隆过滤器中时,只需要对给定字符串再次进行相同的哈希计算,得到值之后判断位数组中的每个元素是否都为 1,如果值都为 1,那么说明这个值在布隆过滤器中,如果存在一个值不为 1,说明该元素不在布隆过滤器中。
不同的字符串可能哈希出来的位置相同,这种情况我们可以适当增加位数组大小或者调整我们的哈希函数。
综上,我们可以得出:布隆过滤器说某个元素存在,小概率会误判。布隆过滤器说某个元素不在,那么这个元素一定不在。
布隆过滤器使用场景
判断给定数据是否存在:比如判断一个数字是否在于包含大量数字的数字集中(数字集很大,5亿以上!)、 防止缓存穿透(判断请求的数据是否有效避免直接绕过缓存请求数据库)等等、邮箱的垃圾邮件过滤、黑名单功能等等。
去重:比如爬给定网址的时候对已经爬取过的 URL 去重
持久化方式
Redis相比Memcache有个最显著的区别就是支持数据持久化,可以将数据写入到硬盘中,而不仅仅是只能存放于内存,重启服务后数据就没了。Redis的持久化分为了rdb快照持久化和aof日志持久化两种方式:
1、RDB快照持久化:每隔一段时间或者操作次数达到一定量后,从内存Dump数据形成rdb文件,RDB文件是经过压缩的二进制数据,文件会比较小,恢复速度快。RDB持久化的缺点是有可能导致一部分数据遗失
2、 AOF日志持久化:可以实现将每次的操作都持久化,数据最为安全,但是影响性能。这两种方式一般会同时配置,但建议在从库上执行数据的持久化,减少对主库性能的影响。
二、手动保存Redis数据
在配置RDB和AOF前还需要了解下保存Redis数据的命令——save和bgsave,在生产环境中建议使用bgsave进行保存数据,它会fork出一个子进程来读取内存中的数据并写入到文件中,这期间还借助写时复制机制,不会阻塞客户端的读写请求。但是由于fork出来了新进程,在保存数据的时候内存的消耗也会double,要小心OOM风险。
三、RDB持久化配置方法
编辑redis.conf配置文件,主要修改以下内容:
save 900 1 #在900秒内,key值发生变化次数超过1次则进行持久化,生产环境通常默认这个值即可
save 300 10 #在300秒内,key值发生变化次数超过10次则进行持久化,生产环境通常默认这个值即可
save 60 10000 #在60秒内,key值发生变化次数超过10000次则进行持久化,生产环境通常默认这个值即可
stop-write-on-bgsave-error yes #rdb文件在导出过程中出错了的话,Redis将停止写入数据,避免数据不一致
rdbcompression yes #压缩数据
rdbchecksum yes #检查rdb文件的完整性
dbfilename dump.rdb #rdb文件名
dir /var/lib/redis/ #rdb文件保存路径
当设置完后重启Redis服务,然后在工作中只要达到了save条件,key就会被保存。并且日志中也会有“xx changes in xx seconds. Saving”这样的记录。如果要关闭rdb功能的话将save选项改为save “”,引号里为空即可
四、AOF持久化配置方法
编辑配置文件,修改以下内容:
appendonly yes #打开aof功能
#appendfsync no #让系统自己决定什么时候进行持久化
#appendfsync always #每次有命令发生都写入到磁盘
appendfsync everysec #每秒写一次数据到磁盘,比如折中的办法,推荐
no-appendfsync-on-rewrite yes #如果正在导出rdb数据,停止aof的写入(aof将保存在一个队列中,rdb备份完成后执行队列,不会丢失数据)
auto-aof-rewrite-percentage 100 #aof文件体积与上次相比增长率达到100%就进行重写(重写相当于记总账,比如对同一个key做了100次操作,我们只需要最后一次的操作,重写就会把多余的操作给忽略掉)
auto-aof-rewrite-min-size 64m #和上一项组合使用,aof文件达到64M时进行重写(重写会节省掉空间,因为多余的操作被删除了)
appendfilename "appendonly.aof" #aof文件名
dir /data/redis6379
注意:
1、持久化不能取代备份,最好周期性的进行备份,过程如下
bgsave #在Redis客户端执行该命令后会在Redis所在目录创建dump.rdb文件,将该文件保存好,需要恢复数据的时候将dump.rdb移动到Redis所在目录后启动服务
2、当Redis服务启动进行数据恢复时,如果rdb和aof文件都存在,会优先使用aof文件来恢复数据(因为aof即时性高于rdb),所以如果rdb有数据时,再开启aof选项会把已经存储过的数据清空