Redis入门到精通
NoSql概述
为什么要用NoSql
现在处于数据量爆炸的年代,传统数据库性能已经不能满足与当下的业务。
什么是NoSql
NoSql=Not only SQL ( 不仅仅是SQL )
关系型数据库:表 行 列
泛指非关系型数据库的,随着web2.0互联网的诞生!传统的关系型数据库很难对付web2.0时代。尤其是超大规模的高并发的社区!爆露出来很多难以克服的问题,NoSql在当今大环境数据环境下发展的十分迅速,Redis是发展最快的,而且是我们当下必须要掌握的技术!
很多的数据类型用户的个人信息,社交网络,地理位置。这些数据类型的存储不需要一个固定的格式!不需要多余的操作就可以实现横向扩展!
NoSql特点
1、方便扩展(数据之间没有关系,很好扩展)
2、大数据量高性能(Redis一秒写8万次,读取11万,NoSql的缓存记录是一种细粒度的缓存,性能会比较高)
3、数据类型是多样型的!(不需要事先设计数据库!随取随用!)
4、传统的RDBMS(关系型数据库)和NoSql
传统的RDBMS
- 结构化组织
- SQL
- 数据和关系都存在单独的表中 row col
- 严格的一致性
- 基础的事务
NoSql
- 不仅仅是数据
- 没有固定的查询语言
- 键值对存储,列存储,文档存储,图形数据库(社交关系)
- 最终一致性
- 高性能,高可用,高扩展
了解3V+3高
大数据的时代的3v:主要描述的问题
1.海量Volume
2.多样Variety
3.实时Velocity
大数据的时代的3高:主要描述的问题
1.高并发
2.高扩展
3.高性能
NoSql的四大分类
KV键值对:
- 新浪: Redis
- 美团:Redis+tair
- 阿里、百度:Redis+Memecahe
文档形数据库(bson格式和json一样)
-
MongoDB
-
主要存储文档文档类型
-
MongoDB是一个基于分布式文件存储的数据库,c++编写,主要用来处理大量的文档
-
MongoDB是介于关系型数据库和非关系型数据库的中间产品,是非关系型数据库功能能最丰富的
-
列存储数据库
- HBase
- 分布式文件系统
图关系型数据库
主要是用来梳理各个节点之间的关系,推荐系统,构建关系图谱会用到
- Neo4J
- InfoGrid
Redis入门
什么是Redis
Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
Redis能干吗
1、内存存储,持久化,内存是断电即失,所以说持久化很重要(rdb、aof)
2、效率高可用于缓存
3、发布定域系统
4、地图信息分析
5、计时器、计数器
Redis的特性
1、多样的存储数据类型
2、持久化
3、集群
4、事务
Windows安装
自行百度
Linux安装
# 1官网下载https://redis.io/download 最新redis.tar.gzip安装包上传至服务器并压缩
# 2进入解压后的目录执行下面命令
[root@pinyoyougou-docker redis-6.2.4]# yum install gcc-c++
[root@pinyoyougou-docker redis-6.2.4]# make
[root@pinyoyougou-docker redis-6.2.4]# make install
# 3进入到/usr/local/bin目录就能找到安装的Redis
# 4将redis配置文件保存到bin目录下
[root@pinyoyougou-docker redis-6.2.4]# cp -a redis.conf ../bin
# redis默认不是后台启动的,修改配置文件 将daemonize 改为yes
[root@pinyoyougou-docker redis-6.2.4]# vim redis.conf
# 启动redis服务 如果启动报错就把配置文件删了再重新copy一下
[root@pinyoyougou-docker redis-6.2.4]# redis-server redis.conf
基础的知识
Redis默认有16个数据库
默认使用的是第0个数据库
可以使用select进行切换
[root@pinyoyougou-docker bin]# redis-cli
127.0.0.1:6379> select 3
OK
#显示数据库的存储大小
127.0.0.1:6379[3]> DBSIZE
#显示所有的key
127.0.0.1:6379[3]> keys *
#清空所有数据库的数据
127.0.0.1:6379[3]> flushall
#清空当前数据库
127.0.0.1:6379[3]> flushdb
Redis是单线程的
因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。
redis为什么单线程还那么快?
1、误区1:高性能的服务器一定是多线程的?
2、误区2:多线程(cpu上下文会切换!)一定比单线程效率高!?
核心:redis是将所有的数据全部放在内存中的,所以说使用单线程去操作效率是最高的,多线程会存在cpu上下文切换的耗时问题,对于内存
系统来讲则不需要去使用多线程操作
五大数据类型
Redis-Key
# 设置过期时间
127.0.0.1:6379> EXPIRE name 10
(integer) 1
# 查看过期时间
127.0.0.1:6379> ttl name
# 当前key的类型
127.0.0.1:6379> type name
String(字符串)
基础
# 设置值
127.0.0.1:6379> set name fanyuanxin
OK
# 追加值 没有key 则set值
127.0.0.1:6379> APPEND name 123
(integer) 13
127.0.0.1:6379> get name
"fanyuanxin123"
# 查看字符长度
127.0.0.1:6379> STRLEN name
(integer) 13
# 自增1
127.0.0.1:6379> INCR views
(integer) 1
# 自减1
127.0.0.1:6379> DECR views
(integer) 1
# 增长10
127.0.0.1:6379> INCRBY views 10
# 减去10
127.0.0.1:6379> DECRBY views 20
# 设置name值
127.0.0.1:6379> set name fanyuanxin1234
OK
# 取 三位
127.0.0.1:6379> GETRANGE name 0 2
"fan"
# 获取全部的字符串和get key 是一样的
127.0.0.1:6379> GETRANGE name 0 -1
"fanyuanxin1234"
# 替换
127.0.0.1:6379> set number 1012323
OK
127.0.0.1:6379> SETRANGE number 3 44
(integer) 7
127.0.0.1:6379> get number
"1014423"
重点
# setex (set with expire) 设置过期时间
127.0.0.1:6379> clear
127.0.0.1:6379> setex key3 30 fanyuanxin
OK
127.0.0.1:6379> ttl key3
(integer) 24
#setnx (不存在设置) 分布式锁常用 如果存在key就保存不成功 如果不存在Key就保存成功
127.0.0.1:6379> SETNX key4 yuanxin
(integer) 1
127.0.0.1:6379> SETNX key4 yuanxin
(integer) 0
# 批量设置Key
127.0.0.1:6379> mset key1 k1 k2 k2 k3 k3
OK
# 批量get key
127.0.0.1:6379> mget key1 k2 k3
1) "k1"
2) "k2"
3) "k3"
# 批量不存在设置是原子操作 要不全成功 要不全失败
127.0.0.1:6379> MSETNX key4 yuanxin Key1 ke
# 设置对象字符串
127.0.0.1:6379> mset user:1:name zhangsan
OK
127.0.0.1:6379> get user:1:name
"zhangsan"
# getset 操作 获取并替换
127.0.0.1:6379> getset name fanyuanxin
(nil)
127.0.0.1:6379> getset name fanyuanxin12
"fanyuanxin"
127.0.0.1:6379> getset name fanyuanxin13
"fanyuanxin12"
应用场景
- 计数器(incr)
- 分布式锁(setnx)
List(列表)
我们可以把List完成,栈,队列,阻塞队列!
# 添加一个List值
127.0.0.1:6379> LPUSH list one
(integer) 1
127.0.0.1:6379> LPUSH list two
(integer) 2
127.0.0.1:6379> LPUSH list three
(integer) 3
# 查询全部List值
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
3) "one"
# 查询前两个值
127.0.0.1:6379> LRANGE list 0 1
1) "three"
2) "two"
# 向右边插入一条数据
127.0.0.1:6379> RPUSH list four
(integer) 4
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
3) "one"
4) "four"
# 移除左边的元素
127.0.0.1:6379> LPOP list
"two"
# 移除右边的元素
"four"
127.0.0.1:6379> RPOP list
# 通过下标获取值
127.0.0.1:6379> LINDEX list 0
"one"
# 返回列表长度
127.0.0.1:6379> Llen list
# 移除指定的值
127.0.0.1:6379> LPUSH list fanyuanxin
(integer) 1
127.0.0.1:6379> LPUSH list zhangsan
(integer) 2
127.0.0.1:6379> LPUSH list lisi
(integer) 3
127.0.0.1:6379> LPUSH list lisiwangwu
(integer) 4
127.0.0.1:6379> LRANGE list 0 -1
1) "lisiwangwu"
2) "lisi"
3) "zhangsan"
4) "fanyuanxin"
127.0.0.1:6379> LREM list 1 lisi
####################################### 移除固定长的的list集合
127.0.0.1:6379> LPUSH list data1
(integer) 1
127.0.0.1:6379> LPUSH list data2
(integer) 2
127.0.0.1:6379> LPUSH list data3
(integer) 3
127.0.0.1:6379> LTRIM list 1 2
OK
127.0.0.1:6379> LRANGE list 0 -1
1) "data2"
2) "data1"
#################################### 移除列表的第一个元素到其他列表
127.0.0.1:6379> LPUSH list data1
(integer) 1
127.0.0.1:6379> LPUSH list data2
(integer) 2
127.0.0.1:6379> LPUSH list data3
(integer) 3
127.0.0.1:6379> RPOPLPUSH list mylist
"data1"
127.0.0.1:6379> LRANGE mylist 0 -1
1) "data1"
################################### 将列表中指定的索引值替换成另外一个值 更新操作
127.0.0.1:6379> LPUSH list data1
(integer) 1
127.0.0.1:6379> LPUSH list data2
(integer) 2
127.0.0.1:6379> LPUSH list data3
(integer) 3
127.0.0.1:6379> LSET list 0 data10
OK
127.0.0.1:6379> LRANGE list 0 -1
1) "data10"
2) "data2"
3) "data1"
################################### 将数据插入指定值的前面(after|before)或者后面
127.0.0.1:6379> LRANGE list 0 -1
1) "data10"
2) "data2"
3) "data1"
127.0.0.1:6379> LINSERT list AFTER data2 data3
(integer) 4
127.0.0.1:6379> LRANGE list 0 -1
127.0.0.1:6379> LINSERT list before data2 data4
小结
- 他实际上是一个链表 before Node after ,left right 都可以插入值
- 如果key不存在创建新的链表
- 如果key存在,新增内容
- 如果移除了所有值,空链表,也代表不存在
- 在两边插入值或者改动效率最高,中间元素相对来讲效率会低一点
消息队列(Lpush Rpop)
栈(Lpush Lpop)
Set(集合)
Set是无序不重复集合
##############################
127.0.0.1:6379> sadd myset hello # set集合中添加一个元素
(integer) 1
127.0.0.1:6379> sadd myset yuanxin
(integer) 1
127.0.0.1:6379> sadd myset loveyuanxin
(integer) 1
127.0.0.1:6379> SMEMBERS myset # 查看指定set的所有值
1) "loveyuanxin"
2) "yuanxin"
3) "hello"
127.0.0.1:6379> SISMEMBER myset hello # 判断某一个值是否再set集合中
(integer) 1
127.0.0.1:6379> SISMEMBER myset test
(integer) 0
#################################
127.0.0.1:6379> SCARD myset # 获取集合中元素的个数
(integer) 3
127.0.0.1:6379> SREM myset hello #移除集合中的指定元素
(integer) 1
###################################
127.0.0.1:6379> SRANDMEMBER myset #随机抽出指定集合的值
"yuanxin"
127.0.0.1:6379> SRANDMEMBER myset
"loveyuanxin"
##################################
127.0.0.1:6379> SPOP myset # 随机移除指定集合中的值
"yuanxin"
127.0.0.1:6379> SMEMBERS myset
1) "loveyuanxin"
##################################
127.0.0.1:6379> SADD myset1 hello #将一个指定的值移植到另外一个Set集合当中
(integer) 1
127.0.0.1:6379> SADD myset2 hello
(integer) 1
127.0.0.1:6379> SADD myset1 hello1
(integer) 1
127.0.0.1:6379> SMOVE myset1 myset2 hello1
(integer) 1
127.0.0.1:6379> SMEMBERS myset2
1) "hello1"
2) "hello"
127.0.0.1:6379> SMEMBERS myset1
1) "hello"
################################ 重点 可以使用这个api可以做一些共同关注,共同爱好,推荐好友
127.0.0.1:6379> SADD mydata1 a
(integer) 1
127.0.0.1:6379> SADD mydata1 b
(integer) 1
127.0.0.1:6379> SADD mydata1 c
(integer) 1
127.0.0.1:6379> SADD mydata2 c
(integer) 1
127.0.0.1:6379> SADD mydata2 d
(integer) 1
127.0.0.1:6379> SADD mydata2 e
(integer) 1
127.0.0.1:6379> SDIFF mydata1 mydata2 #查看mydata1 与mydata2不同的值
1) "b"
2) "a"
127.0.0.1:6379> SINTER mydata1 mydata2 #查看相同的值
1) "c"
127.0.0.1:6379> SUNION mydata1 mydata2 #查看两个集合总共的值
1) "b"
2) "c"
3) "a"
4) "e"
5) "d"
Hash(哈希)
Map集合,key-map
#############################################
127.0.0.1:6379> hset myhash name yuanxin age 18 #基础操作
(integer) 2
127.0.0.1:6379> hget myhash name
"yuanxin"
127.0.0.1:6379> hmget myhash name age
1) "yuanxin"
2) "18"
127.0.0.1:6379> hgetall myhash
1) "name"
2) "yuanxin"
3) "age"
4) "18"
#############################################
127.0.0.1:6379> HDEL myhash name #删除指定的map值
(integer) 1
127.0.0.1:6379> hgetall myhash
1) "age"
2) "18"
#############################################
127.0.0.1:6379> hgetall myhash #获取hash的长度
1) "age"
2) "18"
127.0.0.1:6379> HLEN myhash
(integer) 1
###########################################
127.0.0.1:6379> HEXISTS myhash name # 获取指定集合中是否存在指定的key
(integer) 0
127.0.0.1:6379> HEXISTS myhash age
(integer) 1
###########################################
127.0.0.1:6379> HKEYS myhash #获取所有的Key
1) "age"
127.0.0.1:6379> HVALS myhash #获取所有的value
1) "18"
###########################################
127.0.0.1:6379> HINCRBY myhash age 1 #hash自增数
(integer) 19
127.0.0.1:6379> HINCRBY myhash age -1 # hash自动减1
(integer) 18
127.0.0.1:6379> HSETNX myhash name yuanxin #判断是否存在 存在报错不存在添加
(integer) 1
127.0.0.1:6379> HSETNX myhash name yuanxin
(integer) 0
hash更适合存储用户的数据,String更适合字符串的存储
Zset(有序集合)
#######################################
127.0.0.1:6379> ZADD myset 1 one #基础操作
(integer) 1
127.0.0.1:6379> ZADD myset 2 two 3 yuanxin
(integer) 2
127.0.0.1:6379> ZRANGE myset 0 -1
1) "one"
2) "two"
3) "yuanxin"
#####################################
127.0.0.1:6379> ZADD salary 1000 zhangsan
(integer) 1
127.0.0.1:6379> ZADD salary 500 yuanxin
(integer) 1
127.0.0.1:6379> ZADD salary 5000 lisi
(integer) 1
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf # 查询最小的到最大的所有值
1) "yuanxin"
2) "zhangsan"
3) "lisi"
127.0.0.1:6379> ZREVRANGE salary 0 -1 # 查询最大到最小的所有值
1) "lisi"
2) "zhangsan"
3) "yuanxin"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf 2000 withscores # 查询最小值到2000值的数据并显示数值
1) "yuanxin"
2) "500"
3) "zhangsan"
4) "1000"
#####################################
127.0.0.1:6379> ZCARD salary #查询zset数量
(integer) 3
127.0.0.1:6379> ZREM salary yuanxin #元素移除
(integer) 1
127.0.0.1:6379> ZRANGE salary 0 -1
1) "zhangsan"
2) "lisi"
######################################
127.0.0.1:6379> zcount salary -inf +inf #区间统计 也可以是指定的例如 100 200
(integer) 2
ZSET主要做排序什么的或者可以通过1、2、3做消息的权重
三种特殊数据类型
geospatial地理位置
朋友的定位,附近的人,打车距离计算
Redis的GEO在3.2版本就推出了,这个功能可以推算地理位置的信息,两地之间的距离,方圆几里的人!
经纬度查询测试数据:https://jingweidu.bmcx.com/
#添加地理位置命令 geoadd key 经度 纬度 名称
#注意:不能直接添加北极和南极的坐标
127.0.0.1:6379> GEOADD china:city 113.26925238281248 34.84496433471186 gongyi
(integer) 1
127.0.0.1:6379> GEOADD china:city 113.6401 34.72468 zhengzhou
(integer) 1
127.0.0.1:6379> GEOADD china:city 116.23128 40.22077 beijing
(integer) 1
127.0.0.1:6379> GEOADD china:city 91.13775 29.65262 lasa
(integer) 1
127.0.0.1:6379> GEOADD china:city 121.48941 31.40527 shanghai
(integer) 1
127.0.0.1:6379> GEOADD china:city 120.21201 30.2084 hangzhou
(integer) 1
#获取城市的经纬度
127.0.0.1:6379> GEOPOS china:city gongyi
1) 1) "113.26925486326217651"
2) "34.84496512806270374"
127.0.0.1:6379> GEOPOS china:city gongyi
1) 1) "113.26925486326217651"
2) "34.84496512806270374"
127.0.0.1:6379> GEOPOS china:city gongyi beijing
1) 1) "113.26925486326217651"
2) "34.84496512806270374"
2) 1) "116.23128265142440796"
2) "40.22076905438526495"
#比较两个城市之间的距离
127.0.0.1:6379> GEODIST china:city beijing gongyi km
"652.4058"
#获取附近的人业务实现
#获取经度113 维度34 1000千米以内的数据
127.0.0.1:6379> GEORADIUS china:city 113 34 1000 km
1) "gongyi"
2) "zhengzhou"
3) "beijing"
4) "hangzhou"
5) "shanghai"
127.0.0.1:6379> GEORADIUS china:city 113 34 500 km
1) "gongyi"
2) "zhengzhou"
#获取经度113 维度34 1000千米以内的数据名称及经纬度
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km withdist withcoord
1) 1) "hangzhou"
2) "982.5796"
2) 1) "gongyi"
2) "620.0669"
3) 1) "zhengzhou"
2) "626.8644"
#获取经度113 维度34 1000千米的数据名称及经纬度 前两条数据
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km withdist withcoord count 2
1) 1) "gongyi"
2) "620.0669"
2) 1) "zhengzhou"
2) "626.8644"
# 以key值为半径获取1000千米的数据名称及经纬度
127.0.0.1:6379> GEORADIUSBYMEMBER china:city gongyi 1000 km
1) "gongyi"
2) "zhengzhou"
3) "beijing"
4) "hangzhou"
5) "shanghai"
#查看全部的元素
127.0.0.1:6379> ZRANGE china:city 0 -1
1) "lasa"
2) "hangzhou"
3) "shanghai"
4) "gongyi"
5) "zhengzhou"
6) "beijing"
#移除指定元素
127.0.0.1:6379> ZREM china:city beijing
(integer) 1
Hyperloglog
Redis2.8.9版本更新了Hyperloglog数据结构
Redis Hyperloglog 基数统计的算法!
优点:占用的内存是固定的,2^64长度只需要12kb的内存!如果要从内存角度来比较的话Hyperloglog首选
弊端: 0.81%错误率
应用场景 :网页的UV(一个人访问一个网站多次,但还是算作一个人!)
传统方式用Set类型可以解决 但是如果用户数量大就会严重占用内存
#添加mykey集合
127.0.0.1:6379> PFADD mykey a b c d f v m d i o
(integer) 1
# 统计
127.0.0.1:6379> PFCOUNT mykey
(integer) 9
127.0.0.1:6379> PFADD mykey2 a b c d f v m d i o
(integer) 1
#两个集合合并
127.0.0.1:6379> PFCOUNT mykey mykey2
(integer) 13
如果允许容错,那么一定可以使用Hyperloglog
Bitmaps
位图,数据结构 都是操作二进制来进行记录,就只有0和1两个状态
应用场景:统计用户活跃信息 登录 未登录 打卡 两个状态的都可以使用BitMaps
#保存一个用户1234 0(表示未打卡) 1(表示打卡)
127.0.0.1:6379> SETBIT uni:user:20170101 1234 0
(integer) 0
127.0.0.1:6379> getbit uni:user:20170101 1234
(integer) 0
#统计这个时间段打卡的人数
127.0.0.1:6379> bitcount uni:user:20170101
(integer) 1
事务
Redis事务不保证原子性!
Redis事务的本质:一组命令的集合 在事务的过程中,会按照顺序执行!
Redis特性:一次性,顺序性,排他性
事务过程
-
开启事务(multi)
-
命令入队(........)
-
执行事务(exec)
-
或者回滚事务(discard)
正常事务
#开启事务
127.0.0.1:6379> multi
OK
# 添加命令
127.0.0.1:6379(TX)> set key1 v1
QUEUED
127.0.0.1:6379(TX)> set key2 v2
QUEUED
127.0.0.1:6379(TX)> get key2
QUEUED
127.0.0.1:6379(TX)> set key3 v3
QUEUED
# 执行提交成功
127.0.0.1:6379(TX)> exec
1) OK
2) OK
3) "v2"
4) OK
手动回滚事务
#开启事务
127.0.0.1:6379> multi
OK
# 添加命令
127.0.0.1:6379(TX)> set key1 v1
QUEUED
127.0.0.1:6379(TX)> set key2 v2
QUEUED
127.0.0.1:6379(TX)> get key2
QUEUED
127.0.0.1:6379(TX)> set key3 v3
QUEUED
# 回滚
127.0.0.1:6379(TX)> discard
编译型异常
# 开启事务
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set key1 v1
QUEUED
127.0.0.1:6379(TX)> set key2 v2
QUEUED
#命令报错
127.0.0.1:6379(TX)> getset key3
(error) ERR wrong number of arguments for 'getset' command
# 事务提交的时候发现提交不了 会发现所有的数据回滚
127.0.0.1:6379(TX)> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get key1
"v1"
127.0.0.1:6379> get key2
"v2"
运行时异常
#开启事务
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set key1 txkey1
QUEUED
127.0.0.1:6379(TX)> set key2 txkey2
QUEUED
#设置字符串值运行时添加报错
127.0.0.1:6379(TX)> set key3 "hello"
QUEUED
127.0.0.1:6379(TX)> INCR key3
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) OK
3) OK
4) (error) ERR value is not an integer or out of range
# 这里发现在事务中修改的值还是生效的
127.0.0.1:6379> get key1
"txkey1"
Redis 实现乐观锁 watch(命令)
# 创建一个key
127.0.0.1:6379> INCRBY money 1000
(integer) 1000
# 监视Key
127.0.0.1:6379> WATCH money
OK
# 开启事务
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> INCRBY money 1000
QUEUED
127.0.0.1:6379(TX)> exec #再开一个客户端修改watch的值之后提交事务发现提交失败
(error) EXECABORT Transaction discarded because of previous errors.
######################提交事务失败后监视回自动取消 然后再watch Key 进行事务操作
#然后再重新监视
127.0.0.1:6379> WATCH money
OK
#开启事务
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> INCRBY money 100
QUEUED
#提交事务
127.0.0.1:6379(TX)> exec
1) (integer) 1000
整合Jedis
-
引入Maven
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.2.0</version> </dependency>
-
编写连
代码package com.fyx.jedis; import redis.clients.jedis.Jedis; /** * @author yuanxin * @Date 2021/6/16 14:15 **/ public class TestPing { public static void main(String[] args) { Jedis jedis = new Jedis("127.0.0.1", 6379); System.out.println(jedis.ping()); } }
常用的API(百度Jedis常用api)
SpringBoot整合
SpringBoot操作数据:Spring-data jpa jdbc MongoDB redis!
SpringData也是和SpringBoot其名的项目!
说明:再SpringBoot2.x之后,原来使用的Jedis呗替换了lettuce
Jedis:采用的直连,多个线程操作的话,是不安全的,如果想要避免不安全的.使用 jedis pool连接池!
lettuce:采用netty,实力可以再多个线程中进行共享,不存再线程不安全的情况!可以减少
具体操作百度
Redis.conf详解
启动的时候,就通过Redis配置文件来启动
网络
bind 127.0.0.1 -::1 # 绑定的ip
protected-mode yes # 是否受保护
port # 端口设置
通用GENERAL
daemonize yes # 以守护进程的方式运行,默认是no, 我们需要自己开启为yes!
pidfile /var/run/redis_6379.pid # 如果指定了守护方式运行需要指定pid文件
# 日志
# Specify the server verbosity level.
# This can be one of:
# debug (a lot of information, useful for development/testing)
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably) 生产环境级别
# warning (only very important / critical messages are logged)
loglevel notice
logfile "" # 日志的文件位置名
databases 16 # 数据库的数量,默认是16 个数据库
always-show-logo no # 显示Logo
快照
持久化,再规定的时间内,执行了多少次操作,则会持久化到文件。.rdb .aof文件
Redis是内存书库,如果没有持久化,那么数据断电及失!
#如果900秒内至少有一个key进行修改 就进行持久化操作
save 3600 1
#3000秒之内至少有100个key进行修改 就进行持久化操作
save 300 100
#如果60s内,如果至少10000个key进行修改 就进行持久化操作
save 60 10000
# 持久化如果失败是否还要继续工作
stop-writes-on-bgsave-error yes
# 是否压缩rdb文件 需要消耗cpu的资源
rdbcompression yes
# 保存rdb文件的时候进行错误校验
rdbchecksum yes
# rdb 文件保存的路径
dir ./
REPLICATION 主从复制 百度
SECURITY 安全
# requirepass foobared
requirepass fyxzuishuai12
限制 CLIENTS
# 配置最大的客户端连接数
maxclients 10000
# 设置redis占用的最大内存
maxmemory <bytes>
# 内存达到上限的时候触发的策略
maxmemory-policy noeviction
1、volatile-lru:只对设置了过期时间的key进行LRU(默认值)
2、allkeys-lru : 删除lru算法的key
3、volatile-random:随机删除即将过期key
4、allkeys-random:随机删除
5、volatile-ttl : 删除即将过期的
6、noeviction : 永不过期,返回错误
APPEND ONLY MODE (aof) 持久化方案
#默认开启的是rdb持久化方式
appendonly no
#aof 文件名
appendfilename "appendonly.aof"
# always (每次修改都会同步 消耗性能)
# everysec (每秒执行一次sync 可能回丢失1s的数据)
# no 不执行sync 这个时候操作系统自己同步数据 速度最快
appendfsync everysec
Redis持久化
RDB(Redis DataBase)
什么是RDB
设置rdb
- 修改redis.conf文件 更改快照保存策略
#10秒内至少有一个key修改就会触发snapshot保存操作
save 10 1
触发机制
1、符合save 机制触发
2、执行flushall命令,也会触发我们的rdb规则
3、退出redis ,也会产生rdb文件!
如何恢复rdb文件
1、只需要将rdb文件放在我们redis启动目录就可以,redis启动的时候会自动检查dump.rdb 恢复其中的数据
2、查看配置需要存在的位置
127.0.0.1:6379> config get dir
1) "dir"
2) "/usr/local/bin"
优点:
1、适合大规模的数据恢复
2、对数据的完整经要求不高
缺点:
1、需要一定的时间间隔
2、fork进程的时候,会占用一定的内存空间
AOF(Append Only File)
以日志的形式来记录每个写的操作,只需追加文件不许改写文件,redis启动就会读取改文件重新构建数据 ,大数据量情况下性能缓慢
设置AOF
############################## APPEND ONLY MODE ###############################
# By default Redis asynchronously dumps the dataset on disk. This mode is
# good enough in many applications, but an issue with the Redis process or
# a power outage may result into a few minutes of writes lost (depending on
# the configured save points).
#
# The Append Only File is an alternative persistence mode that provides
# much better durability. For instance using the default data fsync policy
# (see later in the config file) Redis can lose just one second of writes in a
# dramatic event like a server power outage, or a single write if something
# wrong with the Redis process itself happens, but the operating system is
# still running correctly.
#
# AOF and RDB persistence can be enabled at the same time without problems.
# If the AOF is enabled on startup Redis will load the AOF, that is the file
# with the better durability guarantees.
#
# Please check https://redis.io/topics/persistence for more information.
# 默认是不开启的
appendonly yes
# aof文件名
appendfilename "appendonly.aof"
#重启过后就会发现多了一个aof文件
[root@pinyoyougou-docker bin]# ls
appendonly.aof dump.rdb redis-benchmark redis-check-aof redis-check-rdb redis-cli redis.conf redis-sentinel redis-server
数据测试
[root@pinyoyougou-docker bin]# redis-cli
127.0.0.1:6379> auth fyxzuishuai12
OK
127.0.0.1:6379> flsuhall
(error) ERR unknown command `flsuhall`, with args beginning with:
127.0.0.1:6379> flushall
OK
127.0.0.1:6379> set name fanyuanxin
OK
127.0.0.1:6379> set age 10
OK
127.0.0.1:6379> set dick 19cm
# 接下来shutdown关闭redis服务 然后重启 发现数据还是存在的
127.0.0.1:6379> auth fyxzuishuai12
OK
127.0.0.1:6379> get name
"fanyuanxin"
AOF 文件损坏修复
如果这个AOF文件有错误,这时候redis是启动不了的 redis提供了redis-check-aof --fix 修复aof
#破坏aof文件
[root@pinyoyougou-docker bin]# vim appendonly.aof
[root@pinyoyougou-docker bin]# redis-server redis.conf
#发现redis 服务没启动启来
[root@pinyoyougou-docker bin]# netstat -tunlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1068/sshd
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 2071/master
tcp6 0 0 :::22 :::* LISTEN 1068/sshd
tcp6 0 0 ::1:25 :::* LISTEN 2071/master
udp 0 0 0.0.0.0:8201 0.0.0.0:* 764/dhclient
udp 0 0 0.0.0.0:68 0.0.0.0:* 764/dhclient
udp6 0 0 :::42497 :::* 764/dhclient
# 使用官方的aof文件修复
[root@pinyoyougou-docker bin]# redis-check-aof --fix appendonly.aof
0x 90: Expected prefix '*', got: 's'
AOF analyzed: size=168, ok_up_to=144, ok_up_to_line=30, diff=24
This will shrink the AOF from 168 bytes, with 24 bytes, to 144 bytes
Continue? [y/N]: y
Successfully truncated AOF
[root@pinyoyougou-docker bin]# redis-server redis.conf
# 发现服务启动
[root@pinyoyougou-docker bin]# netstat -tunlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.1:6379 0.0.0.0:* LISTEN 2410/redis-server 1
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1068/sshd
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 2071/master
tcp6 0 0 ::1:6379 :::* LISTEN 2410/redis-server 1
tcp6 0 0 :::22 :::* LISTEN 1068/sshd
tcp6 0 0 ::1:25 :::* LISTEN 2071/master
udp 0 0 0.0.0.0:8201 0.0.0.0:* 764/dhclient
udp 0 0 0.0.0.0:68 0.0.0.0:* 764/dhclient
udp6 0 0 :::42497 :::* 764/dhclient
优缺点
优点:
1、每次修改都同步,文件的完整会更好
2、每秒同步一次,可能会丢失一秒的数据
3、从不同步,效率最高
缺点:
1、aof文件大,修复速度远慢与rdb
2、aof效率也要比rdb慢,所以我们默认的配置就是rdb持久化
Redis发布订阅
Redis发布订阅(pub/sub是一种消息通信模式):发送者(pub)发送消息,订阅者(sub)接受消息。微信,微博,关注系统
测试
# 发送端 PUBLISH <频道名>
127.0.0.1:6379> PUBLISH yuanxin nihao
(integer) 1
# 订阅端 SUBSCRIBE <频道名>
127.0.0.1:6379> SUBSCRIBE yuanxin
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "yuanxin"
3) (integer) 1
1) "message"
2) "yuanxin"
3) "nihao"
Redis主从复制
主从复制,读写分离。主负责写,从负责读,主从架构最少有三台机器
主节点可以有多个从节点,从节点只能有一个主节点
只用配置从库,不用配置主库,redis默认的配置就是主库的配置
# 查看当前库的信息
127.0.0.1:6379> info replication
# Replication
# 主机
role:master
# 从机0
connected_slaves:0
master_failover_state:no-failover
master_replid:d3e24756ffaa35a2a0b0ecc17a16acb309940052
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
单机模拟主从复制
1、复制三份redis.conf配置文件然后修改端口
port 端口
logfile "端口.log"
dbfilename "dump端口.rdb"
2、启动三次服务
3、设置80、81 为从机
#连接80 81 端口 redis服务器 执行下面命令
127.0.0.1:6380> SLAVEOF 127.0.0.1 6379
127.0.0.1:6380> INFO replication
# Replication
role:slave
这样的配置是暂时性的,想要永久修改配置就可以
# 配置主机ip 端口
# slaveof 127.0.0.1 6379
# 主机 密码
# masterauth fyxzuishuai12
重点
主机只能写,从机只能读
复制原理
Slave 启动成功连接到Master 后会发送一个异步同步命令
Master 接到命令启动后台的存盘进程,同时收集所有接收到的修改数据命令,将整个文件同步到Slave节点
全量复制
:一次接受完所有命令,完成同步 (建立主从关系的时候执行)
增量复制
:依次传递命令,完成同步(同步后主机发生改变时执行)
哨兵模式
哨兵是独立于Redis的单独进程,通过发送命令到Redis来监控服务是否存活
哨兵的两个作用
- 通过发送命令,让Redis服务器返回其运行状态,包括主服务器和从服务器
- 当哨兵检测到Master宕机,会自动开启选举机制切换从机为Master主机,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让他们切换主机
当然哨兵除了监控Redis服务之外, 哨兵与哨兵之间也会相互监控
选举过程
假设主服务器宕机,哨兵1,2,3 都会检测,一个哨兵检测到Redis服务不可用会被成为主观下线
当所有的哨兵都检测服务不可用的时候会被认为是客观下线
从而就会开始重新选举新的主机,获票多的从机当选新的主机
哨兵配置
vim sentinel.conf
#sentinel monitor 固定写法
#myredis 自定义名命
#127.0.0.1 监控那个ip 端口
#1 表示有一个哨兵认为master失效时,master才算真失效 以此类推
sentinel monitor myredis 127.0.0.1 6379 1
#设置连接密码
sentinel auth-pass mymaster 123456
#启动哨兵
[root@pinyoyougou-docker bin]# redis-sentinel sentinel.conf
然后关闭主节点6379的Redis,
# 发现6380变成了主节点
127.0.0.1:6380> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6381,state=online,offset=13904,lag=1
master_failover_state:no-failover
master_replid:d42bfd2df837d066be4b1daef9cd56215041cdc4
master_replid2:8c20e47ec07f30c543400148717e4236fd741c3b
master_repl_offset:13904
second_repl_offset:12804
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:13904
缓存穿透,缓存击穿和缓存雪崩
缓存穿透
用户请求数据库没有的数据,就请就会一直请求到数据库,而造压力
解决方案:
用户鉴权,id做基础校验,id<=0的拦截
缓存击穿
没有缓存的数据一瞬间同时请求导致的数据库压力过大
解决方案:
设置热点数据不过期、接口限流,熔断降级。布隆过滤器,加锁
缓存雪崩
同时间大批量缓存同时过期,导致访问数据库压力大导致down机
解决方案:
随机生成过期时间,设置热点数据永不过期