Redis.md
文章目录
目录
第一章-Redis概述
1介绍和安装
黑马实用教程:https://www.bilibili.com/video/BV1CJ411m7Gc?p=23
笔记网址:https://blog.csdn.net/branwel/article/details/103897256
介绍
关系型数据库通过外键关联来建立表与表之间的关系。
非关系型数据库通常指数据以对象的形式存储在数据库中,而对象之间的关系通过每个对象自身的属性来决定。Nosql Not-Only SQL
Redis 运行快速的原因
- 完全基于内存操作
- 数据结构简单,数据操作也简单
- 使用多路I/O复用模型
关系型数据库发展的原因,之前的关系型数据库,关系复杂,扩展性差,不容易维护,不便于大规模集群。磁盘IO性能低下。
redis作用:应用基于海量用户和海量数据前提下的数据处理问题。 一般用于,高频,波段性比较强的热点信息。
安装
Linux:https://redis.io/download window:安装和教程https://www.runoob.com/redis/redis-install.html
还可以安装桌面可视化管理工具进行操作,比如redis desktop manager
开启服务:
redis一般都是在Linux下使用,这里只是作基础的学习。 在redis解压目录下:cmd+空格 (通过window+r用cd切换到目录也可以)。
1输入:redis-server.exe redis.windows.conf 会出现一个redis图像 2不要关闭上一个窗口,再开一个。切换到redis目录 输入:redis-cli.exe -h 127.0.0.1 -p 6379 看到前缀变为127.0..:6379 OK
获取配置 redis 127.0.0.1:6379>输入CONFIG GET 配置项的名称 获取全部配置 CONFIG GET *
也就是redis.windows.conf Window下的配置
关闭客户端:shutdown
2数据格式
在redis操作中成功为1,失败为0
类型名称 | redis中类型 |
---|---|
字符串 | string |
列表 | list |
散列 | hash |
集合 | set |
有序集合 | sorted_set |
1string
key的命名习惯 表名:主键名:主键值:字段名称
单条操作
- 增:set key value
- 查:get key
- 删:del key
多条操作
- 增改:mset key value [key1 value1]
- 查:mget key [key1]
** 其他命令**
strlen key //获取字符串长度 append key value //有则追加,无则新建 setnx key value //不存在就设置,存在就不设置 incr key //自增 1 incrby key num //给key的值增加num(int 类型),num 正数则为加,num 为负数 则为减 incrbyfloat key num //给key的值增加num(float 类型) decr key //自减 1 decrby key num //给key的值减num setex key second value //设置key的值为value存活时间为second秒 psetex key millisecond value //设置key的值为value存活时间为millisecond毫秒
2Hash
** 单条操作**
- 增:hset key field value
- 查:hget key field
- 删:hdel key field
** 多条操作**
- 增:hmset key field value [field1 value2]
- 查:hmget key field [field1]
其他命令
hgetall key //获取key的全部的字段和值 hlen key //获取key的值的数量 hexists key field //是否存在field hkeys key //所有key的字段(field) hvals key //所有key的值 hincrby key field num //给key的field的值增加num (num 为int值) hincrybyfloat key field num //给key的field的值增加num (num 为float) hsetnx key field value //存在不设置,不存在设置
注意
Hash类型的value只能存字符串,不允许再嵌套其他类型,如果数据为空为Nil 每个Hash可以存储232−1 2^{32}-12 32 −1个键的值对 Hash类型十分贴近对象的数据存储,并且可以灵活添加、删除对象属性。但Hash类型设计并不是存在量而设计的,切记不可滥用,更不可将Hash作为对象列表使用 hgetall操作可以获取全部属性,如果内部field过多,遍历整体数据时效率会降低,很有可能成为数据访问的瓶颈
3list
保存多个数据,底层用双向链表存储结构实现。
key为集合名
** 添加**
- lpush key value [value1] //从左添加
- rpush key value [value1] //从右添加
|
|
获取
- lrange key start stop(为-1时获取所有数据)
- lindex key index
- llen key
** 获取并移除**
- lpop key //从左出
- rpop key //从右出
从左边移除count个指定数据
lrem key count value
- list具有索引的概念,但是操作数据时通常以队列的形式进行入队出队操作(或以栈的形式进行入栈出栈操作)
规定时间内,(超过时间返回nil,可以有从多个集合获取)获取并移除数据
blopop key1 key2 timout
4set
存储大量数据,查询方面有更高效率
增:sadd key member [member1] 查:smembers key 删:srem key member [member1] 获取总量:scard key 判定是否存在:sismember key member 随机获取(原集合保留):srandmember key [count] 随机获取(原集合不保留):spop key 集合交集:sinter key key1 key2 集合并集:sunion key key1 key2 集合差集:sdiff key key1 key2 存储集合交集:sinterstore destination key key1 key2 存储集合并集:sunionstore destination key key1 key2 存储集合差集:sdiffstore destination key key1 key2 集合元素移动:smove source destination member
应用场景:访问量统计
公司对旗下新的网站做推广,统计网站的PV (访问量) ,UV (独立访客) ,IP (独立IP)。 PV:网站被访问次数,可通过刷新页面提高访问量 UV:网站被不同用户访问的次数,可通过cookie统计访问量,相同用户切换IP地址, UV不变 IP:网站被不同IP地址访问的总次数,可通过IP地址统计访问量,相同IP不同用户访问, IP不变
5sorted_set
zset (sorted set:有序集合)。
增:zadd key score member [score1 member1] 删:zrem key member [member1]
加上withscores意思是会先显示score 再显示member
获取全部(正序):zrange key start stop [withscores] 获取全部(倒序):zrevrange key start stop [withscores]
按条件查(正序):zrangebyscore key min max [withscore limit] 按条件查(倒序):zrevrangebyscore key max min [withscore limit]
按条件删除(索引):zremrangebyrank key start stop 按条件删除(积分):zremrangebyscore key min max 获取集合总量:zcard key | zcount key min max 存储集合交集: zinterstore destination numkeys key key1 存储集合并集:zunionstore destination numkeys key key1 获取索引(正序):zrank key member 获取索引(倒序):zrevrank key member score值获取:zscore key member score值修改:zincrby key num member
3通用的key
删除:del key 判断是否存在:exists key 获取key类型:type key
指定有效期: expire key seconds pexpire key milliseconds expireat key timestamp pexpireat key milliseconds-timestamp
获取有效期: ttl key pttl key
设置永久:persist key 查询key:key pattern //*,?,[] 重命名:rename key newkey 新名称不存在时,才改 renamenx key newkey 对key排序:sort
key pattern匹配符号解释: // *任意数量任意符号,?一个任意符号 []匹配一个指定字符
keys *查询所有,keys it* 查询以it开头
4数据库通用操作
为了解决随着数据量增大,出现大量数据以及对应key,可能出现的数据名类似。
redis为每个服务提供16个数据库,从0到15 每个数据库之间数据相互独立。
- 选择数据库:select index
- 数据移动:move key db
- 数据库大小(查看库中有多少key):dbsize
- 数据清除:
- 单库删除:flushdb
- 多库删除:flushall
redis安全 默认连接redis不需要密码 config set requirepass xxxx设置密码 config get requirepass 查看密码 若无则是:“requirepass” "” auth xxx验证密码
第二章-Redis高级
0-Jedis
加入jar包
1 2 3 4 5 6 7 8 9 10 11 12
<!--jedis客户端依赖jar redis--> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency> <!--使用jedis连接池--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>2.8.0</version> </dependency>
Jedis简单使用
1 2 3 4 5 6 7 8 9
//单机连接redis Jedis jedis=new Jedis("localhost",6379); //操作redis String string1 = jedis.get("string1"); jedis.set("test","汉字能不能用?123456");//redis数据库用cmd窗口会显示编码,java取出汉字 //关闭 jedis.close();
jedis连接池工具类(redis.properties) 使用配置文件,能够解耦。
redis.properties
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
ADDR="localhost" PORT = 6379 密码** AUTH = null 连接实例的最大连接数** MAX_ACTIVE = 1024 控制一个pool最多有多少个状态为idle(空闲的)的jedis实例,默认值也是8。** MAX_IDLE = 200 等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。如果超过等待时间,则直接抛出JedisConnectionException** MAX_WAIT = 2000 连接超时的时间 ** TIMEOUT = 2400 在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的;** TEST_ON_BORROW = true 数据库模式是16个数据库 0~15** DEFAULT_DATABASE = 0
RedisUtil工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
package com.lx.redis; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; import java.util.ResourceBundle; public class RedisUtil { //服务器IP地址 private static String ADDR = "localhost"; //端口 private static int PORT = 6379; //密码 private static String AUTH = null; //连接实例的最大连接数 private static int MAX_ACTIVE = 1024; //控制一个pool最多有多少个状态为idle(空闲的)的jedis实例,默认值也是8。 private static int MAX_IDLE = 200; //等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。如果超过等待时间,则直接抛出JedisConnectionException private static int MAX_WAIT = 10000; //连接超时的时间 private static int TIMEOUT = 10000; // 在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的; private static boolean TEST_ON_BORROW = true; private static JedisPool jedisPool = null; //数据库模式是16个数据库 0~15 public static Integer DEFAULT_DATABASE = null; /** * 初始化Redis连接池 */ static { try { ResourceBundle rb=ResourceBundle.getBundle("redis"); ADDR=rb.getString("ADDR"); PORT=Integer.valueOf(rb.getString("PORT")); MAX_ACTIVE=Integer.valueOf(rb.getString("MAX_ACTIVE")); MAX_IDLE=Integer.valueOf(rb.getString("MAX_IDLE")); MAX_WAIT=Integer.valueOf(rb.getString("MAX_WAIT")); TIMEOUT=Integer.valueOf(rb.getString("TIMEOUT")); TEST_ON_BORROW=Boolean.valueOf(rb.getString("TEST_ON_BORROW")); DEFAULT_DATABASE=Integer.valueOf(rb.getString("DEFAULT_DATABASE")); JedisPoolConfig config = new JedisPoolConfig(); config.setMaxTotal(MAX_ACTIVE); config.setMaxIdle(MAX_IDLE); config.setMaxWaitMillis(MAX_WAIT); config.setTestOnBorrow(TEST_ON_BORROW); jedisPool = new JedisPool(config, ADDR, PORT, TIMEOUT,AUTH,DEFAULT_DATABASE); } catch (Exception e) { e.printStackTrace(); } } /** * 获取Jedis实例 */ public synchronized static Jedis getJedis() { try { if (jedisPool != null) { Jedis resource = jedisPool.getResource(); System.out.println("redis--服务正在运行: "+resource.ping()); return resource; } else { return null; } } catch (Exception e) { e.printStackTrace(); return null; } } /*** * * 释放资源 */ public static void returnResource(final Jedis jedis) { if(jedis != null) { jedis.close(); } } }
0-整合spring
先加入jar包依赖
|
|
1redis.properties
|
|
2spring-dao.xml 注意没有密码,这是整合的mabatis-plus
|
|
3使用时,在service中使用就好
template使用:https://blog.csdn.net/sinat_22797429/article/details/89196933
|
|
注意: 存放取出,全部使用Spring。或者对象。
redisTemplate.opsForHash().increment(“lsl1”,“2”,15);进行修改时,若是hset进去的会报错,ERR,因为其采用的序列号方式不一样,在使用increment修改时候,存放的时候也要这个存放。
1、linux下使用
1-安装
redis6要先更新c++(然后重启服务器)
|
|
安装
|
|
服务器换端口启动 redis-server –port 端口号6380
这时客户端启动 就需要指定端口号 redis-cli -p 6380
2-配置文件
redis配置文件在redis-6.0.0.4下的redis-conf,文件原配置文件不要改动,由于里面大量注解,所以需要重新复制一个配置文件。
|
|
编辑新创建的配置文件redis-6379.conf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
port 6379 以守护进程方式启动** daemonize yes logfile "redis-6379.log" 文件存放位置,日志文件,本地存储文件** dir /home/redis-log rdb持久化方式 save指令相关配置 持久化的文件名称** dbfilename dump-端口号.rdb 存储到本地数据库时 是否压缩,默认yes 用LZF压缩** rdbcompression yes 设置时 是否RDB文件格式校验,读写都进行,默认开启,关闭肯有虽居损坏风险** rdbchecksum yes 900秒内 若有一个1key发生变化就进行保存 可多设几个形成互补** save 900 1 是否开启AOP默认不开启** appendonly yes/no AOF写数据策略** appendfsync always/everysec/no AOF文件名称** appendfilename appendonly-端口号.aof文件名称 AOF重写** 自动重写的最小值** auto-aof-rewrite-min-size size 自动重写百分比** auto-aof-rewrite-percentage percentage 当前大小,和重写最小值有关** aof_current_size 基础大小,和重写百分比有关 (当前大小-基础大小)基础大小>自动重写百分比** aof_base_size
以配置文件启动服务器
1 2 3
redis-server redis-6379.conf 可以在redis-6.0.0.4下创建一个conf文件夹,用来存放各个不同的配置文件。** redis-server conf/redis-6379.conf
2、持久化
持久化:利用永久性存储介质将数据进行保存,在特定的时间将保存的数据进行恢复的工作机制称为持款化。
为什么持久化:防止数据意外丢失,确保数据安全性。
持久化方式
1快照(RDB)
将当前数据状态进行保存,快照形式,存储数据结果,存储格式简单,关注点在数据。 10100011100这样的二进制的数据(快照)。
1快照(RDB)保存command为:在客户端使用save save命令是阻塞式的,直到完成持久化,若时间过长,后面指令就不会执行。(线上不推荐使用),可以用bgsave。
关于RDB持久化配置都在配置文件中。
自动save配置:save second changes save 900 1 在900秒内,若有一个Key发生变化就保存。
缺点:
- 基于快照思想,每次读写都是全部数据,当数据量巨大时,效率非 常低。
- 大数据量下的IO性能较低
- 基于fork创建子进程,内存产生额外消耗
- 宕机带来的数据丢失风险
2日志(AOF)
append only file
将数据的操作过程进行保存,日志形式,存储操作过程,存储格式复杂,关注点在数据的操作过程。
像文本编辑器的Ctrl+Z Ctrl+Y都是记录的过程。
●AOF(append only file)持久化: 以独立日志的方式记录每次写命令,重启时再重新执行AOF文件中命令达到恢复数据的目的。 与RDB相比可以简单描述为改记录数据为记录数据产生的过程 ●AOF的主要作用是解决了数据持久化的实时性,目前已经是Redis持久化的主流方式
AOF写数据的三种策略:always(每次)、everysec(每秒)、no(系统控制)
若使用AOP需要在配置文件配置 appendonly yes/no 是否开启AOP默认不开启 appendfsync always/everysec/no 写数据策略 appendfilename appendonly-端口号.aof文件名称
AOF重写 降低磁盘使用量,提高持久化效率,提高数据恢复效率。
手动:bgrewriteaof。
自动:配置文件配置。
- auto-aof-rewrite-min-size size 自动重写的最小值
- auto-aof-rewrite-percentage percentage 自动重写百分比
与其对比的参数为
- aof_current_size 当前大小,和重写最小值有关
- aof_base_size 基础大小,和百分比有关
其触发条件有两个:
- 1 最小值>当前大小
- 2 (当前大小-基础大小)基础大小>自动重写百分比
如何选择
RDB当数据量大时,保存快照速度很慢。恢复速度快,适合阶段性有效的程序(比如游戏等,丢失一小时数据可以接受)。
若对数据敏感,采用默认AOF,使用everysecond,每秒fsync一次,可保证出现问题时,最多丢失0-1秒数据。由于AOF文件存储体积大,恢复速度慢。
也可以都打开,优先用AOF
3、事务
事务基本操作
开启:multi
操作,从开启到执行,中间的操作,都会放到一个事务中执行。
在事务过程中若发生问题,可以取消事务,discard。
错误1:若语法格式有问题 比如set s1 asb写成seta s1 asb,那么整个事务中的所有命令都不会执行。
错误2:若命令执行出错误,如对List进行incr操作, 正确语句会执行,错误将不会执行。需要程序员自己在程序中回滚。。
执行:exec
3-1、事务-锁
锁
问题:商品的补货有两个人可以进行,若一个人进行了补货,那么其他人就不再进行,如何知道其他人已经补货了呢?
watch key1 key2 对key进行加锁监控,若发生变化就不执行接下来的事务。
unwatch 取消所有key的监视
注意,watch必须在一个事务开启前进行
还可以使用setnx lock-key value设置一个公共锁,操作完毕删除。 但若宕机,就可能产生死锁,可以使用。 expire lock-key second 设置时间-秒 pexpire lock-key millisecond设置时间-毫秒
操作通常是毫秒/微秒,所以需要测试得出。
第三章-redis部署
1主从复制
就是做一个redis集群,由master负责写,slave负责读,提高服务器读写负载能力。
负载均衡:基于主从结构,配合读写分离,由slave分担master负载, 并根据需求的变化,改变slave的数量,通过多个从节点分担数据读取负载,提高Redis服务器并发量与数据吞吐量。
one master ,two slave
主从复制
redis默认是主数据,所以master无需配置,我们只需要修改slave的配置即可。
slave配置:
slave配置文件中,设置 port selfPort6390 自己的端口号 slaveof masterIP masterPort 主人的IP地址和端口号
如果master有密码,需要设置:Masterauth 密码
连接成功后可以info replication查看连接信息
2哨兵sentinel
**哨兵模式sentinel /ˈsentɪnl/ **
一台主机,两台从机,3/5/7奇数哨兵。
Redis sentinel 是一个分布式系统中监控 redis 主从服务器,并在主服务器下线时自动进行故障转移。其中三个特性:
监控(Monitoring):Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。
哨兵模式启动:哨兵
|
|
windos下5.0redis若启动哨兵不报错,就说明启动成功,只是没有第一次启动的信息。
3集群
集群工作方式:在redis的每一个节点上,都有这么两个东西,一个是插槽(slot),它的的取值范围是:0-16383。还有一个就是cluster,可以理解为是一个集群管理的插件。当我们的存取的key到达的时候,redis会根据crc16的算法得出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,通过这个值,去找到对应的插槽所对应的节点,然后直接自动跳转到这个对应的节点上进行存取操作。
1安装rubyinstaller-2.3.3.exe,修改安装地址,三个都勾选。 2在redis地址gem install redis 3官网下载源码:redis-trib.rb(当前redis版本的源码src下) 然后放到redis文件夹下 4修改配置文件:
|
|
5启动这几个服务器后,进行集群操作。 nredis-trib.rb create –replicas 1 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384 replicas 1 表示每个主数据库拥有从数据库个数为1。master节点不能少于3个,所以我们用了6个redis 然后输入YES。
6启动客户端进行测试redis-cli -h 127.0.0.1 -p 6379 -c
set s1 123发现端口号改变就说明集群成功。
4redis注意
雪崩、穿透、击穿。
雪崩
指redis中很多数据在同一时间过期,导致大量请求同一时刻请求到数据库。
解决:设置不同的过期时间,在过期时间上加随机数。
穿透
产生这个原因是大量的外部攻击,例如:请求大量id不存在的用户进行登录频繁请求接口,导致请求穿透redis频繁请求数据库。
解决:使用bloom过滤器,把用户Id先读入到bloom过滤器,然后对请求进行拦截,bloom过滤器会进行判断,过滤器判断不存在,那么一定不存在。过滤器判断存在,也有可能不存在,bloom会存在一定的误判率。
布隆过滤器的原理是,当一个元素被加入集合时,通过K个散列函数将这个元素映射成一个位数组中的K个点,把它们置为1。检索时,我们只要看看这些点是不是都是1就(大约)知道集合中有没有它了:如果这些点有任何一个0,则被检元素一定不在;如果都是1,则被检元素很可能在
击穿
是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。
解决:
可以使用互斥锁更新,保证同一个进程中针对同一个数据不会并发请求到 DB,减小 DB 压力。
使用随机退避方式,失效时随机 sleep 一个很短的时间,再次查询,如果失败再执行更新。
针对多个热点 key 同时失效的问题,可以在缓存时使用固定时间加上一个小的随机数,避免大量热点 key 同一时刻失效。
|
|
文章作者 卢森林
上次更新 2020-08-12