缓存技术整理 redis mongodb minio

本文详细介绍了Redis的多种高可用性和扩展性方案,包括Redis主从复制的配置和全量、增量同步过程,哨兵系统如何实现故障转移,以及Redis集群的搭建和槽位分配。还探讨了Redis的数据类型、过期策略、锁机制以及Java配置Redis的相关内容。此外,提到了MongoDB的安装和使用,并对比了Minio作为对象存储的特性。
摘要由CSDN通过智能技术生成

Redis

192.168.50.130
7000
7001
哨兵
26379,26780,26381

关闭服务方式1
./redis-cli -h 127.0.0.1 -p 6379 shutdown
方式2
ps -ef | grep ‘redis’

连接服务查看信息
info replication

持久化
RDB
RDB是整体快照备份一样,就像我们系统进行镜像的备份这种快照处理,当然看到这个大家应该会有一个问题,这样备份效率相对比较慢,而且一次备份数据比较大,所以官方也不推荐使用此方案进行数据持久化,但我们还得结合实际情况使用,像
redis主从复制的原理底层数据就是通过RDB。
触发条件	 save 同步保存
			    	bgsave 异步保存
AOF
日志追加数据持久化,即在原先的数据基础上进行追加,而不是完全覆写,这样效率高
appendfsync属性   always  everys  进行指令存储 save delete    io操作要求较大  write

配置说明:

	protected-mode no  #关闭保护模式
	daemonize yes   #是否启用后台运行
	port 6666       #设置端口
	requirepass 123456   # 设置使用密码
	logfile ./redislog_louie.log  # 设置日志文件
	dir ./    #设置数据文件存储位置
	# bind 192.168.217.130 127.0.0.1 # 0.0.0.0   任何ip都可以连接

	save 20 1  # 自动保存策略,20秒内有一个key发生变化就自动保存
	dbfilename rdb_louie.rdb  #  rdb文件名
	stop-writes-on-bgsave-error yes #  发生错误中断写入,建议开启
	rdbcompression yes  # 数据文件压缩,建议开启
	rdbchecksum yes # 开启crc64错误校验,建议开启

	appendonly yes  # 开启aof
	appendfilename aof_louie.aof  # aof 日志文件名
	appendfsync everysec  #  每秒记录一次日志,建议everysec
	no-appendfsync-on-rewrite yes  # 重写过程中是否向日志文件写入,yes 代表rewrite过程中,不向aof文件中追加信息,rewrite结束后再写入,no 代表rewrite执行的同时,也向aof追加信息
	auto-aof-rewrite-percentage 100 # 触发重写文件增长百分比 默认100%
	auto-aof-rewrite-min-size 64mb  # 触发重写最小aof文件尺寸
主从复制
主从复制  两台redis服务

主服务器
	daemonize yes
	port 7000
	logfile master7000.log
	dir /usr/local/redis/data
	requirepass 123456
	masterauth 123456 # 132服务器配置masterauth作用主要是为了后期sentinel引入后重新选举master并且7000端口redis重新加入主从复制时必备的,否则会出现权限不足
	#bind 192.168.217.130 127.0.0.1
	
	# AOF 数据持久化
	appendonly yes
	appendfilename aof-master-7000.aof
	appendfsync everysec
	no-appendfsync-on-rewrite yes
	auto-aof-rewrite-percentage 100
	auto-aof-rewrite-min-size 64mb

从服务器
	port 7001
	daemonize yes
	logfile slaver7001.log
	dir /usr/local/redis/data/
	requirepass 123456
	# 低版本的redis这里会是slaveof,意思是一样的,因为slave是比较敏感的词汇,所以在redis后面的版本中不在使用slave的概念,取而代之的是replicaof
	slaveof  192.168.50.130 7000
	masterauth 123456
	#bind 192.168.217.133 127.0.0.1
	
	# AOF 数据持久化
	appendonly yes
	appendfilename aof-slaver-7001.aof
	appendfsync everysec
	no-appendfsync-on-rewrite yes
	auto-aof-rewrite-percentage 100
	auto-aof-rewrite-min-size 64mb

启动
cd /usr/local/redis/etc/master-slaver
## 查看树状结构
tree
./usr/local/redis/bin/redis-service redis-master-7000.config 
./usr/local/redis/bin/redis-service redis-slaver-7001.config

测试在这里插入图片描述
	
  ./redis-cli -p 7000 -h 127.0.0.1 
  ## 验证密码
  auth 123456
  
 # 测试  客户端 连接主节点 
	shutdown save # 手动下线
# 客户端 登录 从节点服务器
	info replication #查看信息
  1. 全量同步
    Redis全量复制一般发生在Slave初始化阶段,这时Slave需要将Master上的所有数据都复制一份。具体步骤如下:
      1)从服务器连接主服务器,发送SYNC命令;
      2)主服务器接收到SYNC命名后,开始执行BGSAVE命令生成RDB文件并使用缓冲区记录此后执行的所有写命令;
      3)主服务器BGSAVE执行完后,向所有从服务器发送快照文件,并在发送期间继续记录被执行的写命令;
      4)从服务器收到快照文件后丢弃所有旧数据,载入收到的快照;
      5)主服务器快照发送完毕后开始向从服务器发送缓冲区中的写命令;
      6)从服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令;

  2. 增量同步
    Redis增量复制是指Slave初始化后开始正常工作时主服务器发生的写操作同步到从服务器的过程。
    增量复制的过程主要是主服务器每执行一个写命令就会向从服务器发送相同的写命令,从服务器接收并执行收到的写命令。

  3. Redis主从同步策略
     主从刚刚连接的时候,进行全量同步;全同步结束后,进行增量同步。当然,如果有需要,slave 在任何时候都可以发起全量同步。redis 策略是,无论如何,首先会尝试进行增量同步,如不成功,要求从机进行全量同步。

集群配置

提高负载

主从哨兵是全量存储,集群是分布式存储,只使用集群的话,如果其中一台机器挂了,就会导致部分数据丢失,所以集群一般是多套主从结合使用

配置文件内容为如下:其他的配置文件修改一下端口以及log文件、日志文件即可。其中中间部分cluster代表集群设置
配置文件

	mkdir  /usr/local/redis/etc/cluster
redis-7000.config
	daemonize yes
	port 7000
	logfile 7000.log
	dir ./redis/data/
	#bind 192.168.217.130 127.0.0.1
	pidfile /redis/data/redis_6379.pid

	cluster-enabled yes
	cluster-config-file nodes_7000.conf
	cluster-node-timeout 15000

	appendonly yes
	appendfilename aof-7000.aof
	appendfsync everysec
	no-appendfsync-on-rewrite yes
	auto-aof-rewrite-percentage 100
	auto-aof-rewrite-min-size 64mb
	

redis-7001.config

daemonize yes
	port 7001
	logfile 7001.log
	dir ./redis/data/
	#bind 192.168.217.130 127.0.0.1
	pidfile /redis/data/redis_6379.pid

	cluster-enabled yes
	cluster-config-file nodes_7001.conf
	cluster-node-timeout 15000

	appendonly yes
	appendfilename aof-7001.aof
	appendfsync everysec
	no-appendfsync-on-rewrite yes
	auto-aof-rewrite-percentage 100
	auto-aof-rewrite-min-size 64mb


启动

	cd /usr/local/redis/etc/cluster/
	
	./bin/redis-server cluster-6379.conf
	./bin/redis-server cluster-6380.conf
	./bin/redis-server cluster-6381.conf
	./bin/redis-server cluster-6382.conf
	./bin/redis-server cluster-6383.conf
	./src/redis-server cluster-6384.conf

槽位的分配和主从关系的设定有两种方式进行,

  1. 使用redis-cli连接到集群节点上后使用cluster meet命令连接其他的节点,如我们首先执行如下命令连接到6379端口的节点:
    ./bin/redis-cli -p 6379
	127.0.0.1:6379>cluster meet 127.0.0.1 6380
	127.0.0.1:6379>cluster meet 127.0.0.1 6381
	127.0.0.1:6379>cluster meet 127.0.0.1 6382
	127.0.0.1:6379>cluster meet 127.0.0.1 6383
	127.0.0.1:6379>cluster meet 127.0.0.1 6384   #连接到其他节点 
	
	cluster nodes #查看集群状态

分别连接到 节点 分配hash槽
./bin/redis-cli -p 6379
#连接到各节点服务器 分配hash槽
#因为还没有将16384个槽分配到集群节点中。虚拟槽的分配可以使用redis-cli分别连接到6379,6380和6381端口的节点中,然后分别执行如下命令:
127.0.0.1:6379>cluster addslots {0...5461}
127.0.0.1:6380>cluster addslots {5462...10922}
127.0.0.1:6381>cluster addslots {10923...16383}

  1. 通过客户端创建集群
    具体的可以参照redis官方网站查看 https://redis.io/topics/cluster-tutorial
./bin/redis-cli --cluster create 192.168.50.130:7000 192.168.50.130:7001 192.168.50.133:7002 192.168.50.133:7003  --cluster-replicas 1

#登录redis客户端 ./src/redis-cli -c -p 7000, -c 参数代表连接到集群中

哨兵
提高高可用性
解决这个问题我们就需要有一种方案让redis宕机后可以自动进行故障转移,还好redis给我们提供一种高可用解决方案 Redis-Sentinel。Redis-sentinel本身也是一个独立运行的进程,它能监控多个master-slave集群,发现master宕机后能进行自动切换。Sentinel可以监视任意多个主服务器

以及主服务器属下的从服务器,并在被监视的主服务器下线时,自动执行故障转移操作。

主机说明	主机IP	端口	sentinel端口
master	     192.168.50.130    7000	26379
slave 	192.168. 50.133	7001	26380
slave 	192.168. 50.133	7001	26381

sentinel-26379.conf

	port 26379
	daemonize yes
	logfile "sentinel26379.log"
	dir "/usr/local/redis/data/"
	sentinel monitor mymaster 192.168.50.130 7000 2
	sentinel failover-timeout mymaster 15000
	sentinel auth-pass mymaster 123456
	#bind 192.168.50.130

sentinel-26380.conf

	port 26380
	daemonize yes
	logfile "sentinel26380.log"
	dir "/usr/local/redis/data/"
	sentinel monitor mymaster 192.168.50.130 7000 2
	sentinel failover-timeout mymaster 15000
	sentinel auth-pass mymaster 123456
	#bind 192.168.50.130

sentinel-26381.conf

	port 26381
	daemonize yes
	logfile "sentinel26381.log"
	dir "/usr/local/redis/data/"
	sentinel monitor mymaster 192.168.50.130 7000 2
	sentinel failover-timeout mymaster 15000
	sentinel auth-pass mymaster 123456
	#bind 192.168.50.130
cd /usr/local/redis/etc/sentinel

./usr/local/redis/bin/redis-sentinel sentinel-26379.conf
./usr/local/redis/bin/redis-sentinel sentinel-26380.conf
./usr/local/redis/bin/redis-sentinel sentinel-26381.conf

检测

由于sentinel节点也是一个redis实例,因而我们可以通过如下命令使用redis-cli连接sentinel节点:

./usr/local/redis/bin/redis-cli -p 26379

127.0.0.1:26379> info sentinel

参数 说明

sentinel monitor <master-name> <ip> <redis-port> <quorum>
告诉sentinel去监听地址为ip:port的一个master,这里的master-name可以自定义,quorum是一个数字,指明当有多少个sentinel认为一个master失效时,master才算真正失效
sentinel auth-pass <master-name> <password>
设置连接master和slave时的密码,注意的是sentinel不能分别为master和slave设置不同的密码,因此master和slave的密码应该设置相同。
sentinel down-after-milliseconds <master-name> <milliseconds> 
这个配置项指定了需要多少失效时间,一个master才会被这个sentinel主观地认为是不可用的。 单位是毫秒,默认为30秒
sentinel parallel-syncs <master-name> <numslaves> 
这个配置项指定了在发生failover主备切换时最多可以有多少个slave同时对新的master进行 同步,这个数字越小,完成failover所需的时间就越长,但是如果这个数字越大,就意味着越 多的slave因为replication而不可用。可以通过将这个值设为 1 来保证每次只有一个slave 处于不能处理命令请求的状态。
sentinel failover-timeout <master-name> <milliseconds>
failover-timeout 可以用在以下这些方面:     
1. 同一个sentinel对同一个master两次failover之间的间隔时间。   
2. 当一个slave从一个错误的master那里同步数据开始计算时间。直到slave被纠正为向正确的master那里同步数据时。    
3. 当想要取消一个正在进行的failover所需要的时间。    
4. 当进行failover时,配置所有slaves指向新的master所需的最大时间。不过,即使过了这个超时,slaves依然会被正确配置为指向master,但是就不按parallel-syncs所配置的规则来了。

mymaster:监控主数据的名称,自定义即可,可以使用大小写字母和“.-_”符号
192.168.1.9:监控的主数据库的IP
6379:监控的主数据库的端口
2:最低通过票数
锁机制
先拿setnx来争抢锁,抢到之后,再用expire给锁加一个过期时间防止锁忘记了释放。
	设置字符串锁
		设置锁字符串健 若存在则设置失败
		SETNX(“keycode::11”,1) == 1 //设置成功
		SETNX(“keycode::11”,1) == 0 //设置失败
	业务处理完释放锁
		DEL(“keycode::11”,1)  
	设置锁字符串健的失效时间 
		PEXPIRE(“keycode::11”,10000)
设置全局锁
	Boolean getlock =”ok”.equals(jedis.set(“keycode::11”,”1”,”NX”,“EX”,10))
	释放全局锁
	jdedis.del(“keycode::11”)
		//设置时是否 被占用
		Boolean b =  this.getRedisTemplate().opsForValue().setIfAbsent(key, value);
		//释放
		this.getRedisTemplate().delete(key)

		public void del(String key, String[] fields) {
        RedisSerializer keySerializer = getRedisTemplate().getKeySerializer();
        RedisSerializer hashKeySerializer = getRedisTemplate().getHashKeySerializer();
    	getRedisTemplate().executePipelined((RedisCallback<Object>) connection -> {
    		byte[][] fieldByte = new byte[fields.length][];
            for (int i =0; i< fields.length; i++) {
            	fieldByte[i] = hashKeySerializer.serialize(fields[i]);
            }
            connection.hDel(keySerializer.serialize(key), fieldByte);
            return null;
        });
    }

	 // 批量 key 失效 
    public int batchExpire(List<ObjectExpire> expires) {
        return getRedisTemplate().execute(new RedisCallback<Integer>() {
            @Override
            public Integer doInRedis(RedisConnection redisConnection) throws DataAccessException {
                RedisSerializer<String> serializer = new StringRedisSerializer();
                redisConnection.openPipeline();
                for (ObjectExpire expire : expires) {
                    redisConnection.expire(serializer.serialize(expire.getKey()), expire.getExpire());
                }
                List<Object> list = redisConnection.closePipeline();
                return 1;
            }
        });
    }
	
	
	//hash 删除
	public void del(String key, String[] fields) {
        RedisSerializer keySerializer = getRedisTemplate().getKeySerializer();
        RedisSerializer hashKeySerializer = getRedisTemplate().getHashKeySerializer();
    	getRedisTemplate().executePipelined((RedisCallback<Object>) connection -> {
    		byte[][] fieldByte = new byte[fields.length][];
            for (int i =0; i< fields.length; i++) {
            	fieldByte[i] = hashKeySerializer.serialize(fields[i]);
            }
            connection.hDel(keySerializer.serialize(key), fieldByte);
            return null;
        });
    }
案例 贴的访问数量
	INCR key
	GET key
	
	
EXPIRE key seconds(不止字符串健)设置一个key的过期时间 (秒)
		PEXPIRE key millseconds       设置一个key 的过期时间 (毫秒)
	针对数字操作
		INCRBY key increment  对数字key进行{increment} 的增加
		DECRBY  key increment  对数字key进行{increment} 的减少
		INCR     key 对数字key进行+1
		DECR    key  对数字key进行 -1

事务

Redis事务相关的命令
MULTI、EXEC、DISCARD、WATCH
包含5个命令 MULTI、EXEC、DISCARD、WATCH、UNWATCH。

DISCARD 取消事务,放弃执行事务块内的所有命令。
EXEC 执行所有事务块内的命令。
MULTI 标记一个事务块的开始。
UNWATCH 取消 WATCH 命令对所有 key 的监视。
WATCH key [key ...] 监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。


Redis 监控 锁机制 首先设置乐观锁 
	WATCH key [key ...]
开启事务
	MULTI

命令入列
....

执行事务
	EXEC
	eq: 
		redisTemplate.multi();
		redisTemplate.opsForValue().increment("xxx",1);
		redisTemplate.opsForValue().increment("ttt",1);
		redisTemplate.exec();

数据类型

Hash结构数据 散列表

	hset key field value
	hget key field
	#设置获取多个字典元素键值
	hmset key field value [field value...]
	hmget key field [field...]
	#获取字典中所有键值
	hgetall key
	#判断是否存在,不存在赋值
	hexists key field value
	#增加数字的值
	hincrby key key field increment

List结构数据 有序可重复

	lpush key value [value...]
	rpush key value [value...]
	#从列表两端弹出元素:
	lpop key
	rpop key

Set结构数据 无须不可重复

#增加删除元素(如果键不存在直接创建):
	SAAD key member [member ...]
	SREM key member [member ...]

Zset结构数据 有序不可重复的 set集合 设置分数 进行排序

	ZADD key score member [score member]
	#获取元素分数:
	ZSCORE key [member]
指令信息
## Redis key的过期时间和永久有效
    EXPIRE和PERSIST命令 ttl 命令
    PERSIST 设置永久  ttl 查看过期时间  永久为 -1
    EXPIRE <KEY> <TTL> : 将键的生存时间设为 ttl 秒
    PEXPIRE <KEY> <TTL> :将键的生存时间设为 ttl 毫秒
    EXPIREAT <KEY> <timestamp> :将键的过期时间设为 timestamp 所指定的秒数时间戳
    PEXPIREAT <KEY> <timestamp>: 将键的过期时间设为 timestamp 所指定的毫秒数时间戳.


过期键删除策略
定时删除:在设置键的过期时间的同时,创建一个定时器timer). 让定时器在键的过期时间来临时,立即执行对键的删除操作。
惰性删除:放任键过期不管,但是每次从键空间中获取键时,都检查取得的键是否过期
定期删除:每隔一段时间程序就对数据库进行一次检查,删除里面的过期键
 
需要人为 操作
数据淘汰策略
volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
no-enviction(驱逐):禁止驱逐数据

注意这里的6种机制,volatile和allkeys规定了是对已设置过期时间的数据集淘汰数据还是从全部数据集淘汰数据,后面的lru、ttl以及random是三种不同的淘汰策略,再加上一种no-enviction永不回收的策略。

使用策略规则:
1、如果数据呈现幂律分布,也就是一部分数据访问频率高,一部分数据访问频率低,则使用allkeys-lru
2、如果数据呈现平等分布,也就是所有的数据访问频率都相同,则使用allkeys-random

一个字符串类型的值能存储最大容量是多少  512M

maxmemory  占用物理内存的比列 默认时0 不限制  只有达到阈值才会触发 数据删除策略
maxmemory-samples  随机获取部分数量的数据

maxmemory-policy allkeys-lru



(1)客户端执行一条新命令,导致数据库需要增加数据(比如set key value)
(2)Redis会检查内存使用,如果内存使用超过maxmemory,就会按照置换策略删除一些key
(3)新的命令执行成功
过滤 sscan

sscan 支持三个参数cursor=0, match=None, count=None

		#先添加两个数字
		127.0.0.1:6379> sadd myset1 998, 123
		# 用正则匹配大于100的值
		127.0.0.1:6379> sscan myset1 0 match "[1-9][0-9][0-9]*"
		1) "0"
		2) 1) "123"
		    2) "998,"
		
		
		#指定count 为1,返回一个
		127.0.0.1:6379> sscan myset1 0 match "[1-9][0-9][0-9]*" count 1
		1) "2"
		2) 1) "123"
		
		127.0.0.1:6379> sadd myset1 189
		(integer) 1
		127.0.0.1:6379> sadd myset1 89
		127.0.0.1:6379> sscan myset1 0 match "[1-9][0-9][1-9]*"
		1) "3"
		2) 1) "123"
		   2) "998,"
		   3) "189"
		
		
		#指定cursor
		127.0.0.1:6379> sscan myset1 1 match "[1-9][0-9][1-9]*"
		1) "0"
		2) 1) "189"
		
		# 匹配更复杂的
		127.0.0.1:6379> sscan myset1 0 match "{'i': '[1-9][0-9][0-9]*'}"
		1) "3"
		2) 1) "{'i': '990', 'i2': '991'}"
shell脚本

yum install psmisc

#!/bin/sh
# chkconfig: 2345 18 85
# redis
#$1为从服务器端传入的第一个参数
. /etc/rc.d/init.d/functions
PIDFILE=/var/run/redis.pid

        start(){
                /usr/local/redis/bin/redis-server /usr/local/redis/etc/master-slaver/redis-master-7000.config &
                /usr/local/redis/bin/redis-server /usr/local/redis/etc/master-slaver/redis-slaver-7001.config &
                /usr/local/redis/bin/redis-sentinel /usr/local/redis/etc/sentinel/sentinel-26379.conf &
                /usr/local/redis/bin/redis-sentinel /usr/local/redis/etc/sentinel/sentinel-26380.conf &
                /usr/local/redis/bin/redis-sentinel /usr/local/redis/etc/sentinel/sentinel-26381.conf
        }

        stop(){
                /usr/local/redis/bin/redis-cli -p 7000 -h 127.0.0.1 -a 123456  shutdown  > /dev/null 2>&1
                /usr/local/redis/bin/redis-cli -p 7001 -h 127.0.0.1 -a 123456  shutdown  > /dev/null 2>&1
                /usr/local/redis/bin/redis-cli -p 26379 -h 127.0.0.1 -a 123456  shutdown > /dev/null 2>&1
                /usr/local/redis/bin/redis-cli -p 26380 -h 127.0.0.1 -a 123456  shutdown > /dev/null 2>&1
                /usr/local/redis/bin/redis-cli -p 26381 -h 127.0.0.1 -a 123456  shutdown > /dev/null 2>&1
        }
case $1 in
        start)
         if [ -f $PIDFILE ]
         then
                echo "$PIDFILE exists, process is already running or crashed"
         else
                start
         fi
         ;;

        stop)
        if [ ! -f $PIDFILE ]
        then
                echo "$PIDFILE does not exist, process is not running"
        else
                stop
        fi
        ;;

        restart)
              stop
              sleep 3
              start
        ;;

        status)
        status=`pstree | grep redis |wc -l`

         if [ $status == 0 ]; then
                echo 'redis close'
         else
                echo -e "\033[31;47m redis running \033[0m"
         fi
        ;;

        redis-cli)
        #启动redis客户端
        /usr/local/redis/bin/redis-cli -p 7000 -h 127.0.0.1 -a 123456
         ;;
         *)
           echo "Please input [status]、[start]、[stop]、[redis-cli]"
           echo "For example:service redis start"
        ;;
esac

设置开机启动
 等级0表示:表示关机   
  等级1表示:单用户模式   
  等级2表示:无网络连接的多用户命令行模式
  等级3表示:有网络连接的多用户命令行模式  
  等级4表示:不可用   
  等级5表示:带图形界面的多用户模式   
  等级6表示:重新启动
10是启动优先级,90是停止优先级,优先级范围是0-100,数字越大,优先级越低。

4、其它相关命令
添加开机自启动服务:chkconfig --add redis
查看开机自启动服务:chkconfig --list redis

	 #/etc/init.d    存放  redis.sh 脚本 ,脚本头设置 chkconfig  开机 和 关机的 优先级,开机执行start  关机执行 stop
	 ##/var/lock/subsys/ 目录下建立 同名空文件 关机是用 
	 touch /var/lock/subsys/redis

	 chmod +x /etc/init.d/redis
	 chkconfig --add  redis
	 chkconfig --list redis
	#支持服务操作
	 service redis start
	 service redis stop
java配置

依赖

  <!-- SpringBoot Boot Redis -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
   <!-- Jackson -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.6.7</version>
        </dependency>
        
        <!-- lettuce pool 缓存连接池  默认未 jedis pool--> 
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
            <version>2.5.0</version>
        </dependency>

配置哨兵 主从

spring:
    redis:
        open: true  # 是否开启redis缓存  true开启   false关闭
        database: 0
        host: 127.0.0.1
        port: 6379
        password:    # 密码(默认为空)
        timeout: 6000ms  # 连接超时时长(毫秒)
        lettuce:
            pool:
                max-active: 1000  # 连接池最大连接数(使用负值表示没有限制)
                max-wait: -1ms      # 连接池最大阻塞等待时间(使用负值表示没有限制)
                max-idle: 10      # 连接池中的最大空闲连接
                min-idle: 5       # 连接池中的最小空闲连接
        sentinel:
            master: mymaster
            nodes: 192.168.50.130:26379,192.168.50.130:26380,192.168.50.130:26381


配置类

public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T>
{
    @SuppressWarnings("unused")
    private ObjectMapper objectMapper = new ObjectMapper();

    public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");

    private Class<T> clazz;

    static
    {
        ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
    }

    public FastJson2JsonRedisSerializer(Class<T> clazz)
    {
        super();
        this.clazz = clazz;
    }

    @Override
    public byte[] serialize(T t) throws SerializationException
    {
        if (t == null)
        {
            return new byte[0];
        }
        return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
    }

    @Override
    public T deserialize(byte[] bytes) throws SerializationException
    {
        if (bytes == null || bytes.length <= 0)
        {
            return null;
        }
        String str = new String(bytes, DEFAULT_CHARSET);

        return JSON.parseObject(str, clazz);
    }

    public void setObjectMapper(ObjectMapper objectMapper)
    {
        Assert.notNull(objectMapper, "'objectMapper' must not be null");
        this.objectMapper = objectMapper;
    }

    protected JavaType getJavaType(Class<?> clazz)
    {
        return TypeFactory.defaultInstance().constructType(clazz);
    }
}
@Configuration
@EnableCaching //加上这个注解是的支持缓存注解
public class RedisConfig extends CachingConfigurerSupport
{
    @Bean
    @SuppressWarnings(value = { "unchecked", "rawtypes", "deprecation" })
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory)
    {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);

        FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);
        //设置开启事务
        template.setEnableTransactionSupport(true);
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        serializer.setObjectMapper(mapper);

        template.setValueSerializer(serializer);
        // 使用StringRedisSerializer来序列化和反序列化redis的key值
        template.setKeySerializer(new StringRedisSerializer());
        template.afterPropertiesSet();
        return template;
    }
}

// 服务 Api
private RedisTemplate redisTemplate
	redisTemplate.opsForValue()
	redisTemplate.expire(key, timeout, unit)
	
  ValueOperations<String, T> operation = redisTemplate.opsForValue();
       operation.get(key);
    redisTemplate.delete(collection)
    redisTemplate.opsForList()
    redisTemplate.boundSetOps(key
    redisTemplate.opsForSet()
    redisTemplate.opsForHash().put(key, hKey, value)
 	//锁 和 配置失效 释放锁
    boolean  redisTemplate.opsForValue().setIfAbsent(key, value);
			 redisTemplate.expire(key, timeout, unit)
			 
	redisTemplate.execute(new SessionCallback<Object>() {
				@Override
				@SuppressWarnings({ "unchecked", "rawtypes" })
				public Object execute(RedisOperations operations) throws DataAccessException {
					List<Object> result = null;
					do {
						int count = 0;
						operations.watch(key);  // watch某个key,当该key被其它客户端改变时,则会中断当前的操作
						String value = (String) operations.opsForValue().get(key);
						if (!StringUtils.isEmpty(value)) {
							count = Integer.parseInt(value);
						}
						count = count + 1;
						operations.multi(); //开始事务
						operations.opsForValue().set(key, String.valueOf(count));
						try {
							result = operations.exec(); //提交事务
						} catch (Exception e) { //如果key被改变,提交事务时这里会报异常
	
						}
					} while (result == null); //如果失败则重试
					return null;
				}
	
			});


//executePipelined 方法会执行 RedisCallback or SessionCallback 的回调方法以返回结果.
List<Object> results = stringRedisTemplate.executePipelined(new RedisCallback<Object>() {
         public Object doInRedis(RedisConnection connection) throws DataAccessException {
       		 StringRedisConnection stringRedisConn = (StringRedisConnection)connection;
	       	 for(int i=0; i< batchSize; i++) { 
	            stringRedisConn.rPop("myqueue");
	         } 
        	 return null; 
         } 
    });
redis session 分布式session
粘性会话
如果某台服务器宕机,那么会话信息就没有了。
复制会话
每台机器都复制会话,如果量太大的话,不现实
集中会话
使用 mongo 、redis 等统一保持会话

当请求进来的时候,SessionRepositoryFilter 会先拦截到请求,将 request 和 response 对象转换成 SessionRepositoryRequestWrapper 和 SessionRepositoryResponseWrapper 。后续当第一次调用 request 的getSession方法时,会调用到 SessionRepositoryRequestWrapper 的getSession方法。这个方法是被从写过的,逻辑是先从 request 的属性中查找,如果找不到;再查找一个key值是"SESSION"的 Cookie,通过这个 Cookie 拿到 SessionId 去 Redis 中查找,如果查不到,就直接创建一个RedisSession 对象,同步到 Redis 中。
		<dependency>
			<groupId>org.springframework.session</groupId>
			<artifactId>spring-session-core</artifactId>
		</dependency>
		
		
		<dependency>
			<groupId>org.springframework.session</groupId>
			<artifactId>spring-session-data-redis</artifactId>
		</dependency>

springBoot 配置

@EnableRedisHttpSession-存放在缓存redis
@EnableMongoHttpSession-存放在Nosql的MongoDB
@EnableJdbcHttpSession-存放数据库
//启动
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 86400*30)

 	  <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
		<dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session</artifactId>
        </dependency>


//业务
        request.getSession().setAttribute("url", request.getRequestURL());
		request.getSession().getId()
		request.getSession().getAttribute("url")

spring: 
	session:
		storeType: redis

mongoDB

安装

MongoDB 官方
下载
mongodb community server


	tar -zxvf mongodb-linux-x86_64-rhel70-4.4.6.tgz
	
	mv /home/admin/mongodb/mongodb-linux-x86_64-rhel70-4.4.6 mongodb4
	 
	export PATH=/usr/local/mongodb4/bin:$PATH 
	
	source /etc/profile 
	
	
	##默认情况下 MongoDB 启动后会初始化以下两个目录:
	##数据存储目录:/var/lib/mongodb
	##日志文件目录:/var/log/mongodb
	##我们在启动前可以先创建这两个目录并设置当前用户有读写权限:
	
	sudo mkdir -p /var/lib/mongo
	sudo mkdir -p /var/log/mongodb
	
	##暂时废弃
	sudo chown `admin` /var/lib/mongo     # 设置权限
	sudo chown `admin` /var/log/mongodb   # 设置权限
	
	
	##启动 方式1
	mongod --dbpath /var/lib/mongo --logpath /var/log/mongodb/mongod.log --fork

	#停止 方式1 
	mongod --dbpath /var/lib/mongo --logpath /var/log/mongodb/mongod.log --shutdown


   ##配置文件启动 
   ##创建 mongodb.conf 配置文件
		dbpath = /usr/local/mongodb4/data/db #数据文件存放目录
		logpath = /usr/local/mongodb4/logs/mongodb.log #日志文件存放目录
		bind_ip = 0.0.0.0  #远程访问
		port = 27017  #端口
		fork = true  #以守护程序的方式启用,即在后台运行
		#nohttpinterface = true

	#启动 方式2
    mongod --config /usr/local/mongodb4/etc/mongodb.conf
    #启动 方式2
	mongod  --shutdown --config /usr/local/mongodb4/etc/mongodb.conf
	
	## 客户端
	cd /usr/local/mongodb4/bin
	./mongo

    ##由于它是一个JavaScript shell,您可以运行一些简单的算术运算:

	## 客户端停止服务
	 use admin ## 切换某db
	 db.shutdownServer()
配置 开机启动

在 /etc/init.d/ 文件夹编写脚本 mongodb

#!/bin/sh
#chkconfig: 2345 80 90
#description: mongodb shell

if test -f /sys/kernel/mm/transparent_hugepage/enabled; then
   echo never > /sys/kernel/mm/transparent_hugepage/enabled
fi
if test -f /sys/kernel/mm/transparent_hugepage/defrag; then
   echo never > /sys/kernel/mm/transparent_hugepage/defrag
fi

start() {
        /usr/local/mongodb4/bin/mongod --config /usr/local/mongodb4/etc/mongodb.conf
}

stop() {
        /usr/local/mongodb4/bin/mongod --config /usr/local/mongodb4/etc/mongodb.conf --shutdown
}

case $1 in
  start)
    start
    RETVAL=$?
    ;;
  stop)
    stop
    ;;
  restart)
    stop
    start
    RETVAL=$?
    ;;
  *)
 echo $"Usage: -bash {start|stop|restart}"
 exit $RETVAL
esac

在 /var/lock/subsys/ 下间 mongodb 同名空文件夹
touch /var/lock/subsys/mongodb

	chmod +x /etc/init.d/mongodb
	# 添加服务
	chkconfig --add mongodb
	# 查看服务
	chkconfig  --list
	# 删除服务
	chkconfig --del mongodb
服务配置

mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb

服务引用
nodeJs 配置
java配置
spring:
  data:
    mongodb:
      uri:  mongodb://192.168.50.130:27017/testDb
      ##如果要配置多个数据库,则中间用","分割
		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
        </dependency>
  1. 基于 MongoTemplate 进行基础的 增删改查操作
MongoTestDao
@Component

public class MongoTestDao {
    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 创建对象
     */
    public void saveTest(TUser test) {
        mongoTemplate.save(test);
    }

    /**
     * 根据用户名查询对象
     * @return
     */
    public TUser findTestByName(String name) {
        Query query=new Query(Criteria.where("name").is(name));
        TUser mgt =  mongoTemplate.findOne(query , TUser.class);
        return mgt;
    }

    /**
     * 更新对象
     */
    public void updateTest(TUser test) {
        Query query=new Query(Criteria.where("id").is(test.getId()));
        Update update= new Update().set("age", test.getAge()).set("name", test.getName());
        //更新查询返回结果集的第一条
        mongoTemplate.updateFirst(query,update,TUser.class);
        //更新查询返回结果集的所有
        // mongoTemplate.updateMulti(query,update,TestEntity.class);
    }

    /**
     * 删除对象
     * @param id
     */
    public void deleteTestById(Integer id) {
        Query query=new Query(Criteria.where("id").is(id));
        mongoTemplate.remove(query,TUser.class);
    }
   //大于gt 小于lt,大于等于gte,小于等于lte , 非空ne
    // Criteria.where("name").is("").and("age").gt(12)
    public List<TUser>  queryLikName(String name){
     	  Pattern  pattern = Pattern.compile("^.*"+  name +".*$", Pattern.CASE_INSENSITIVE);
      	  return mongoTemplate.find(Query.query(Criteria.where("name").regex(pattern))
                .with(Sort.by(Sort.Direction.ASC, "age")), TUser.class);

    }

    //分页查询
    public Map<String, Object> findByPage(String name, int page , int size) {

        Map<String, Object> result = new HashMap<>();
        Pageable pageable = PageRequest.of(page, size, Sort.Direction.DESC, name);

        //正则
        Pattern  pattern = Pattern.compile("^.*"+name+".*$", Pattern.CASE_INSENSITIVE);
        //创建查询对象
        Query query = new Query();

        //设置模糊查询
        query.addCriteria(Criteria.where("name").regex(pattern));
        //排序
        query.with(Sort.by(Sort.Direction.ASC, "age"));
        //设置分页
        query.with(pageable);


        //查询当前页数据集合
        List<TUser> userList = mongoTemplate.find(query, TUser.class);

        //查询总记录数
        int count=(int) mongoTemplate.count(query,TUser.class);

        //创建分页实体对象
            result.put("list",userList);
            result.put("page",pageable.getPageNumber());
            result.put("size",pageable.getPageSize());
            result.put("total",count);


        return result;
    }

}
  1. 基于 MongoRepository spring-data-mongo 进行基础的 增删改查操作

  2. 基于 GridFsTemplate 进行的 进行存读取文件

 	 @Autowired
        GridFsTemplate gridFsTemplate;
    //save
   		 gridFsTemplate.store(file.getInputStream(), file.getFilename(), "yml");
   		 gridFsTemplate().store(file.getInputStream(), fileName, file.getContentType(), metaData);

    //get 
		 GridFsResource[] txtFiles = gridFsTemplate.getResources("*");
         for (GridFsResource txtFile : txtFiles) {
            System.out.println(txtFile.getFilename());
         }
	//删除
	    Query query = Query.query(Criteria.where("_id").is("objectId"));
        gridFsTemplate.delete(query);
     //下载 获取 文件流
	      GridFsResource gridFsResource = gridFsTemplate.getResource(gridFSFile);
	      InputStream inputStream = gridFsResource.getInputStream();

File 操作的 基础服务类
@Configuration
public class MongodbCfg {

    @Bean(name = "excelGridFs")
    @Primary
    GridFsTemplate gridFsTemplate(MongoDatabaseFactory factory, MongoTemplate mongoTemplate) {
        return new GridFsTemplate(factory, mongoTemplate.getConverter(),"excel");
    }

    @Bean(name = "imgGridFs")
     GridFsTemplate gridFsTemplateFs2(MongoDatabaseFactory factory, MongoTemplate mongoTemplate) {
         return new GridFsTemplate(factory, mongoTemplate.getConverter(),"img");
    }
 }
public abstract class BaseFileService {

    protected abstract GridFsTemplate getGridFsTemp();// 针对性的 传入 GridFsTemplate 

    /**
     *文件流:inputStream
     * fileName:文件的唯一标识id
     * file.getContentType():内容类型
     * metaData:元数据
     * @param file 文件
     * @param fileName
     * @return
     */
    public String saveFile(MultipartFile file, String fileName) {
        DBObject metaData = new BasicDBObject();
        metaData.put("createdDate", new Date());
        InputStream inputStream = null;
        try {
            inputStream = file.getInputStream();
            getGridFsTemp().store(inputStream, fileName, file.getContentType(), metaData);
        } catch (IOException e) {
        }
        return fileName;
    }
    /**
     * 根据文件名称检索对应的文件
     * @param fileId
     * @return
     * @throws IOException
     */
    public GridFSFile getFileById(String fileId) {
        GridFSFile gridFSFile =
                getGridFsTemp().findOne(Query.query(Criteria.where("_id").is(fileId)));
        if (gridFSFile == null) {
            return null;
        }
        return gridFSFile;
    }

    /**
     * 根据文件名称检索对应的文件
     * @param fileName
     * @return
     * @throws IOException
     */
    public GridFSFile getFileByName(String fileName) throws IOException {
        GridFSFile gridFSFile  = getGridFsTemp()
                .findOne(new Query(Criteria.where("filename").is(fileName)));
        if (gridFSFile == null) {
            return null;
        }
        return gridFSFile;
    }
    /**
     *
     * @param out
     * @param gridFSFile
     */
    public void writTo(OutputStream out,GridFSFile gridFSFile){
        //创建gridFsResource,用于获取流对象
        GridFsResource gridFsResource = getGridFsTemp().getResource(gridFSFile);
        //获取流中的数据
        InputStream inputStream = null;
        try {
           inputStream = gridFsResource.getInputStream();
            int len = 0;
            byte[] b = new byte[1024];
            while ((len = inputStream.read(b)) != -1) {
                out.write(b, 0, len);
            }
            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
                if (out != null) {
                    out.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 根据文件的唯一标识删除对应的文件
     * @param fileName
     */
    public void removeFile(String fileName) {
        getGridFsTemp().delete(new Query().addCriteria(Criteria.where("filename").is(fileName)));
    }
    /**
     * 字节流 文件名称
     * @param body
     * @param filename
     * @param appends
     */
    public String saveFile(byte[] body, String filename, Map<String, Serializable> appends) {
        Document metadata = new Document();
        if (appends != null && !appends.isEmpty()) {
            for (Map.Entry<String, Serializable> entry : appends.entrySet()) {
                metadata.put((String)entry.getKey(), (Object)entry.getValue());
            }
        }
        ObjectId store = getGridFsTemp().store(new ByteArrayInputStream(body), filename,metadata);
        return store.toString();
    }
}

基础语法
	mongo
	## 创建库
	use testDb  
	
	## 查看当前db
		db
	
	## 插入数据
	 db.runootb.insert({"name":"菜鸟教程"})
	## 查看所有的db
	
	show dbs
	
	##删除当前库
	db.dropDatabase()
	
	
	## 查看当前库的 集合
	show tables
	
	## 删除当前库的 runoob集合
	db.runoob.drop()
增加
删除
修改
聚合
过滤
分片 备份

hbase

Minio

对象存储: 分段读取 分段存储, 基于 bucket 桶的概念,实现对象数据的分布式存储,最大5G 最小 5MB, 断点续传需要自己实现。
Minio在分布式和单机模式下,所有读写操作都严格遵守read-after-write一致性模型。

minio

server 服务端
wget https://dl.min.io/server/minio/release/linux-amd64/minio
chmod +x minio
可以启动前配置密码 默认是 minioadmin 
	export MINIO_ROOT_USER=minio
	export MINIO_ROOT_PASSWORD=minio13
	
nohup ./minio server /usr/local/minio/data/ &
client 客户端
wget https://dl.min.io/client/mc/release/linux-amd64/mc
chmod +x mc
./mc --help

mc alias set --api --path

mc alias set minio http://127.0.0.1 minioadmin minioadmin

./mc tree --files
./mc du /usr/local/minio/data/

minio 的 putObject 方法 传 上传对象大小 就,走的分段上传 最大 5g

API

JAVA SDK

<dependency>
    <groupId>io.minio</groupId>
    <artifactId>minio</artifactId>
    <version>7.0.2</version>
</dependency>

列出存储桶中被部分上传的对象。
Iterable<Result> myObjects = minioClient.listIncompleteUploads(“mybucket”);

删除一个未完整上传的对象。
removeIncompleteUpload(String bucketName, String objectName)

presignedPutObject(String bucketName, String objectName, Integer expires)
生成一个给HTTP PUT请求用的presigned URL。浏览器/移动端的客户端可以用这个URL进行上传,即使其所在的存储桶是私有的。这个presigned URL可以设置一个失效时间,默认值是7天。

presignedGetObject(String bucketName, String objectName, Integer expires)
生成一个给HTTP GET请求用的presigned URL。浏览器/移动端的客户端可以用这个URL进行下载,即使其所在的存储桶是私有的。这个presigned URL可以设置一个失效时间,默认值是7天。不得大于7天

minio 实现 断点续传,
1 业务上 将上传数据进行拆分, 上传成多个 objct对象, 记录上传状态。 如果失败的就可以重写上传那一部分的
2. 然后 listObject 列出拆分好的文件,然后 调用 composeObject 进行合并成最终的 object

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值