# 1.文档型数据库
MongoDB
#2.图片
分布式文件系统FastDFS
- hadoop : HDFS
-淘宝 : TDS
- 阿里云 :OOS
# 3.搜索
- Isearch 多隆
- 搜索引擎 elasticSearch
# 4.内存数据库
redis
# 5.思想层面的提高远比知识更重要
# 列存储数据库
HBase
#key-value数据库
cc
Redis
- 内存存储
- 高速缓存
- 发布订阅系统
- 地图信息
在linux服务器上搭建redis:
连接:
切换数据库
redis key
key * #查看所有的key
set name chenweibin #设置key
get name #获取key
expire name 10 #设置key的过期时间
ttl name # 查看key 的剩余时间
exists name #判断当前key是否存在
https://www.redis.net.cn/
服务器最初的所有文件夹
安装Redis
用docker安装 (见docker博文)
Redis存储数据的结构
常用的5中存储结构
- key-string :一个key对用一个值
- key-hash :一个key对应一个map 一般用来存储对象
- key-list: 一个key对应一个列表 可以用来实现队列和栈结构
- key-set:一个key对应一个集合 交集,差集,并集
- key-zset: 一个key对应一个有序的集合 排行榜,积分存储
另外三种数据结构:
HyperLog:计算近似值
GEO:地理位置
BIT:一般存储的也是一个字符串,存储的是一个byte[]
常用命令
1. string常用命令
# 1.添加值
set key value
# 2 取值
get key
#3.批量操作
mset key value [key value ...]
#4.自增命令 例如点赞功能
incr key
#5.自减命令
decr key
#6.自增或自减指定数量
incrby key increment
decrby key increment
#7.指定值的同时,指定生存时间
setex key second value
#8.设置值 如果当key不存在时
setnx key value
#9.在key对应的value后,追加内容
append key value
#10.查看value字符串的长度
strlen key
2.hash常用命令
# 1.存储数据
hset key field value
# 2.获取数据
hget key field
# 3.批量操作
hmset key field vlaue[field value]
# 4.自增
hincrby key field increment
#5. 设置值 如果不存在则添加 存在什么也不做
hsetnx key field vlaue
#6. 检查field是否存在
hexists key field
#7.删除key对应的field。可以删除多个
hdel key field [field]
# 8.获取当前hash结构中的全部field和value
hgetall key
# 9.获取当前hash结构中的全部field
hkeys key
# 10.获取当前hash结构中的全部value
hvals key
# 获取hash的长度
hlen key
3.list常用命令
#1.存储数据 从左侧插入数据 从右侧插入
lpush key value [value]
rpush key value [value]
#2.存储数据(如果key不存在,也就是列表不存在,什么也不做,如果key存在但不是list结构么也不做)
lpushx key value
rpushx key value
#3.修改数据(并指定索引位置,会覆盖之前所在位置的数据)
lset key index value
# 4.获取数据 弹栈的方式
lpop key
rpop key #所以list可以实现栈或者队列
# 5.指定索引范围的数据(start从0开始 stop从输入-1代表最后一个,-2代表倒数第二个)
lrange key start stop
# 6.获取指定索引位置的数据
lindex key index
# 7.获取整个列表的长度
llen key
#8.删除列表中的数据(删除当前列表中的count个value值,count>0从左向右删除,count<0从右向左删除,count=0删除全部数据)
lrem key count value
# 9.保留列表中的数据(保留你指定索引范围内的数据,超过指定索引范围的被移除掉)
ltrim key start stop
# 10.将一个列表中的最后一个数据,插入到另外一个列表的头部位置
rpoplpush list1 list2
4.set常用命令
# 1.存储数据
sadd key member[member]
#2.获取全部数据
smembers key
#3.随机获取数据 (获取的同时移除数据)
spop key [count] #count 代表弹出数据的个数
# 4.交集
sinter set1 set2 ...
#5.并集
sunion set1 set2 ...
# 6.差集
sdiff set1 set2
# 7.删除数据
srem key member [member]
#8.查看当前的set集合中是否包含这个值
sismember key member
5.zset常用命令
有序不允许重复
#1.添加一个数据 (score必须是数值 member不允许重复)
zadd key score member [score member ...]
#2.修改member的分数(如果member是存在于key中的,正常增加分数,如果member不存在,这个命令相当于zadd)
zincrby key increment member
# 查看指定member的分数
zscore key member
#获取zset中数据的数量
zcard key
#根据score的范围查询member数量
zcount key min max
#删除zset中的成员
zrem key member [member]
#根据分数从小到大的排序,获取指定范围内的数据(withscores 如果添加这个参数,那么会返回member对应的分数)
zrange key start stop [withscores]
#根据分数从大到小 排序,获取指定范围内的数据(withscores 如果添加这个参数,那么会范返回member对应的分数)
zrevrange key start stop [withscores]
# 根据分数的返回去获取member(withscores代表同时返回score 添加limit作用和mySQL一样)
zrangebyscore key min max [withscores] [limit offset count]
# 根据分数的返回去获取member(withscores代表同时返回score 添加limit作用和mySQL一样)
zrangebyscore key max min [withscores] [limit offset count]
6.key常用的命令
# 查看redis中全部的key
keys pattern
# 查看某一个key是否存在(1存在 0不存在)
exists key
# 删除key
del key [key ...]
# 设置key的生存时间 单位为秒 单位为毫秒 设置还能活多久
expire key second
pexpire key milliseconds
# 设置key的生存时间 单位为秒 单位为毫秒 设置能活到哪个时间点
expireat key timestamp
pexpireat key milliseconds
# 查看key的剩余生存时间 单位为秒 毫秒(-2 当前key不存在 -1 当前未设置生存时间
ttl key
pttl key
#移除key的生存时间 使永久存在服务器中 1 移除成功 0不存在生存时间 或者不存在key
persist key
#选择操作的库
select 0~15
# 移动key到另外一个库
move key db
7.库的常用命令
# 清空当前所在的库
flushdb
# 清空所有的库
flushall
# 查看当前数据库中有多少个key
dbsize
# 查看最后一次操作的时间
lastsave
# 实时监控redis
monitor
java连接redis
Jedis连接redis ; Lettuce连接redis
选择Jedis
Jedis连接redis
创建maven空项目
导入依赖
- Jedis
- Junit
- lombok
<dependencies>
<dependency>
<groupId></groupId>
<artifactId></artifactId>
</dependency>
<!--Jedis-->
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<!--Junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>compile</scope>
</dependency>
</dependencies>
测试
package com.testredis;
import org.junit.Test;
import redis.clients.jedis.Jedis;
public class demo1 {
@Test
public void test(){
// 1.连接redis
Jedis jedis =new Jedis("139.224.44.232",6379);
// 2。操作redis redis中命令是什么 jedis的方法就是什么
jedis.set("name","chenweibin");
// 3.释放连接
jedis.close();
}
}
jedis存储对象 byte[]
创建一个实体类 使用lombok byte[]存储因此实现序列化
package com.entity;
import lombok.*;
import java.io.Serializable;
import java.util.Date;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {
Integer id;
String name;
Date date;
}
存储与获取 (转化为byte数组)导入依赖spring-context
package com.testredis;
import com.entity.User;
import lombok.Data;
import org.junit.Test;
import org.springframework.util.SerializationUtils;
import redis.clients.jedis.Jedis;
import java.util.Date;
public class objectDemo {
@Test
public void setByteArray(){
// 1.连接redis
Jedis jedis =new Jedis("139.224.44.232",6379);
// 2.1 准备redis中的Key-value
String key ="user";
User value=new User(1,"陈伟斌",new Date());
// 2.2用Spring-context中的工具类将key-value转化为byte[] 对象需要实现序列化
byte[] keyByte = SerializationUtils.serialize(key);
byte[] valueByte = SerializationUtils.serialize(value);
// 2.3存储到redis
jedis.set(keyByte,valueByte);
// 3.释放连接
jedis.close();
}
@Test
public void getByteArray(){
// 1.连接redis
Jedis jedis =new Jedis("139.224.44.232",6379);
// 2.1 准备redis中的Key
String key ="user";
// 2.2将key序列化
byte[] keyByte = SerializationUtils.serialize(key);
// 2.3获取value,并反序列化
byte[] valueByte = jedis.get(keyByte);
User user =(User) SerializationUtils.deserialize(valueByte);
// 2.4输出
System.out.println( user.getName());
// 3.释放连接
jedis.close();
}
}
jedis存储对象 String
导入josn依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
存储获取 使用fastjson将对象转化为字符串
public class ObjectToStringToRedis {
@Test
public void setStringObject(){
// 1.连接
Jedis jedis =new Jedis("139.224.44.232",6379);
// 2.1创建key-value 并将value转为为json字符串
String key="jsonObject";
User value=new User(2,"苏",new Date());
String valueString = JSON.toJSONString(value);
// 2.2存入redis
jedis.set(key,valueString);
// 3.释放连接
jedis.close();
}
@Test
public void getStringObject(){
// 1.连接
Jedis jedis =new Jedis("139.224.44.232",6379);
// 2.1创建key
String key="jsonObject";
// 2.2获取 并将Json字符串转为为对象
String valueString = jedis.get(key);
User user=JSON.parseObject(valueString,User.class);
// 2.3输出
System.out.println("User:"+user);
// 3.释放连接
jedis.close();
}
}
Jedis连接池操作
JedisPool 创建连接池 GenericObjectPoolConfig配置信息
public class RedisPoolTest {
@Test
public void testPool(){
// 1.创建连接池配置信息 (通过读JedisPool的源码发现此对象)
GenericObjectPoolConfig poolConfig =new GenericObjectPoolConfig();
poolConfig.setMaxTotal(100); //连接池中的最大活跃数
poolConfig.setMaxIdle(10); //最大空闲数
poolConfig.setMinIdle(5); //最小空闲数
poolConfig.setMaxWaitMillis(3000); // 当连接池空了之后,多久没有jedis对象,就超时
// 2.创建连接池
JedisPool pool =new JedisPool(poolConfig,"139.224.44.232");
// 3.获取Jedis
Jedis jedis=pool.getResource();
// 4.操作
String value=jedis.get("jsonObject");
System.out.println(value);
// 5.关闭
pool.close();
}
}
Redis管道操作
为什么使用管道? 加快执行时间
因为在操作redis的过程中,执行一个命令需要先发送请求到redis服务器,这个过程要经历网络延迟,以及redis还要给客户端一个响应.
如果有多个命令的话,这个过程就要执行多次,效率低
通过redis管道的方式可以加快速度: 其先将命令存放到客户端的一个Pipeline中,然后一次性将命令发送到Redis,当redis将所有命令执行完之后,再响应给客户端
public class Testpipe {
@Test
public void testPipe(){
long a=System.currentTimeMillis();
// 1.创建连接池
JedisPool pool =new JedisPool("139.224.44.232",6379);
// 2.获取jedis
Jedis jedis = pool.getResource();
/*// 通过普通方式 94995ms
for (int i = 0; i < 10000; i++) {
jedis.incr("tt");
}*/
// 3.创建管道
Pipeline pipeline = jedis.pipelined();
// 4.测试数据 执行10万次 10000 310ms
for (int i = 0; i < 10000; i++) {
pipeline.incr("dd");
}
// 5.执行命令
pipeline.syncAndReturnAll();
// 6.释放资源
jedis.close();
System.out.println(System.currentTimeMillis()-a);
}
}
Redis其他配置及集群
修改yml文件 以方便后期修改redis配置信息 (自己的redis在opt文件夹)
- 挂载数据卷 将配置文件映射出来到自己创建的conf文件夹下
- 执行redis-server命令并加载配置文件
version: '3.1'
services:
redis:
image: daocloud.io/library/redis:5.0.7-alpine
restart: always
container_name: redis
environment:
- TZ=Asia/Shanghai
ports:
- 6379:6379
volumes:
- ./conf/redis.conf:/usr/local/redis.conf
command: ["redis-server","/usr/local/redis.conf"]
- 创建对应的文件夹conf和文件redis.conf
- 删除原来的docker容器docker-compose down
- 创建新的容器 docker-compose up -d
docker exec -it 2bc2f281cf95 /bin/sh
redis-cli
redis AUTH
- 修改redis.conf 来给redis添加密码
重启容器 此时连接需要密码 docker-compose restart
因此用jedis时要用jedis.auth()指定密码
- 也可以在redis命令中设置临时密码 config set requirepass 1234 前提是redis.conf中没有设置密码
redis持久化机制RDB
RDB是redis默认的持久化机制
-
挂载数据卷 将data目录挂载出来 因为data目录存储持久化的数据
volumes: - ./data:/data
然后重新创建容器 先down 后up
-
修改配置文件
#在900秒内,有一个key改变,就执行RDB持久化 save 900 1 #在300秒内,有10个key改变,就执行RDB持久化 save 300 10 #在60秒内,有10000个key改变,就执行RDB持久化 save 60 10000
#开启人rdb持久化压缩
rdbcompression yes
rdb持久化的文件名
dbfilename redis.rdb
```bash
#出现问题可以查看日志文件 以上的配置文件不要在命令同一行写命令 否则无法启动redis
docker logs --tail 50 --follow --timestamps c7(容器id)
shutdown执行立即持久化,在data目录下就会看到持久化后的文件
redis事务
redis事务:一次事务执行过程中,该成功的命令成功,改失败的命令失败
- 开启事务 :multi
- 输入要执行的命令 :全部放到一个队列当中
- 执行事务:exec
- 取消事务:discard
redis事务要发挥功能 需要配置watch监听机制:
在开启事务之前,先通过watch监听一个或者多个key 在开启事务之后 如果有其他客户端修改了我监听的key 事务就会自动取消
watch监听会在执行事务或者事务取消之后,自动消除,不需要手动unwatch
redis持久化机制AOF
修改redis配置文件(已挂载出来)
#代表开启AOF持久化
appendonly yes
#AOF文件名称
appendfilename "myredis.aof"
#AOF持久化时机
#appendfsync always
appendfsync everysec
#appendfsync no
AOF持久化默认关闭,但是官方推荐同时开启AOF和RDB
- AOF持久化速度比RDB慢,因为存储的是一个文本文件而RDB是二进制文件
- 通过同时开启AOF和RDB 当系统宕机后 默认加载AOF中的数据因为AOF中的数据更加全面
- 因此应该同时开启RDB和AOF 否则如果先开启RDB则宕机后,AOF加载的时候会覆盖原来RDB持久化的数据
Redis的主从架构
单机版 redis存在读写瓶颈
创建docker-compose.yml
version: '3.1'
services:
redis1:
image: daocloud.io/library/redis:5.0.7-alpine
restart: always
container_name: redis
environment:
- TZ=Asia/Shanghai
ports:
- 6379:6379
volumes:
- ./conf/redis1.conf:/usr/local/redis.conf
command: ["redis-server","/usr/local/redis.conf"]
redis2:
image: daocloud.io/library/redis:5.0.7-alpine
restart: always
container_name: redis
environment:
- TZ=Asia/Shanghai
ports:
- 7001:6379
volumes:
- ./conf/redis2.conf:/usr/local/redis.conf
links:
- redis1:master
command: ["redis-server","/usr/local/redis.conf"]
redis3:
image: daocloud.io/library/redis:5.0.7-alpine
restart: always
container_name: redis3
environment:
- TZ=Asia/Shanghai
ports:
- 7002:6379
volumes:
- ./conf/redis3.conf:/usr/local/redis.conf
links:
- redis1:master
command: ["redis-server","/usr/local/redis.conf"]
redis2与redis3添加从节点配置如下: 而redis1.conf为空即可
# replicaof <masterip> <masterport> master为linkd设置的别名 6379为容器内的端口
replicaof master 6379
如果master发生故障整个系统的就会崩溃,无法写,即单点故障
Redis哨兵
哨兵可以帮助我们解决主从架构中的单点故障问题
准备哨兵的配置文件 并且在容器内部手动启动哨兵即可
#哨兵后台启动
daemonize yes
# sentinel monitor <master-name> <ip> <redis-port> <quorum>
#<master-name>给主机起名 <ip>主机的ip此处给主机指定了别名 quorum从机个数
#从机
sentinel monitor master master 6379 2
#主机
sentinel monitor master localhost 6379 2
## sentinel down-after-milliseconds <master-name> <milliseconds>
#哨兵每隔多久监听一次redis架构
sentinel down-after-milliseconds master 10000
在conpose-docker.yml添加数据卷 把哨兵的配置文件映射到容器中
- ./conf/sentinel1.conf:/data/sentinel.conf
在容器内部手动启动哨兵即可
#在容器中的data目录下执行 指定配置文件
redis-sentinel sentinel.conf
从机:
当停掉主机之后 哨兵会从其他两个中选一个为主机
docker stop df
主机发生变化
Redis集群
之前是一个主从架构,现在是去中心化的集群 每个redis无主从之分
搭建redis集群
编写docker-compose.yml
version: "3.1"
services:
redis1:
image: daocloud.io/library/redis:5.0.7-alpine
restart: always
container_name: redis1
environment:
- TZ=Asia/Shanghai
ports:
- 7004:7004
- 17004:17004
volumes:
- ./conf/redis1.conf:/usr/local/redis/redis.conf
command: ["redis-server","/usr/local/redis/redis.conf"]
redis2:
image: daocloud.io/library/redis:5.0.7-alpine
restart: always
container_name: redis2
environment:
- TZ=Asia/Shanghai
ports:
- 7005:7005
- 17005:17005
volumes:
- ./conf/redis2.conf:/usr/local/redis/redis.conf
command: ["redis-server","/usr/local/redis/redis.conf"]
redis3:
image: daocloud.io/library/redis:5.0.7-alpine
restart: always
container_name: redis3
environment:
- TZ=Asia/Shanghai
ports:
- 7006:7006
- 17006:17006
volumes:
- ./conf/redis3.conf:/usr/local/redis/redis.conf
command: ["redis-server","/usr/local/redis/redis.conf"]
redis4:
image: daocloud.io/library/redis:5.0.7-alpine
restart: always
container_name: redis4
environment:
- TZ=Asia/Shanghai
ports:
- 7007:7007
- 17007:17007
volumes:
- ./conf/redis4.conf:/usr/local/redis/redis.conf
command: ["redis-server","/usr/local/redis/redis.conf"]
redis5:
image: daocloud.io/library/redis:5.0.7-alpine
restart: always
container_name: redis5
environment:
- TZ=Asia/Shanghai
ports:
- 7008:7008
- 17008:17008
volumes:
- ./conf/redis5.conf:/usr/local/redis/redis.conf
command: ["redis-server","/usr/local/redis/redis.conf"]
redis6:
image: daocloud.io/library/redis:5.0.7-alpine
restart: always
container_name: redis6
environment:
- TZ=Asia/Shanghai
ports:
- 7009:7009
- 17009:17009
volumes:
- ./conf/redis6.conf:/usr/local/redis/redis.conf
command: ["redis-server","/usr/local/redis/redis.conf"]
编写redis.conf
#指定redis端口号
port 7004
#开启redis集群
cluster-enabled yes
#集群信息文件
cluster-config-file nodes-7004.conf
#集群对外ip地址
cluster-announce-ip 139.224.44.232
# 集群的对外port
cluster-announce-port 7004
#集群对外端口
cluster-announce-bus-port 17004
集群间建立关系
进入任意一个redis
# 写上集群中所有redis的ip和port 并且设置只有一个从节点 因此3个主节点3个从节点
#此处的从节点 只管备份 不管查询
redis-cli --cluster create 139.224.44.232:7004 139.224.44.232:7005 139.224.44.232:7006 139.224.44.232:7007 139.224.44.232:7008 139.224.44.232:7009 --cluster-replicas 1
用redis-cli -h 139.224.44.232 -p 7004 -c #-c可以在集群中跳转
java连接redis集群
JedisCluster对象
package com.testredis;
import org.junit.Test;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import java.util.HashSet;
import java.util.Set;
public class clusterRedis {
@Test
public void testCluster(){
Set<HostAndPort> nodes=new HashSet<HostAndPort>();
nodes.add(new HostAndPort("139.224.44.232",7004));
nodes.add(new HostAndPort("139.224.44.232",7005));
nodes.add(new HostAndPort("139.224.44.232",7006));
nodes.add(new HostAndPort("139.224.44.232",7007));
nodes.add(new HostAndPort("139.224.44.232",7008));
nodes.add(new HostAndPort("139.224.44.232",7009));
JedisCluster jedisCluster =new JedisCluster(nodes);
String value=jedisCluster.get("name");
System.out.println(value);
}
}
定期删除与惰性删除
redis淘汰策略
指定淘汰策略的方式
maxmemory-policy 淘汰策略
redis缓存
缓存穿透
缓存击穿
缓存雪崩
缓存倾斜
ic class clusterRedis {
@Test
public void testCluster(){
Set nodes=new HashSet();
nodes.add(new HostAndPort(“139.224.44.232”,7004));
nodes.add(new HostAndPort(“139.224.44.232”,7005));
nodes.add(new HostAndPort(“139.224.44.232”,7006));
nodes.add(new HostAndPort(“139.224.44.232”,7007));
nodes.add(new HostAndPort(“139.224.44.232”,7008));
nodes.add(new HostAndPort(“139.224.44.232”,7009));
JedisCluster jedisCluster =new JedisCluster(nodes);
String value=jedisCluster.get(“name”);
System.out.println(value);
}
}
> 定期删除与惰性删除
[外链图片转存中...(img-dEmpBhEy-1608771264262)]
### redis淘汰策略
[外链图片转存中...(img-kODHK3nm-1608771264263)]
> 指定淘汰策略的方式
```bash
maxmemory-policy 淘汰策略
redis缓存
缓存穿透
[外链图片转存中…(img-2NjK8GKh-1608771264266)]
缓存击穿
[外链图片转存中…(img-fVimb680-1608771264268)]
缓存雪崩
[外链图片转存中…(img-mXiDOmTs-1608771264271)]
缓存倾斜
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0zGm9WQk-1608771264274)(C:\Users\hello\AppData\Roaming\Typora\typora-user-images\image-20201129150855979.png)]