文章目录
前言
非关系型数据数据库大量应用于数据缓存、消息队列等场景中,在大数据领域其非结构化特征与内存缓存数据的效果,适用于实时的数据分析,这里简单记录redis、hbase、kafka相关概念和使用
一、Redis基础
Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。其特点是启动时将数据加载至内存,可持久化数据,value数据类型丰富,本身可实现分布式、高可用。
1.Redis安装和配置
下载地址:https://download.redis.io/releases/ 或者wget https://download.redis.io/releases/redis-5.0.8.tar.gz
Redis是基于C语言编写的,因此首先需要安装Redis所需要的gcc依赖:
yum -y install gcc-c++ tcl
之后进行解压、编译、安装。
其主要配置为,log文件服务日志文件地址、datas文件持久化数据地址,
## 69行,配置redis服务器接受链接的网卡,表示任意机器都可以连接Redis服务
bind 0.0.0.0
## 136行,redis是否后台运行,设置为yes
daemonize yes
## 171行,设置redis服务日志存储路径
logfile "/export/server/redis/logs/redis.log"
## 263行,设置redis持久化数据存储目录
dir /export/server/redis/datas/
使用redis-server启动服务,使用redis-cli启动客户端
2.Redis简单操作
redis支持的数据类型有:
官方文档:https://redis.io/commands
客户端命令:help @generic
1、通用操作
- keys:列举当前数据库中所有Key
- 语法:keys 通配符
- del key:删除某个KV
- exists key :判断某个Key是否存在
- 返回值:1,表示存在
- 返回值:0,表示不存在
- type key:判断这个K对应的V的类型的
- expire K 过期时间:设置某个K的过期时间,一旦到达过期时间,这个K会被自动删除
- ttl K:查看某个K剩余的存活时间
- select N:切换数据库的
-
Redis默认由16个数据:db0 ~ db15,个数可以通过配置文件修改,名称不能改
-
Redis是一层数据存储结构:所有KV直接存储在数据库中
-
默认进入db0
-
- flushdb:清空当前数据库的所有Key
- flushall:清空所有数据库的所有Key
2、String类型操作
- set:给String类型的Value的进行赋值或者更新
- 语法:
set K V
- 语法:
- get:读取String类型的Value的值
- 语法:
get K
- 语法:
- mset:用于批量写多个String类型的KV
- 语法:
mset K1 V1 K2 V2 ……
- 语法:
- mget:用于批量读取String类型的Value
- 语法:
mget K1 K2 K3 ……
- 语法:
- setnx:只能用于新增数据,当K不存在时可以进行新增
- 语法:
setnx K V
- 语法:
- incr:用于对数值类型的字符串进行递增,递增1,一般用于做计数器
- 语法:
incr K
- 语法:
- incrby:指定对数值类型的字符串增长固定的步长
- 语法:
incrby K N
- 语法:
- decr:对数值类型的数据进行递减,递减1
- 语法:
decr K
- 语法:
- decrby:按照指定步长进行递减
- 语法:
decrby K N
- 语法:
- incrbyfloat:基于浮点数递增
- 语法:
incrbyfloat K N
- 语法:
- strlen:统计字符串的长度
- 语法:
strlen K
- 语法:
- getrange:用于截取字符串
- 语法:
getrange s2 start end
,包头包尾
- 语法:
3、Hash类型操作
Hash相当于表形式,将字典类型的数据按列进行分区,通过field获取
-
hset:用于为某个K添加一个属性
- 语法:
hset K k v
- 语法:
-
hget:用于获取某个K的某个属性的值
- 语法:
hget K k
- 语法:
-
hmset:批量的为某个K赋予新的属性
- 语法:
hmset K k1 v1 k2 v2 ……
- 语法:
-
hmget:批量的获取某个K的多个属性的值
- 语法:
hmget K k1 k2 k3……
- 语法:
-
hgetall:获取所有属性的值
- 语法:
hgetall K
- 语法:
-
hdel:删除某个属性
- 语法:
hdel K k1 k2 ……
- 语法:
-
hlen:统计K对应的Value总的属性的个数
- 语法:
hlen K
- 语法:
-
hexists:判断这个K的V中是否包含这个属性
- 语法:
hexists K k
- 语法:
-
hvals:获取所有属性的value的
- 语法:
hvals K
- 语法:
4、List类型操作
redis中的list类型相当于java中的linkedlist即双向链表,其特点是便于修改、添加、删除,不便于查询
-
lpush:将每个元素放到集合的左边,左序放入
- 语法:
lpush K e1 e2 e3……
- 语法:
-
rpush:将每个元素放到集合的右边,右序放入
- 语法:
rpush K e1 e2 e3……
- 语法:
-
lrange:通过下标的范围来获取元素的数据
-
语法:
lrange K start end
-
注意:从左往右的下标从0开始,从右往左的下标从-1开始,一定是从小的到大的下标
-
lrange K 0 -1
:所有元素
-
-
llen:统计集合的长度
- 语法:
llen K
- 语法:
-
lpop:删除左边的一个元素
- 语法:
lpop K
- 语法:
-
rpop:删除右边的一个元素
- 语法:
rpop K
- 语法:
5、Set类型操作
set集合类型类似于hashset,无序、不重复、查找快、支持交集、并集、差集等操作
-
sadd:用于添加元素到Set集合中
- 语法:
sadd K e1 e2 e3 e4 e5……
- 语法:
-
smembers:用于查看Set集合的所有成员
- 语法:
smembers K
- 语法:
-
sismember:判断是否包含这个成员
- 语法:
sismember K e1
- 语法:
-
srem:删除其中某个元素
- 语法:
srem K e
- 语法:
-
scard:统计集合长度
- 语法:
scard K
- 语法:
-
sunion:取两个集合的并集
- 语法:
sunion K1 K2
- 语法:
-
sinter:取两个集合的交集
- 语法:
sinter K1 K2
- 语法:
6、SortSet类型操作
SortSet有序集合,其中有默认score属性,可利用其对集合排序,不重复、查询快
-
zadd:用于添加元素到Zset集合中
- 语法:
zadd K score1 k1 score2 k2 ……
- 语法:
-
zrange:范围查询
- 语法:
zrange K start end [withscores]
- 语法:
-
zrevrange:倒序查询
- 语法:
zrevrange K start end [withscores]
- 语法:
-
zrem:移除一个元素
- 语法:
zrem K k1
- 语法:
-
zcard:统计集合长度
- 语法:
zcard K
- 语法:
-
zscore:获取评分
- 语法:
zscore K k
- 语法:
3.Redis Api操作
利用maven导入redis依赖
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.2.0</version>
</dependency>
建立连接
// 建立连接
Jedis jedis = new Jedis("127.0.0.1", 6379);
// 选择库
jedis.select(0);
释放资源
// 释放资源
if (jedis != null) {
jedis.close();
}
测试代码
public class JedisApiTest {
// 定义Jedis变量
private Jedis jedis = null ;
@Before
public void open(){
// TODO: step1. 获取连接
jedis = new Jedis("127.0.0.1", 6379);
}
@Test
public void testString() throws Exception{
// TODO: step2. 使用连接,操作Redis数据,数据类型为String
/*
set/get/exists/expire/ttl
*/
jedis.set("name","Jack");
System.out.println(jedis.get("name"));
System.out.println(jedis.exists("name"));
System.out.println(jedis.exists("age"));
jedis.expire("name",10);
Long ttl = -1L ;
while(-2 != ttl){
ttl = jedis.ttl("name") ;
System.out.println("ttl = " + ttl);
TimeUnit.SECONDS.sleep(1);
}
}
@After
public void close(){
// TODO: step3. 关闭连接
if(null != jedis) jedis.close();
}
}
Jdeis Poll连接池,数据库连接池,减少频繁的创建和销毁连接
public class JedisPoolTest {
// 定义Jedis变量
private Jedis jedis = null ;
// 初始化
@Before
public void open(){
// TODO: step1. 获取连接
// 1-1. 配置
JedisPoolConfig config = new JedisPoolConfig();
// 连接池中总的连接数
config.setMaxTotal(8);
// 表示连接池中最小有几个连接空闲
config.setMinIdle(3);
// 表示连接池最多有几个连接空闲
config.setMaxIdle(8);
// 1-2. 连接池
JedisPool jedisPool = new JedisPool(config, "127.0.0.1", 6379) ;
// 1-3. 获取连接
jedis = jedisPool.getResource();
}
@Test
public void testString(){
/*
set/get/exists/expire/ttl
*/
jedis.set("name", "Lucy");
System.out.println(jedis.get("name"));
System.out.println("name exists: " + jedis.exists("name"));
System.out.println("age exists: " + jedis.exists("age"));
jedis.expire("name", 10);
System.out.println(jedis.ttl("name"));
}
@After
public void close(){
// TODO: step3. 关闭连接,释放资源
if(null != jedis) jedis.close();
}
}
二、Redis集群
1.Redis 单点问题
redis 持久化RDB持久化
RDB全称Redis Database Backup file(Redis数据备份文件),也被叫做Redis数据快照。简单来说就是把内存中的所有数据都记录到磁盘中。
- 当Redis实例故障重启后,从磁盘读取快照文件,恢复数据。
- 快照文件称为RDB文件,默认是保存在当前运行目录。
- RDB持久化在四种情况下会执行:
- 执行save命令
- 执行bgsave命令
- Redis停机时
- 触发RDB条件时
save与bgsave的区别在于,save命令会导致主进程执行RDB,这个过程中其它所有命令都会被阻塞。只有在数据迁移时可能用到。
bgsave会开启独立进程完成RDB,主进程可以持续处理用户请求,不受影响。
停机时save会自动触发
同时在redis.conf可以配置save触发条件和是否开启压缩:
# 900秒内,如果至少有1个key被修改,则执行bgsave , 如果是save "" 则表示禁用RDB
save 900 1
save 300 10
save 60 10000
# 是否压缩 ,建议不开启,压缩也会消耗cpu,磁盘的话不值钱
rdbcompression yes
# RDB文件名称
dbfilename dump.rdb
# 文件保存的路径目录
dir ./
bgsave开始时会fork主进程得到子进程,子进程共享主进程的内存数据。完成fork后读取内存数据并写入 RDB 文件。
fork采用的是copy-on-write技术:
- 当主进程执行读操作时,访问共享内存;
- 当主进程执行写操作时,则会拷贝一份数据,执行写操作。
redis 持久化AOF持久化
AOF全称为Append Only File(追加文件)。Redis处理的每一个写命令都会记录在AOF文件,可以看做是命令日志文件。
默认关闭。可通过redis.conf开启
# 是否开启AOF功能,默认是no
appendonly yes
# AOF文件的名称
appendfilename "appendonly.aof"
# 表示每执行一次写命令,立即记录到AOF文件
appendfsync always
# 写命令执行完先放入AOF缓冲区,然后表示每隔1秒将缓冲区数据写到AOF文件,是默认方案
appendfsync everysec
# 写命令执行完先放入AOF缓冲区,由操作系统决定何时将缓冲区内容写回磁盘
appendfsync no
RDB和AOF各有自己的优缺点,如果对数据安全性要求较高,在实际开发中往往会结合两者来使用。
2.Redis 主从复制集群
单纯提高集群的并发能力,设置多态服务器,主节点为master,从介节点为salve
主要配置为:
replica-announce-ip
slaveof <masterip> <masterport>
3.Redis 哨兵集群
解决主从集群单点故障问题,实现自动故障转移,通过频繁的ping pong实现。
Redis提供了哨兵(Sentinel)机制来实现主从集群的自动故障恢复,哨兵的结构和作用如下:
- 监控 :Sentinel 会不断检查您的master和slave是否按预期工作
- 自动故障恢复:如果master故障,Sentinel会将一个slave提升为master
- 通知:Sentinel充当Redis客户端的服务发现来源,当集群发生故障转移时,将最新信息推送给Redis的客户端
Sentinel基于心跳机制监测服务状态,每隔1秒向集群的每个实例发送ping命令:
- 主观下线:如果某sentinel节点发现某实例未在规定时间响应,则认为该实例主观下线。
- 客观下线:若超过指定数量(quorum)的sentinel都认为该实例主观下线,则该实例客观下线。quorum值最好超过Sentinel实例数量的一半。
- sentinel给备选的slave1节点发送slaveof no one命令,让该节点成为master
- sentinel给所有其它slave发送slaveof 命令,让这些slave成为新master的从节点,开始从新的master上同步数据。
- 最后,sentinel将故障节点标记为slave,当故障节点恢复后会自动成为新的master的slave节点
4.Redis 分片集群
通过多个集群集成,实现数据分布式存储,高并发写,海量数据存储
- 集群中有多个master,每个master保存不同数据
- 每个master都可以有多个slave节点
- master之间通过ping监测彼此健康状态
客户端请求可以访问集群任意节点,最终都会被转发到正确节点
每个小集群分摊一部分槽位Slot,对每一条Redis的数据进行槽位计算,这条数据属于哪个槽位,就存储对应槽位的小集群中
根据Key进行槽位运算:CRC16[K] & 16383 = 0 ~ 16383
5.Redis 集群API
public class JedisClusterTest {
public static void main(String[] args) {
// TODO: 1. 获取连接
// 1-1. 连接池设置
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(8); // 连接池中总的连接数
config.setMaxIdle(5); // 连接池中最大连接空闲数,连接未被使用数目
config.setMinIdle(2); // 连接池中最小连接空闲数
config.setMaxWaitMillis(5000); // 最大等待时间
// 1-2. 集群地址和端口号
HashSet<HostAndPort> set = new HashSet<>() ;
set.add(new HostAndPort("node1",7001));
set.add(new HostAndPort("node1",7002));
set.add(new HostAndPort("node2",7001));
set.add(new HostAndPort("node2",7002));
set.add(new HostAndPort("node3",7001));
set.add(new HostAndPort("node3",7002));
// 1-3. 获取连接
JedisCluster jedisCluster = new JedisCluster(set, 2000, 2000, 5, config);
// TODO: 2. 使用连接,操作数据
jedisCluster.set("name", "zhangsan");
System.out.println(jedisCluster.get("name"));
// TODO: 3. 关闭连接
jedisCluster.close();
}
}
总结
Redis简单应用。
时光如水,人生逆旅矣。