1 redis高级的数据类型HyperLogLog
1.1 介绍
Redis在2.8.9的版本中添加了HyperLogLog结构,HyperLogLog是用来做基数统计的算法,HyperLogLog的优点是,在输入元素的数量或者体积非常非常大时,
计算基数所需的空间总是固定的,并且是很小的。
在Redis里面,每个HyperLogLog键只需要花费12kb内存,就可以计算接近264个不同元素的技术。这和计算技术,元素越多消耗内存就越多的集合形成鲜明对比。
但是,因为HyperLogLog只会根据输入元素来计算基数,而不会存储输入元素本身,所以HyperLogLog不能像集合那样,返回输入的各个元素。
什么是基数?
比如数据集{1,3,4,7,5,7,8},那么这个数据集的基数集为{1,3,4,5,7,8},基数(不重复元素)为6,说白了也就是数据集中去除重复元素个数。基数估计就是在误差可
接受的范围内,快速计算基数。
总之:
①对度量值进行基数统计,使用的是Hyperloglog算法,占用内存小,误差小 。
②redis针对于海量数据的存储,使用到了一种过滤技术:布隆(bloom)过滤器,底层使用的就是HyperLogLog结构。
HyperLogLog与布隆过滤器都是针对大数据统计存储应用场景下的知名算法。
HyperLogLog是在大数据的情况下关于数据基数的空间复杂度优化实现,布隆过滤器是在大数据情况下关于检索一个元素是否在一个集合中的空间复杂度优化后的实现。
→ 共性:都是为了节省存储空间。
HyperLogLog:针对基数进行空间复杂度优化的算法。
布隆过滤器:判断新的元素在集合中是否存在的空间复杂度优化后的算法。
1.2 HyperLogLog实操
[root@NODE01 ~]# redis-cli -h node01
node01:6379> PFADD nosql hbase redis
(integer) 1
node01:6379> PFCOUNT nosql
(integer) 2
node01:6379> PFADD db mysql oracle
(integer) 1
node01:6379> PFADD db mysql oracle
(integer) 0
node01:6379> PFCOUNT nosql
(integer) 2
node01:6379> PFCOUNT db
(integer) 2
node01:6379> PFMERGE alldb nosql db
OK
node01:6379> PFCOUNT alldb
(integer) 4
node01:6379> type nosql
string
node01:6379>
说明:指令操作的key的类型是hyperloglog类型,指令底层封装了HyperLogLog算法。
2 使用redis进行消息的发布和订阅
2.1 介绍
Redis发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。
Redis客户端可以订阅任意数量的频道。(频道:类似于kafka中的主题)
命令 描述
————————————————————————————————————————
PSUBSCRIBE pattern [pattern ...] 订阅一个或多个符合给定模式的频道
PUSBSUB subcommand [argument [argument...]] 查看订阅与发布系统状态
PUBLISH channel message 将消息发送到指定的频道
PUNSUBSCRIBE [ pattern [pattern ...]] 退订所有给定模式的频道
SUBSCRIBE channel [channel ...] 订阅给定的一个或多个频道的信息
UNSUBSCRIBE [channel [channel ...]] 指退订给定的频道
注意:
①只有Master节点才能发布消息
②slave节点可以订阅相应的channel
2.2 实操
消息的发布方:
node01:6379> PUBLISH nosql 学习nosql了
(integer) 0
node01:6379> PUBLISH nosql 学习nosql啦
(integer) 0
消息的订阅方:
[root@NODE02 ~]# redis-cli -h node02
node02:6379> SUBSCRIBE nosql
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "nosql"
3) (integer) 1
1) "message"
2) "nosql"
3) "are you ok?"
1) "message"
2) "nosql"
3) "are you nosql?"
[root@NODE03 ~]# redis-cli -h node03
node03:6379> SUBSCRIBE nosql db
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "nosql"
3) (integer) 1
1) "subscribe"
2) "db"
3) (integer) 2
1) "message"
2) "nosql"
3) "are you nosql?"
3 redis其他常用命令
Redis的常用命令主要分为两个方面、一个是键值相关命令、一个是服务器相关命令
1、键值相关命令
keys * 取出当前所有的key
exists name 查看redis是否有name这个key
del name 删除key name
expire confirm 100 设置confirm这个key100秒过期 ,→ 阅后即焚
ttl confirm 获取confirm 这个key的有效时长
select 0 选择到0数据库 redis默认的数据库是0~15一共16个数据库
move confirm 1 将当前数据库中的key移动到其他的数据库中,
persist confirm 移除confirm这个key的过期时间
randomkey 随机返回数据库里面的一个key
rename key2 key3 重命名key2 为key3
type key2 返回key的数据类型
2、服务器相关命令
ping PONG 返回响应是否连接成功
echo 在命令行打印一些内容
select 0~15 编号的数据库
quit /exit 退出客户端
dbsize 返回当前数据库中所有key的数量
info 返回redis的相关信息
config get dir/* 实时传储收到的请求
flushdb 删除当前选择数据库中的所有key
flushall 删除所有数据库中的数据库
4 redis的安全性(设置访问的密码)
===>主题:Redis高级教程之安全性
我们可以通过redis的配置文件设置密码参数,这样客户端连接到redis服务就需要密码验证,这样可以让你的redis服务更安全。
实例:
我们可以通过以下命令查看是否设置了密码验证:
NODE01:6379> CONFIG GET requirepass
1) "requirepass"
2) ""
[root@NODE01 ~]# redis-cli -h NODE01
NODE01:6379> keys *
(error) NOAUTH Authentication required.
NODE01:6379> AUTH 123
OK
NODE01:6379> KEYS *
1) "2018-05-31"
2) "2018-06-02"
3) "2018-05-29"
4) "2018-05-30"
5) "name_"
6) "BLOOM_BITS"
7) "2018-06-01"
8) "address"
默认情况下requirepass参数是空的,这就意味着无需密码验证就可以连接到redis服务。如果设置密码,客户端连接redis服务就需要密码验证。否则无法执行命令。设置方式:Jedis的实例.auth(密码);
5 redis的数据备份和恢复
备份
Redis SAVE命令用于创建当前数据的备份
恢复
如果需要恢复数据,只需要将备份文件(dump.rdb)移动到redis安装目录并启动服务即可。获取redis目录可以使用config命令。
Bgsave
创建redis备份文件可以使用命令BGSAVE,该命令在后台运行。
实例
redis localhost:6379> BGSAVE
Background saving started
默认有备份到机制(下述是默认的配置,满足其中任何一条就会触发自动的备份):
save 900 1
save 300 10
save 60 10000
作者说明:
# In the example below the behaviour will be to save:
# after 900 sec (15 min) if at least 1 key changed
# after 300 sec (5 min) if at least 10 keys changed
# after 60 sec if at least 10000 keys changed
6 redis中使用管道pipeline优化程序(redis中的批处理)
源码以及效果
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;
import java.io.IOException;
public class JedisClientPip {
private Jedis jedis;
@Before
public void setup() {
jedis = new Jedis("NODE01", 6379, 2000);
//设置密码
jedis.auth("123");
}
@Test
public void testBatchDealWith() {
//需求:将一个String类型的key从1递增到10000,分别使用普通方式和批处理方式进行验证。
//方式1:普通方式
long beginTime = System.currentTimeMillis();
for (int i = 1; i <= 10000; i++) {
jedis.incr("num4");
}
String numValue = jedis.get("num4");
long endTime = System.currentTimeMillis();
System.out.printf("普通方式,递增10000次后的结果是:%s,耗时:%d ms %n%n", numValue, (endTime - beginTime));
//结果:普通方式,递增10000次后的结果是:10000,耗时:3878 ms
System.out.println("\n____________________________________________\n");
//方式2:批处理方式
beginTime = System.currentTimeMillis();
Pipeline pipeline = jedis.pipelined();
pipeline.multi();
for (int i = 1; i <= 10000; i++) {
pipeline.incr("numOther");
}
//执行
pipeline.exec();
try {
pipeline.close();
} catch (IOException e) {
e.printStackTrace();
}
numValue = jedis.get("numOther");
endTime = System.currentTimeMillis();
System.out.printf("批处理方式,递增10000次后的结果是:%s,耗时:%d ms %n%n", numValue, (endTime - beginTime));
//结果:批处理方式,递增10000次后的结果是:29945,耗时:100 ms
}
@After
public void cleanUp() {
if (jedis != null) {
jedis.close();
}
}
}
/*注意点:
1,批处理需要开启事务
2,Pipeline使用完毕之后,需要close*/
7 redis用于性能测试的脚本
Redis性能测试是通过同时执行多个命令实现的。(记在redis集群中,读的效率是~> 11万次/秒; 写的效率是~> 8.1万次/秒)
语法
redis-benchmark [option] [option-value]
实例
以下实例同时执行100000个请求来检测性能(选择其一测试即可):
1)、redis-benchmark -h NODE01 -n 100000
2)、redis-benchmark -h NODE01 -p 6379 -t set,lpush -n 100000 -q
说明:以上实例中主机为NODE01,端口号为6379,执行的命令为set,lpush,请求数为100000,通过-q参数让结果只显示每秒执行的请求数
8 redis持久化
→ rdb方式
RDB方式的持久化是通过快照(snapshotting)完成的,当符合一定条件时Redis会自动将内存中的数据进行快照并持久化到硬盘。
RDB是Redis默认采用的持久化方式,在redis.conf配置文件中默认有此下配置:
save 900 1
save 300 10
save 60 10000
save开头的一行就是持久化配置,可以配置多个条件(每行配置一个条件),每个条件之间是“或”的关系,“save 900 1”表示15分钟(900秒钟)内至少1个键被更改则进行快照,“save 300 10”表示5分钟(300秒)内至少10个键被更改则进行快照。
→ AOF(append only file)方式
开启设置:
在redis.conf配置文件中,设置参数(默认值是no):
appendonly yes
特点:
①会在文件appendonly.aof中记录用户操作的命令(注意:存储的不是数据。)
②如果rdb方式和aof方式同时使用的话,那么默认从aof文件中加载数据(会将文件中保存的指令重新执行一遍,此时内存中就会存在数据了)。
③AOF持久化配置,在Redis的配置文件中存在三种同步方式,它们分别是:
appendfsync always #每次有数据修改发生时都会写入AOF文件。
appendfsync everysec #每秒钟同步一次,该策略为AOF的缺省策略。
appendfsync no #从不同步。高效但是数据不会被持久化。
注意点:
演示时,需要将集群中所有的节点的redis.conf文件中开启aof持久化方式。只开启一个,没有效果!
总结:
针对于redis的持久化,到底选用rdb还是选用aof方式,根据实际的业务来定夺。
rdb:数据量大,优势:效率高,劣势:存在着数据丢失的情形(如:没有满足持久化的任意一个条件,此时,若redis server异常终止了,就会造成数据丢失)。
aof :数据量小,对数据的精确性要求高,要求零丢失。优势:安全,精度高; 劣势:若数据量庞大,恢复时要耗费大量的时间(将appendonly文件中所有的指令都会重新执行一遍)。
9 redis事务
Redis事务可以一次执行多个命令,并且带有以下两个重要的保证:
事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。
一个事务从开始到执行会经历三个阶段:
开始事务。
命令入队。
执行事务。
实例:
以下是一个事务的例子,它先以MULTI开始一个事务,然后将多个命令入队到事务列表中,最后又EXEC命令触发事务,一并执行事务中的所有命令:
MULTI 标记一个事务块的开始
EXEC 执行所有事务块内的命令
node01:6379> MULTI
OK
node01:6379> set address 'beijing'
QUEUED
node01:6379> sett add1 'ok'
(error) ERR unknown command 'sett'
node01:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.
node01:6379> get address
(nil)
node01:6379> MULTI
OK
node01:6379> set address 'wuhan'
QUEUED
node01:6379> set name 'jackson'
QUEUED
node01:6379> exec
1) OK
2) OK
node01:6379> get address
"wuhan"
node01:6379> get name
"jackson"
node01:6379>
3.2.10.1 搭建步骤介绍
集群搭建步骤:
1, Ruby环境准备
yum install -y ruby
yum install -y rubygems
2, 机器准备
①在/opt/redis-cluster下准备redis01 (复制于之前的/opt/redis,删除dump.rdb, appendfile.aof,/opt/redis/logs/redis.log)
修改redis01下相应的配置信息:
bind NODE01
port 7001
cluster-enable yes
cluster-config-file nodes-7001.conf
②参照redis01,准备NODE01上的redis02
port 7002
cluster-config-file nodes-7002.conf
③将NODE01上的redis-cluster跨机器复制到NODE02,NODE03上
scp -r redis-cluster root@NODE02:/opt
为了增强可读性,将其他节点上的redis目录的名字以及端口号进行变更。
如:
NODE02:redis03 ~> 7003
redis04 ~> 7004
修改处:
port 7003
cluster-config-file nodes-7003.conf
bind node02
NODE03: redis05 ~> 7005
redis06 ~> 7006
3,使用ruby脚本(redis-trib.rb)来创建集群
伪分布式集群:
./redis-trib.rb create --replicas 1 192.168.8.101:7001 192.168.8.101:7002 192.168.8.101:7003 192.168.8.101:7004 192.168.8.101:7005 192.168.8.101:7006
分布式集群(前提:启动所有的redis server):
./redis-trib.rb create --replicas 1 192.168.8.101:7001 192.168.8.102:7003 192.168.8.103:7005 192.168.8.101:7002 192.168.8.102:7004 192.168.8.103:7006
注意:
1, 创建redis-trib.rb来创建集群时,指定相应的ip地址 (实验的结果)
2, 报错:
ERR Slot xxx is already busy (Redis::CommandError)
或
[ERR] Node 192.168.8.102:7003 is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0
应对措施:
依次进入到redis集群中的每一台server中,清空db,且重置集群
清空db: flushall
重置集群:cluster reset
3, redis.conf配置文件中需要修改:
bind NODE01 ~> 可以书写,也可以省略配置
cluster-config-file nodes-7001.conf ~> 文件名为:nodes-端口号.conf
cluster-enabled yes ~> 启用集群
4,安装ruby脚本运行使用的包
gem install redis-3.0.0.gem 若是安装失败,下载 redis-3.0.0.gem,进行离线安装。
3.2.10.2. 正式搭建集群
[root@NODE01 src]# redis-trib.rb create --replicas 1 192.168.8.101:7001 192.168.8.102:7003 192.168.8.103:7005 192.168.8.101:7002 192.168.8.102:7004 192.168.8.103:7006
>>> Creating cluster
>>> Performing hash slots allocation on 6 nodes...
Using 3 masters:
192.168.8.101:7001
192.168.8.102:7003
192.168.8.103:7005
Adding replica 192.168.8.102:7004 to 192.168.8.101:7001
Adding replica 192.168.8.101:7002 to 192.168.8.102:7003
Adding replica 192.168.8.103:7006 to 192.168.8.103:7005