学习方式:不是为了面试和工作学习!仅仅是为了兴趣!兴趣才是最好的老师!
●上手就用(是什么、怎么用!)
●基本的理论先学习,然后将知识融汇贯通!
nosql概述
1、单机MySQL的年代
90年代,-个基本的网站访问量- -般不会太大,单个数据库完全足够!
那个时候,更多的去使用静态网页Html ~服务器根本没有太大的压力!
思考一下,这种情况下:整个网站的瓶颈是什么?
1、数据量如果太大、-个机器放不下了!
2、数据的索引(B+ Tree),-个机器内存也放不下
3、访问量(读写混合) , - -个服务器承受不了~
只要你开始出现以上的三种情况之- , 那么你就必须要晋级!
2.Memcached (缓存) + MySQL +垂直拆分(读写分离)
网站80%的情况都是在读,每次都要去查询数据库的话就十分的麻烦!所以说我们希望减轻数据的压力, 我们可以使用缓存来保证
效率!
发展过程:优化数据结构和索引–>文件缓存(IO ) --> Memcached (当时最热门的技术! )
不缺人,你们的竞争对手并不是人才,而是那些图安稳又踏实(老实人)
3、分库分表+水平拆分+ MySQL集群
技术和业务在发展的同时,对人的要求也越来越高!
本质:数据库(读,写)
早些年MyISAM:表锁,十分影响效率!高并发下就会出现严重的锁问题
转战Innodb :行锁
慢慢的就开始使用分库分表来解决写的压力! MySQL 在哪个年代推出了表分区!这个并没有多少公司使用!
MySQL的集群,很好满足哪个年代的所有需求!
4、如今最近的年代
2010–2020十年之间,世界已经发生了翻天覆地的变化: ( 定位,也是一种数据,音乐,热榜! )
2010–2020十年之间,世界已经发生了翻天覆地的变化; (定位,也是一种数据,音乐,热榜! )
MySQL等关系型数据库就不够用了!数据量很多,变化很快~ !
MySQL有的使用它来村粗一些比较大的文件,博客,图片!数据库表很大,效率就低了! 如果有- -种数据库来专门处理这种数据,
MySQL压力就变得十分小(研究如何处理这些问题! )大数据的I0压力下,表几乎没法更大!
目前一个基本的互联网项目!
为什么要用NoSQL!
用户的个人信息,社交网络,地理位置。用户自己产生的数据,用户日志等等爆发式增长!
这时候我们就需要使用NoSQL数据库的, Nosql可以很好的处理以上的情况!|
什么是NoSQL
NoSQL=Bot Only SQL(不仅仅是 SQL)
关系型数据库:表格,行,列
泛指非关系型数据库的,随着web2.0互联网的诞生!传统的关系型数据库很难对付web2.0时代!尤其是超大规模的高并发的社
区!暴露出来很多难以克服的问题, NoSQL在当今大数据环境下发展的十分迅速, Redis是发展最快的,而且是我们当下必须要掌
握的一个技术!
很多的数据类型用户的个人信息,社交网络 ,地理位置。这些数据类型的存储不需要一个固定的格式!不需要多月的操作就可以横
向扩展的! Map<String,Object> 使用键值对来控制!
NoSQL特点
解耦!
1、方便扩展(数据之间没有关系,很好扩展! )
2、大数据量高性能( Redis -秒写8万次,读取11万, NoSQL的缓存记录级,是-种细粒度的缓存,性能会比较高! )
3、数据类型是多样型的! (不需要事先设计数据库!随取随用!如果是数据量十分大的表,很多人就无法设计了! )
4、传统RDBMS和NoSQL
传统的RDBMS
-结构化组织
- SQL
-数据和关系都存在单独的表中
-操作操作,数据定义语言
-严格的一致性
-基础的事务
-…
Nosql
-不仅仅是数据
-没有固定的查询语言
-健值对存储,列存储,文档存储,图形数据库(社交关系)
-最终一致性,
- CAP定理和BASE(异地多活)初级架构师! (狂神理念: 只要学不死,就往死里学! )
-高性能,高可用,高可扩
-…
了解:3V+3高
大数据时代的3V :主要是描述问题的
1.海量Volume
2.多样Variety
3.实时Velocity
大数据时代的3高:主要是对程序的要求
1.高并发
2.高可扩
3.高性能
真正在公司中的实践: NoSQL + RDBMS -起使用才是最强的,阿里巴巴的架构演进!
技术没有高低之分,就看你如何去使用! ( 提升内功,思维的提高! )
●阿里巴巴架构演进分析
技术急不得,越是慢慢学,才能越扎实! .
开源才是技术的王道!
任何一家互联网的公司,都不可能只是简简单单让用户能用就好了!
大量公司做的都是相同的业务; ( 竞品协议)
随着这样的竞争,业务是越来越完善,然后对于开发者的要求也是越来越高!
高中,大一开始就应该是认真的学习了!
如果你未来相当一个架构师:没有什么是加一层解决不了的!
# 1、商品的基本信息
名称、价格、商家信息;
关系型数据库就可以解决了! MySQL / oracle (浏宝早年就去IOE了! -王坚:推荐文章:阿里云的这群疯子: 40分钟重要! )
淘宝内部的MySQL不是大家用的MySQL
# 2、商品的描述、评论(文字比较多)
文档型数据库中,MongoDB
#3、图片
分布式文件系统FastDFs
-淘宝自己的 FFs
- Gooale的 GFS
- Hadoop HDFS
-阿里云的 oss
# 4、商品的关键字 (搜索)
-搜索引擎 solr elasticsearch
ISerach: 多隆(多去了解一些技术大佬)
所有牛遥的人都有- -段苦逼的岁月:但是你只要像SB-一样的去坚持,终将牛逼!
# 5、商品热门的波段信息
-内存数据库
-Redis Tair、Memache.. .
# 6、商品的交易,外部的支付接口
-三方应用
要知道,一个简单地网页背后的技术-定不是大家所想的那么简单!
大型互联网应用问题:
●数据类型太多了!
●数据源繁多,经常重构!
●数据要改造,大面积改造?
解决问题:
●Nosql 四大分类
KV键值对:
●新浪:Redis
●美团: Redis + Tair
●阿里、百度: Redis + memecache
文档型数据库( bson格式和json一样) :
●MongoDB ( -般必须要掌握)
。MongoDB是一个基于分布式文件存储的数据库, C++编写,主要用来处理大量的文档!
。MongoDB 是-个介于关系型数据库和非关系型数据中中间的产品! MongoDB是非关系型数据库中功能最丰富,最像关
系型数据库的!
●ConthDB
列存储数据库
●HBase
●分布式文件系统
图关系数据库
●他不是存图形,放的是关系,比如:朋友圈社交网络广告推荐!
●Neo4j,InfoGrid ;
四者对比!
敬畏之心可以使人进步!宇宙!科幻!
活着的意义?追求幸福(帮助他人,感恩之心),探索未知 (不断的学习,未知的宇宙!)
■Redis入门
概述
Redis ( Remote Dictionary Server ),即远程字典服务!
是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库 ,并提供多种语言的API。
免费和开源!是当下最热门的 NoSQL 技术之一! 也被人们称之为结构化数据库!
Redis能干嘛?
1、 内存存储、持久化,内存中是断电即失、所以说持久化很重要( rdb、aof)
2、效率高,可以用于高速缓存
3、发布订阅系统
4、地图信息分析
5、计时器、计数器(浏览量!)
6、…
特性
1、多样的数据类型
2、持久化
3、集群
4、事务
…
学习中需要用到的东西
1、狂神的公众号:狂神说
2、官网: 添加链接描述
3、中文网: 添加链接描述
4、下载地址:通过官网下载即可
注意: Wdinow在Github上下载(停更很久了! )
Redis推荐都是在Linux服务器上搭建的,我们是基于Linux学习!
Windows安装
1、下载安装包: 添加链接描述
2、下载完毕得到压缩包:
3、解压到自己电脑上的环境目录下的就可以的! Redis十分的小,只有5M
4、开启Redis ,双击运行服务即可!
5、使用redis客户单来来连接redis
记住一句话,Window下使用确实简单,但是Redis推荐我们使用Linux去开发使用!
Linux安装
1、下载安装包! redis-5.0.8.tar gz
2、解压Redis的安装包!程序
tar -zxvf red1s-5.0.8.tar.gz
3、进入解压后的文件,可以看到我们redis的配置文件
4、基本的环境安装 j进入到解压过后的redis文件里面
yum insta11 gCC-C++
make
make install
5、redis的默认安装路径 /usr/local/bin
6.将redis配置文件。复制到我们当前目录下
7、redis默认不是后台启动的,修改配置文件!
8、启动Redis服务!
9.使用redis-cli 进行连接测试!
10、查看redis 是否开启
11、如何关闭Redis服务命令
12、后面我J会使用毕机多RedIs后动集群测试!
测试性能
redis-benchmark是一个压力测试工具!
官方自带的性能测试工具!
redis-benchmark命令参数!
redis性能测试工具可选参数如下所示:
我们来简单测试下:
#测试: 100个并发连接100000请求
redis-benchmark -h localhost -p 6379 -C 100 -n 10000
如何查看这些分析呢?
基础的知识
redis默认有16个数据库
默认使用的是第0个
可以使用select进行切换数据库!
127.0.0.1:6379> select 3 #切换数据库
OK
127.0.0.1:6379[3]> DBSIZE #查看D大小!
(integer) 0
127.0.0.1:6379[3]> keys 白
#查看数据库所有的key
1) "name'
清除当前数据库 flushdb
清除当前数据库 flushall
思考:为什么redis是6379! (了解一下即可!)
Redis是单线程的!
明白Redis是很快的,官方表示, Redis是基于内存操作, CPU不是Redis性能瓶颈, Redis的瓶颈是根据机器的内存和网络带宽,既然可以使用单线程来实现,就使用单线程了!所以就是使用了单线程!
Redis是C语言写的,官方提供的数据为10000+ 的QPS ,完全不比同样是使用key-vale的Memecache差!
Redis为什么单线程还这么快?
1.误区1 :高性能的服务器- -定是多线程的?
2、误区2:多线程( CPU上下文会切换! ) -定比单线程效率高!
先去CPU>内存>硬盘的速度要有所了解!
核心: redis是将所有的数据全部放在内存中的,所以说使用单线程去操作效率就是最高的,多线程( CPU上下文会切换:耗时的操作! ! ! ) , 对于内存系统来说,如果没有上下文切换效率就是最高的!多次读写都是在一个CPU 上的,在内存情况下,这个就是最佳的方案!
■五大数据类型
官网文档
全段翻译:
Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。
我们现在讲解的所有命令大家-定要全部记住,后面我们使用SpringBoot。Jedis ,所有的方法就是这些命令!
Redsi-Key
127.0.0.1:6379> keys * # 查看所有的key
(empty list or set)
127.0.0.1:6379> set name kuangshen #set key
OK
127.0.0.1:6379> keys
1) "name"
127.0.0.1:6379> set age 1
0K
127.0.0.1:6379> keys *
1) "age"
2) "name"
127. 0.0.1:6379> EXISTS name #判断当前的key是否存在
I
(integer) 1
127.0.0.1:6379> EXISTS name1
(integer) 0
127.0.0.1:6379> move name 1 #移除当前的key
(integer) 1
127.0.0.1:6379> keys *
1) "age"
127.0.0.1:6379> set name qinjiang
OK
127.0.0.1:6379> keys *
1) "ages"
2) "name"
127.0.0.1:6379> clear
127.0.0.1:6379> keys *
127.0.0.1:6379> EXPIRE name 10 #设置key的过期时间,单位是秒
(integer) 1
127.0.0.1:6379> tt1 name #查看当前key的剩余时间
(integer) 4
127.0.0.1:6379> tt1 name
(integer) 3
127.0.0.1:6379> tt1 name
(integer) 2
127.0.0.1:6379> tt1 name
(integer) 1
127.0.0.1:6379> tt1 name
(integer) -2
127.0.0.1:6379> get name
(ni1)
127.0.0.1:6379> type name # 查看当前key的一个类型!
string
I
127.0.0.1:6379> type age
string
后面如果遇到不会的命令,可以在官网查看帮助文档!
String(字符串)
90%的java程序员使用redis只会使用一个String类型!
-------------------------------------------
127.0.0.1:6379> set key1 v1 #设置值
OK
127.0.0.1:6379> get key1 #获得值
"v1"
127.0.0.1:6379> keys * #获得所有的key
1) "key1"
127.0.0.1:6379> EXISTS key1 #判断某一个key是否存在
(integer) 1
127.0.0. 1:6379> APPEND key1 "he11o" #追加字符串,如果当前key不存在,就相当于setkey
(integer) 7
127.0.0.1:6379> get key1
"v1he1lo"
127.0.0.1:6379> STRLEN key1 # 获取字符串的长度!
(integer) 7
127.0.0.1:6379> APPEND key1 ",kaungshen"
(integer) 17
127.0.0.1:6379> STRLEN key1
(integer) 17
127.0.0.1:6379> get key1
"v1he1lo, kaungshen"
---------------------------------------------------------
#i++
#步长i+=
127.0.0.1:6379> set views 0 #初始浏览量为0
OK
127.0.0.1:6379> get views
"0"
127.0.0.1:6379> incr views #自增1浏览量变为1
(integer) 1
127.0.0.1:6379> incr views
(integer) 2
127.0.0.1:6379> get views
"2"
127.0.0.1:6379> decr views #自减1 浏览量-1
(integer) 1
127.0.0.1:6379> decr vi ews
(integer) 0
127.0.0.1:6379> decr vi ews
(integer) -1
127.0.0.1:6379> get vi ews
"-1"
127.0.0.1:6379> ENCRBY views 10
(integer) 9
127.0.0.1:6379> INCRBY views 10 #可以设置步长,指定增量!
(integer) 19
127.0.0.1:6379> DECRBY views 5
(integer) 14
----------------------------------------------
#字符串范围range
127.0.0.1:6379> set key1 "hel1o, kuangshen" #设置key1的值
OK
127.0.0.1:6379> get key1
"he11o , kuangshen "
127.0.0.1:6379> GETRANGE key 1 0 3
#截取字符串[0,3]
"he1l"
127. 0.0.1:6379> GETRANGE key1 0 -1 # 获取全部的字符串和get key是一样的
"hel1o, kuangshen "
#替换!
127.0.0.1:6379> set key2 abcdefg
0K
127.0.0.1:6379> get key2
"abcdefg"
127.0.0.1:6379> SETRANGE key2 1 Xx # 替换指定位置开始的字符串!
(integer) 7
127.0.0.1:6379> get key2
"axxdefg"
----------------------------------------------------
# setex (set with expire) #设置过期时间
# setnx(set if not exist)#不存在在设置(在分布式锁中会 常常使用! )
127.0.0.1:6379> setex key3 30 "he11o" #设置key3的值为he11o, 30秒后过期
OK
127.0.0.1:6379> tt1 key3
(integer) 26
127.0.0.1:6379> get key3
"he1lo"
127.0.0.1:6379> setnx mykey "redis" #如果mykey不存在,创建mykey
(integer) 1
127.0.0.1:6379> keys白
1) "key2"
2) "mykey"
3) "key1"
127.0.0.1:6379> tt1 key3
(integer) -2
127.0.0.1:6379> setnx mykey "MongoDB" #如果mykey存在,创建失败!
(integer) 0
127.0.0.1:6379> get mykey
"redis"
---------------------------------------------------
mset
mget
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 #同时设置多 个值
OK
127.0.0.1:6379> keys *
1) "k1"
2) "k2"
3) "k3"
127.0.0.1:6379> mget k1 k2 k3 # 同时获取多 个值
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379> msetnx k1 v1 k4 v4 # msetnx 是一个原子性的操作,要么一一起成功,要么- -起失败!
(integer) 0
127.0.0.1:6379> get k4
(ni1)
#对象
set user:1 {name:zhangsan,age:3} # 设置一个user:1 对象值为json字符来保存一个对象!
#这里的key是- -个巧妙的设计: user:{id}:{filed},如此设计 在Redis中是完全0K 了!
127.0.0.1:6379> mset user:1:name zhangsan user:1:age 2
OK
127.0.0.1:6379> mget user:1:name user:l:age
1) "zhangsan"
2) "2"
------------------------------------------------------------
getset #先get然后在set
127.0.0.1:6379> getset db redis # 如果不存在值,则返回nil
(ni1)
127.0.0.1:6379> get db
"redis"
127.0.0.1:6379> getset db mongodb # 如果存在值,获取原来的值,并设置新的值
"redis"
127.0.0.1:6379> get db
'mongodb"
数据结构是相同的
String类似的使用场景: value除了是我们的字符串还可以是我们的数字!
●计数器
●统计多单位的数量
●粉丝数
●对象缓存存储!
List
基本的数据类型,列表
在redis里面,我们可以把list玩成, 栈、队列、阻塞队列!
所有的list命令都是用I开头的,redis不区分大小写命令
127.0.0.1:6379> LPUSH 1ist one # 将- 一个值或者多个值,插入到列表头部(左)
(integer) 1
127.0.0.1:6379> LPUSH list two
(integer) 2
127.0.0.1:6379> LPUSH list three
(integer) 3
127.0.0.1:6379> LRANGE list 0 -1
#获取1ist中值!
1) "three"
2)'two'
3)"one"
127.0.0.1:6379> LRANGE 1ist 0 1 #通过区间获取具体的值!
1) "three"
2) "two"
127.0.0.1:6379> Rpush list righr # 将一个值或者多个值,插入到列表位部(右)
(integer) 4
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
3) "one"
4) "righr"
-----------------------------------------------------
LPOP
RPOP
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
3) "one'"
4) "righr"
127.0.0.1:6379> Lpop list # 移除1ist的第一个元素
"three"
127. 0.0.1:6379> Rpop list # 移除list的最后-一个元素
"ri ghr"
127.0.0.1:6379> LRANGE list 0 -1
1) "two"
2) "one"
Lindex
127.0.0.1:6379> LRANGE list 0 -1
1)"two"
2)"one"
127.0.0.1:6379> 1index 1ist 1 # 通过下标获得1ist 中的某-一个值!
"one"
127.0.0.1:6379> 1index list 0
"two"
-------------------------------------------------------
Llen
127.0.0.1:6379> Lpush 1ist one
(integer) 1
127.0.0.1:6379> Lpush 1ist two
(integer) 2
127.0.0.1:6379> Lpush list three
(integer) 3
127.0.0.1:6379> Llen 1ist # 返回列表的长度
(integer) 3
----------------------------------------------
移除指定的值!
取关uid
Lrem
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "three"
3) "two"
4) "one"
127.0.0.1:6379> 1rem list 1 one # 移除1ist集合中指定个数的value,精确匹配
(integer) 1
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "three"
3) "two"
127.0.0.1:6379> Trem list 1 three
(integer) 1
127.0.0.1:6379> LRANGE list 0 -1
1) "three"
2) "two"
127.0.0.1:6379> Lpush 1ist three
(integer) 3
127.0.0.1:6379> 1rem list 2 three
(integer) 2
127.0.0.1:6379> LRANGE 1ist 0 -1
1) "two"
-------------------------------------------------------
trim修剪.: 1ist 截断!
127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379> Rpush mylist "hel1o"
(integer) 1 I
127.0.0.1:6379> Rpush mylist "he11o1"
(integer) 2
127.0.0.1:6379> Rpush mylist "he11o2"
(integer) 3
127.0.0.1:6379> Rpush my1ist "he11o3"
(integer) 4
127.0.0.1:6379> 1trim mylist 1 2 #通过下标截取指定的长度,这个1ist已经被改变了,截断了只剩下截取的元素!
OK
127.0.0.1:6379> LRANGE mylist 0 -1
1) "he11o1"
2) "he11o2"
---------------------------------------------------------
rpoplpush #移除列表的最后一个元素,将他移动到新的列表中!
127.0.0.1:6379> rpush my1ist "he11o"
(integer) 1
127.0.0.1:6379> rpush my1ist "he11o1"
(integer) 2
127.0.0.1:6379> rpush mylist "he11o2"
(integer) 3
127.0.0.1:6379> rpoplpush my7ist myotherlist
"he11o2"
127.0.0.1:6379> Trangel my1ist 0 -1 #查看原来的列表
1) "hel1o"
2) "he11o1"
127.0.0.1:6379> Trange myotherlist 0 -1 #查看目标列表中,确实存在该值!
1) "he1lo2"
---------------------------------------------------------
lset 将列表中指定下标的值替换为另外-一个值, 更新操作
127.0.0. 1:6379> EXISTS 1ist # 判断这个列表是否存在
(integer) 0
127.0.0.1:6379> 1set list 0 item # 如果不存在列表我们去更新就会报错
(error) ERR no such key
127.0.0.1:6379> 1push 1ist value1
(integer) 1
127.0.0.1:6379> LRANGE 1ist 0 0
1) "value1"
127.0.0.1:6379> 1set list 0 item #如果存在,更新当前下标的值
OK
127.0.0.1:6379> LRANGE list 0 0
1) "item"
127.0.0.1:6379> Iset list 1 other #如果不存在,则会报错!
(error) ERR index out of range
------------------------------------
linsert #将某个具体的value插入到列把你中某个元素的前面或者后面!
127.0.0.1:6379> Rpush mylist "he11o"
(integer) 1
127.0.0.1:6379> Rpush mylist "world"
(integer) 2
127.0.0.1:6379> LINSERT mylist before "wor1ld" "other"
(integer) 3
127.0.0.1:6379> LRANGE my1ist 0 -1
1) "hel1o"
2) "other"
3) "wor1d"
127.0.0.1:6379> LINSERT my1ist after wor1d new
(integer) 4
127.0.0.1:6379> LRANGE mylist 0 -1
1) "he1lo"
2) "other"
3) "world"
4) "new"
小结
●他实际上是一一个链表, before Node after ,left , right都可以插入值
●如果key 不存在,创建新的链表
●如果key存在,新增内容
●如果移除了所有值,空链表,也代表不存在!
●在两边插入或者改动值,效率最高!中间元素,相对来说效率会低- -点~
消息排队!消息队列(Lpush Rpop) ,栈( Lpush Lpop)
Set(集合)
set中的值是不能重复的
127.0.0.1:6379> sadd myset "he11o"
# set集合中添加匀速
(integer) 1
127.0.0.1:6379> sadd myset "kuangshen"
(integer) 1
127.0.0.1:6379> sadd myset "lovekuangshen "
(integer) 1
127.0.0.1:6379> SMEMBERS myset #查看指定set的所有值
1) "hello"
2) "7ovekuangshen"
3) "kuangshen"
127.0.0.1:6379> SISMEMBER myset he11o #判断某一个值是不是在set集合中!
(integer) 1
127.0.0.1:6379> SISMEMBER myset world
(integer) 0
--------------------------------------------------
127.0.0.1:6379> scard myset # 获取set集合中的内容元素个数!
(integer) 4
-----------------------------------------------------
rem 移除元素值
127.0.0.1:6379> srem myset he11o # 移除set集合中的指定元素
(integer) 1
127.0.0.1:6379> scard myset
(integer) 3
127.0.0.1:6379> SMEMBERS myset
1) "lovekuangshen2"
2) "1ovekuangshen"
3) "kuangshen"
-------------------------------------------------------
set 无序不重复集合。抽随机!
127.0.0.1:6379> SMEMBERS myset
1) "Tovekuangshen2"
2) "1 ovekuangshen"
3) "kuangshen"
127.0.0.1:6379> SRANDMEMBER myset #随机抽选出一个元素
"kuangshen"
127.0.0.1:6379> SRANDMEMBER myset
"kuangshen"
127.0.0.1:6379> SRANDMEMBER myset
"kuangshen"
127.0.0.1:6379> SRANDMEMBER myset
"kuangshen"
127.0.0.1:6379> SRANDMEMBER myset 2 #随机抽选指定个数的元素
1) "1ovekuangshen"
2) "Tovekuangshen2"
127.0.0.1:6379> SRANDMEMBER myset 2
1) "Tovekuangshen"
2) "Tovekuangshen2"
127.0.0.1:6379> SRANDMEMBER myset #随机抽选出一个元素
"lovekuangshen2"
------------------------------------------------------
删除定的key,随机删除key!
127.0.0.1:6379> SMEMBERS myset
1) "7 ovekuangshen2"
2) "1ovekuangshen
3) "kuangshen
127.0.0.1:6379> spop myset #随机删除一些set集合中的元素!
"lovekuangshen2"
127.0.0.1:6379> spop myset
"lovekuangshen"
127.0.0.1:6379> SMEMBERS myset
1) "kuangshen"
-------------------------------------------------
将一个指定的值,移动到另外一个set集合!
127.0.0.1:6379> sadd myset "he11o"
(integer) 1
127.0.0.1:6379> sadd myset "world"
(integer) 1
127.0.0.1:6379> sadd myset "kuangshen"
(integer) 1
127.0.0.1:6379> sadd myset2 "set2"
(integer) 1
127.0.0.1:6379> smove myset myset2 "kuangshen" #将一个指定的值,移动到另外一个set集合!
(integer) 1
127.0.0.1:6379> SMEMBERS myset
1) "world"
2) "he11o"
127.0.0.1:6379> SMEMBERS myset2
1) "kuangshen "
2) "set2"
--------------------------------------------------------
微博,B站,共同关注! (并集)
数字集合类:
-差集SDIFF
-交集
-并集
127.0.0.1:6379> SDIFF key1 key2
#差集
1) "b"
2) "a"
127.0.0.1:6379> SINTER key1 key2 # 交集共同好 友就可以这样实现
1) "c"
127.0.0.1:6379> SUNION key1 key2 # 并集
1) "b"
2) "c"
3) "e"
4) "a"
5) "d"
微博,A用户将所有关注的人放在- -个set集合中!将它的粉丝也放在一个集合中!
共同关注,共同爱好,二度好友,推荐好友(六度分割理论)
Hash
Map集合, key-map!时候这个值是一个map集合 !本质和String类型没有太大区别,还是一-个简单的key-vlaue !
127.0.0.1:6379> hset myhash fie1d1 kuangshen # set- - 个具体key-vlaue
(integer) 1
127.0.0.1:6379> hget myhash fie1d1 #获取一个字段值
"kuangshen"
127.0.0.1:6379> hmset myhash fie1d1 he11o fie1d2 world # set多个key-vlaue
OK
127.0.0.1:6379> hmget myhash fie1d1 fie1d2
#获取多个字段值
1) "he11o"
2) "world"
127.0.0.1:6379> hgeta11 myhash
#获取全部的数据,
1) "field1"
2) "he11o"
3) "field2"
4) "world"
127.0.0.1:6379> hde1 myhash fie1d1 # 删除hash指定key字段! 对应的value值也就消失了!
(integer) 1
127.0.0.1:6379> hgetal1 myhash
1) "field2"
2) "world"
-------------------------------------------------------------------------------
hlen 获取hash表的字段数量
127.0.0.1:6379> hmset myhash fie1dl hello fie1d2 world
OK
127.0.0.1:6379> HGETALL myhash
1)"field2"
2)"world"
3)"fie1d1"
4)"hel1o"
127.0.0.1:6379> hlen myhash # 获取hash表的字段数量!
(integer) 2
-------------------------------------------------------------------------------
127.0.0.1:6379> HEXISTS myhash fie1d1 # 判断hash中指定字段是否存在!
(integer) 1
127.0.0.1:6379> HEXISTS myhash field3
(integer) 0
-------------------------------------------------------------------------------
#只获得所有field
#只获得所有value
127.0.0.1:6379> hkeys myhash # 只获得所有field
1) "fie1d2"
2) "field1"
127.0.0.1:6379> hvals myhash # 只获得所有value
1) "world"
2) "he11o"
-------------------------------------------------------------------
incr decr
127.0.0.1:6379> hset myhash field3 5 #指定增量!
(integer) 1
127.0.0.1:6379> HINCRBY myhash field3 1
(integer) 6
127.0.0.1:6379> HINCRBY myhash fie1d3 -1
(integer) 5
127.0.0.1:6379> hsetnx myhash fie1d4 he1lo # 如果不存在则可以设置
(integer) 1
127.0.0.1:6379> hsetnx myhash fie1d4 world # 如果存在则不能设置
(integer) 0
hash变更的数据user name age,尤其是是用户信息之类的,经常变动的信息! hash 更适合于对象的存储, String更加适合字符串存储!
Zset(有序集合)
在set的基础上,增加了一个值, set k1 v1 zset k1 score1 v1
127.0.0.1:6379> zadd myset 1 one #添加一个值
(integer) 1
127.0.0.1:6379> zadd myset 2 two 3 three #添加多个值
(integer) 2
127.0.0.1:6379> ZRANGE myset 0 -1
1) "one"
2) "two"
3) "three"
------------------------------------------------------------------
排序如何实现
三种特殊数据类型
geospatial地理位置
朋友的定位,附近的人,打车距离计算?
Redis的Geo在Redis3.2版本就推出了!这个功能可以推算地理位置的信息,两地之间的距离,方圆几里的人!
可以查询一些测试数据:经纬度查询
getadd
#getadd 添加地理位置
#规则:两级无法直接添加,我们一般会下载城市数据,直接通过java程序一次性导入!
#参数 key 值(经度、纬度、名称)
#有效的经度从-180度到180度。
#有效的纬度从-85.05112878度到85.05112878度。
#当坐标位置超出上述指定范围时,该命令将会返回一个错误。
# 127.0.0. 1:6379> geoadd china:city 39.90 116.40 beijin
127.0.0.1:6379> geoadd chind:city 116.40 39.90 beijing
(integer) 1
127.0.0.1:6379> geoadd chind:city 121.47 31.23 shanghai
(integer) 1
127.0.0.1:6379> geoadd chind:city 106.50 29.53 chongqing
(integer) 1
127.0.0.1:6379> geoadd chind:city 114.05 22.52 shenzhen
(integer) 1
127.0.0.1:6379> geoadd chind:city 120.16 30.24 hangzhou
(integer) 1
getpos
127.0.0.1:6379> geopos chind:city beijing #获取指定的城市的经度和纬度!
1) 1) "116.39999896287918091"
2) "39.90000009167092543"
127.0.0.1:6379> geopos chind:city beijing chongqing
1) 1) "116.39999896287918091"
2) "39.90000009167092543"
2) 1) "106.49999767541885376"
2) "29.52999957900659211"
geodist
两人之间的距离!
单位:
●m表示单位为米。
●km表示单位为千米。
●mi表示单位为英里。
●ft 表示单位为英尺。
127.0.0.1:6379> geodist chind:city beijing chongqing #查看上海到北京的直线距离
"1464070.8051"
127.0.0.1:6379> geodist chind:city beijing chongqing km #查看重庆到北京的直线距离
"1464.0708"
我附近的人?(获取所有附近的人的地址,定位!)通过半径来查询!
georadius以给定的经纬度为中心,找出某一半径内的元素
获得指定数量的人, 200
所有数据应该都录入: china:city ,才会让结果更加请求!
127.0.0.1:6379> georadius chind:city 110 30 1000 km #以110,30这个经纬度为中心,寻找方圆1000km内的城市
1) "chongqing"
2) "shenzhen"
3) "hangzhou"
127.0.0.1:6379> georadius chind:city 110 30 500 km
1) "chongqing"
127.0.0.1:6379> georadius chind:city 110 30 500 km withcoord #显示他人的定位信息
1) 1) "chongqing"
2) 1) "106.49999767541885376"
2) "29.52999957900659211"
127.0.0.1:6379> georadius chind:city 110 30 500 km withdist #显示到中间距离的位置
1) 1) "chongqing"
2) "341.9374"
127.0.0.1:6379> georadius chind:city 110 30 500 km withdist withcoord count
(error) ERR syntax error
127.0.0.1:6379> georadius chind:city 110 30 500 km withdist withcoord count 2 #筛选出指定的结果!(count 接后面筛选的数量)
1) 1) "chongqing"
2) "341.9374"
3) 1) "106.49999767541885376"
2) "29.52999957900659211"
127.0.0.1:6379>
georadisbymember
#找出位于指定元素周围的其他元素!
127.0.0.1:6379> georadiusbymember chind:city beijing 1000 km
1) "beijing"
127.0.0.1:6379> georadiusbymember chind:city beijing 4000 km
1) "chongqing"
2) "shenzhen"
3) "hangzhou"
4) "shanghai"
5) "beijing"
geohash 命令-返回一个或多个位置元素的Geohash表示
该命令将返回11个字符的Geohash字符串!
#将二维的经纬度转换为一维的字符串,如果两个字符串约接近则越相似
127.0.0.1:6379> geohash chind:city beijing chongqing
1) "wx4fbxxfke0"
2) "wm5xzrybty0"
GEO底层的实现原理其实就是Zset !我们可以使用Zset命令来操作geo !
127.0.0.1:6379> zrange chind:city 0 -1 #查看地图中全部元素
1) "chongqing"
2) "shenzhen"
3) "hangzhou"
4) "shanghai"
5) "beijing"
127.0.0.1:6379> zrem chind:city beijing #移除指定元素
(integer) 1
127.0.0.1:6379> zrange chind:city 0 -1
1) "chongqing"
2) "shenzhen"
3) "hangzhou"
4) "shanghai"
127.0.0.1:6379>
Hyperloglog
什么是基数?
A{1,3,5,7,8,7}
B{1 , 3,5,7,8}
基数(不重复的元素)=5,可以接受误差
简介
Redis 2.8.9版本就更新了Hyperloglog 数据结构!
Redis Hyperloglog基数统计的算法!
优点:占用的内存是固定, 2^64不同的元素的技术,只需要废12KB内存!如果要从内存角度来比较的话
网页的UV ( -一个人访问一个网站多次,但是还是算作-个人! )
传统的方式,set 保存用户的id ,然后就可以统计set中的元素数量作为标准判断!
这个方式如果保存大量的用户id ,就会比较麻烦!我们的目的是为了计数,而不是保存用户id ;
测试使用
127.0.0.1:6379> PFadd mykey a b c d e f g h i j #创建第一组元素 mykey
(integer) 1
127.0.0.1:6379> pfcount mykey #统计mykey元素的技术数量
(integer) 10
127.0.0.1:6379> pfadd mykey2 i j z x c v b n m #创建第二组元素 mykey2
(integer) 1
127.0.0.1:6379> pfcount mykey2
(integer) 9
127.0.0.1:6379> pfmerge mykey3 mykey mykey2 #合并两组mykey mykey2 => mykey3并集
OK
127.0.0.1:6379> pfcount mykey3 #看并集的数量!
(integer) 15
127.0.0.1:6379>
如果允许容错,那么-定可以使用Hyperloglog !
如果不允许容错,就使用set或者自己的数据类型即可!
Bitmaps
为什么其他教程都不喜欢讲这些?这些在生活中或者开发中,都有十分多的应用场景,学习了,就是就是多-个思路!
技多不压身!
位存储
统计疫情感染人数疫情: 0 1 0 1 0
统计用户信息,活跃,不活跃!登录、未登录!打卡,365打卡!两个状态的都可以使用Bitmaps
Bitmaps位图,数据结构!都是操作二进制位来进行记录,就只有0和1两个状态!
365天= 365 bit 1字节=8bit 46 个字节左右!
测试
使用bitmap来记录周- -到周日的打卡!
周一: 1周二:0周三:0周四: 1…
查看某一天是否有打卡!
127.0.0.1:6379> getbit sign 3
(integer) 1
127.0.0.1:6379> getbit sign 6
(integer) 0
统计操作,统计打卡的天数
127.0.0.1:6379> bi tcount sign #统计这周的打卡记录,就可以看到是否有全勤!
(integer) 3
事务
Redis事务没有没有隔离级别的概念!
所有的命令在事务中,并没有直接被执行!只有发起执行命令的时候才会执行!Exec
Redis事务本质: 一组命令的集合! 一个事务中的所有命令都是序列化,在事务执行过程中,会按照顺序执行!
次性、顺序性、排他性!执行一些列的命令!
-------队列set set set执行------
redis的事务:
●开启事务( multi )
●命令入队…
●执行事务( exec )
正常执行事务!
127.0.0.1:6379> mu1ti
#开启事务
OK
#命令入队
127.0.0.1:6379> set kl v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> set k3 V3
QUEUED
I
127.0.0.1:6379> exec # 执行事务
1) OK
2) OK
3) "v2"
4) OK
放弃事务
127.0.0.1:6379> multi
#开启事务
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> DISCARD
#取消事务
OK
127.0.0.1:6379> get k4 # 事务队列中命令都不会被执行!
(ni1)
编译型异常(代码有问题!命令有错!),事务中所有的命令都不会被执行
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> getset k3 #错误的命令
I
(error) ERR wrong number of arguments for ' getset ' command
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> set k5 v5
QUEUED
127.0.0.1:6379> exec #执行事务报错!
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k5 #所有的命令都不会被执行!
(ni1)
运行时异常( 1/0),如果事务队列中存在语法性,那么执行命令的时候,其他命令式可以正常执行的,错误命令抛出异常!
127.0.0.1:6379> set kl "v1"
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr k1 #会执行的时候失败!
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> get k3
QUEUED
127.0.0.1:6379> exec
1) (error) ERR value is not an integer or out of range # 虽然第一条命令报错了,但是依旧正常执行成功了!
2) OK
3) OK
4) "v3"
127.0.0.1:6379> get k2
"v2"
127.0.0.1:6379> get k3
"v3"
监控! Watch (面试常温!)
悲观锁:
●很悲观,认为什么时候都会出问题,无论做什么都会加锁!
乐观锁:
●很乐观,认为什么时候都不会出问题,所以不会上锁!更新数据的时候去判断- -下,在此期间是否有人修改过这个数据,
●获取version
●更新的时候比较version
Redis测监视测试
127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> watch money # 监视money对象
OK
127.0.0.1:6379> multi #事务正常结束,数据期间没有发生变动
OK
127.0.0.1:6379(TX)> decrby money 20
QUEUED
127.0.0.1:6379(TX)> incrby out 20
QUEUED
127.0.0.1:6379(TX)> exec
1) (integer) 80
2) (integer) 20
测试多线程修改值,使用watch可以当做redis的乐观锁操作!
127. 0.0.1:6379> watch money
#监视
money
OK
127.0.0.1:6379> mu1ti
OK
127.0.0.1:6379> DECRBY money 10
QUEUED
127.0.0.1:6379> INCRBY out 10
QUEUED
127.0.0.1:6379> exec # 执行之前,另外一个线程,修改了我们的值,这个时候,就会导致事务执行失败!
(ni1)
如果修改失败,获取最新的值就好
mysql
Redis单条命令式保存原子性的,但是事务不保证原子性!
Jedis
什么是Jedis是Redis官方推荐的java连接开发工具!使用Java 操作Redis中间件!如果你要使用java操作redis ,那么-定要对Jedis十分的熟悉!
测试
1、导入对应的依赖
<!--导入jedis的包-->
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.6.3</version>
</dependency>
<!-- fastjson-->
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
2、编码测试:
●连接数据库
●操作命令
●断开连接!
public class TestPing {
public static void main(String[] args) {
//1.new Jedis 对象即可
Jedis jedis=new Jedis("127.0.0.1",6379);
// jedis 所有的命令就是我们之前学习的所有指令!所以之前的指令学习很重要!
System.out.println(jedis.ping());
}
}
输出:
常用的API
String
List
Set
Hash
Zset
所有的api命令,就是我们对应的上面学习的指令, -一个都没有变化!
事务
public class TestTX {
public static void main(String[] args) {
Jedis jedis=new Jedis("127.0.0.1",6379);
JSONObject jsonObject=new JSONObject();
jsonObject.put("hello","world");
jsonObject.put("name","siyue");
//清空数据
jedis.flushDB();
//开启事务
Transaction multi=jedis.multi();
String result=jsonObject.toJSONString();
// jedis.watch(result);
try {
multi.set("user1",result);
multi.set("user2",result);
int i=1/0; //代码抛出异常事务,执行失败!
multi.exec(); //执行事务
}catch (Exception e){
multi.discard(); //放弃事务
e.printStackTrace();
}finally {
System.out.println(jedis.get("user1"));
System.out.println(jedis.get("user2"));
jedis.close(); //关闭连接
}
}
}
SpringBoot整合
SpringBoot操作数据: spring-data jpa jdbc mongodb redis !
SpringData也是和SpringBoot齐名的项目与!
说明:在SpringBoot2.x之后,原来使用的jedis被替换为了lettuce?
jedis :采用的直连,多个线程操作的话,是不安全的,如果想要避免不安全的,使用jedis pool 连接池!更像BIO 模式
lettuce :采用netty ,实例可以再多个线程中进行共享,不存在线程不安全的情况!可以减少线程数据了,更像NIO模式
源码分析:
@Bean
@Condi ti onalonMi ssingBean(name = "redisTemplate") // 我们可以自己定义一个redi sTemplate来替换这个默认的!
pub1ic Redi sTemplate<object,object> redisTemplate (Redi sConnecti onFactory redi sConnecti onFactory)
throws UnknownHostException {
//默认的RedisTemplate 没有过多的设置,redis 对象都是需要序列化!
//两个泛型都是object, object 的类型,我们后使用需要强制转换<String, object>
Redi sTemplate<object,object> temp1ate = new RedisTemp1ate<>O);
template. setConnecti onFactory(redi sConnecti onFactory);
return template;
}
@Bean
@ConditionalonMissingBean // 由于String 是redis中最常使用的类型,所以说单独提出来了一个bean!
pub1ic stringRedi sTemplate stringRedi sTemplate (Redi sConnecti onFactory redi sConnecti onFactory)
throws UnknownHostException {
StringRedisTemplate template = new StringRedi sTemp1ate();
template. setConnecti onFactory(redi sConnecti onFactory);
return temp1ate;
}
整合测试一下
1、导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2、配置连接
#配置redis
spring.redis.host=127.0.0.1
spring.redis.port=6379
3、测试
@SpringBootTest
class RedisJedisSpringbootApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
@Test
void contextLoads() {
// redisTemplate 操 作不同的数据类型,api 和我们的指令是一样的
// opsForValue 操作字符串 类似String
// opsForlist
// 操作List类似List
// opsForSet
// opsForHash
// opsForZSet
// opsForGeo
// opsForHyperLoglog
// redisTemplate.opsForSet().intersect()
//除了进本的操作,我们常用的方法都可以直接通过redisTemplate操作,比如事务,和基本的CR
//获取redis的连接对象
RedisConnection connection=redisTemplate.getConnectionFactory().getConnection();
connection.flushDb();
connection.flushAll();
redisTemplate.opsForValue().set("mykey","kuangshen");
System.out.println( redisTemplate.opsForValue().get("mykey"));
}
}
我们来编写一个自己的RedisTemplete
@Configuration
public class RedisConfig {
//编写我们自己的 redisTemplate
@Bean
@SuppressWarnings("all")
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory){
RedisTemplate<String,Object> template=new RedisTemplate<>();
template.setConnectionFactory(factory);
//序列化配置
Jackson2JsonRedisSerializer jackSon2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om. setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om. enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackSon2JsonRedisSerializer.setObjectMapper(om);
//String 的序列化
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用string 的序列化方式
template . setKeySerializer( stringRedisSerializer);
// hash 的key也采用String的序列化方式
template . setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用iackson
template . setValueSerializer(jackSon2JsonRedisSerializer);
// hash 的value序列化方式采用jackson
template.setHashValueSerializer(jackSon2JsonRedisSerializer);
template.afterPropertiesSet();
return template ;
}
}
所有的redis操作,其实对于java开发人员来说,十分的简单,更重要是要去理解redis的思想和每一种数据结构的用处和作用场景 !
Redis.conf详解
启动的时候,就通过配置文件来启动!
工作中, 一些小小的配置,可以让你脱颖而出!
行家有没有,出手就知道
单位
1、配置文件unit单位对大小写不敏感!
包含
就是好比我们学习Spring、Improt ,include
网络
bind 127.0.0.1
#绑定的ip
protected-mode yes #保护模式
port 6379
#端口设置
通用general
daemonize yes # 以守护进程的方式运行,默认是no, 我们需要自己开启为yes !
pidfile /var/run/redis_ _6379.pid # 如果以后台的方式运行,我们就需要指定一个pid文件!
#日志
# Specify the server verbosity 1eve1.
# This can be one of:
# debug (a 1ot of information, usefu1 for deve lopment/testing)
# verbose (many rarely usefu1 info,but not a mess like the debug 1eve1)
# notice (moderately verbose, what you want in production probab1y) 生产环境
# warning (only very important / critical messages are logged)
1og1eve1 notice
logfile "" #日志的文件位置名
databases 16
#数据库的数量,默认是16个数据库
always-show-1ogo yes # 是否总是显示LOGO
快照
持久化,在规定的时间内,执行了多少次操作,则会持久化到文件.rdb. aof
redis是内存数据库,如果没有持久化,那么数据断电及失!
#如果900s内,如果至少有一一个1 key进行了修改,我们及进行持久化操作
save 900 1
#如果300s内,如果至少10 key进行了修改,我们及进行持久化操作
save 300 10
#如果60s内,如果至少10000 key进行了修改,我们及进行持久化操作
save 60 10000
#我们之后学习持久化,会自己定义这个测试!
stop -writes -on-bgsave-error yes #持久化如果出错,是否还需要继续工作!
rdbcompression yes #是否压缩rdb 文件,需要消耗一些cpu资源!
rdbchecksum yes #保存rdb 文件的时候,进行错误的检查校验!
dir ./ # rdb 文件保存的目录!
replcation 复制,我们后面讲解主从复制的,时候再进行讲解
security 安全
可以在这里设置redis的密码,默认是没有密码!
127.0.0.1:6379> ping
PONG
127.0.0. 1:6379> config get requirepass
#获取redis的密码
1) "requi repass"
2)
127.0.0.1:6379> config set requi repass "123456"
#设置redi s的密码
OK
127.0.0. 1:6379> config get requirepass
#发现所有的命令都没有权限了
(error) NOAUTH Authentication required.
127.0.0.1:6379> ping
(error) NOAUTH Authentication required.
127.0.0.1:6379> auth 123456 # 使用密码进行登录!
OK
127.0.0.1:6379> config get requi repass
1) "requi repass"
2) "1234 56"
限制 clients
maxclients 10000 #设置能连接上redis的最大客户端的数量
maxmemory <bytes> # redis配置最大的内存容量
maxmemory-policy
nc
eviction #内存到达上限之后的处理策略
1、volatile-1ru: 只对设置了过期时间的key进行LRU (默认值)
2、a11keys-1ru :删除1ru 算法的key
3、volatile-random: 随机删除即将过期key
4、al1keys-random: 随机删除
5、volatile-tt1 :删除即将过期的
6、noeviction :永不过期, 返回错误
append only m 模式 aof配置
appendonly no #默认是不开启aof模式的, 默认是使用rdb方式持久化的, 在大部分所有的情况下,rdb完全够用!
appendfilename "appendonly.aof" # 持久化的文件的名字
# appendfsync a lways#每次修改都会sync。 消耗性能
appendfsync everysec#每秒执行一-次sync,可能会丢失这1s的数据!
# appendfsync no #不执行sync, 这个时候操作系统自己同步数据,速度最快!
具体的配置,我们在Redis持久化中去给大家详细详解!
Redis持久化
面试和工作,持久化都是重点!
Redis是内存数据库,如果不将内存中的数据库状态保存到磁盘,那么一旦服务器进程退出,服务器中的数据库状态也会消失。所以Redis提供了持久化功能!
RDB ( Redis DataBase )
什么是RDB
在主从复制中, rdb就是备用了!从机上面!
在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里。
Redis会单独创建( fork) -个子进程来进行持久化,会先将数据写入到-一个临时文件中,待持久化过程都结束了,再用这个临时文
件替换上次持久化好的文件。整个过程中,主进程是不进行任何I0操作的。这就确保了极高的性能。如果需要进行大规模数据的恢
复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。我们默认的就是RDB ,一般情况下不需要修改这个配置!
有时候在生产环境我们会将这个文件进行备份!
rdb保存的文件是dump.rdb 都是在我们的配置文件中快照中进行配置的!
触发机制
1、save的规则满足的情况下,会自动触发rdb规则
2、执行flushall命令,也会触发我们的rdb规则!
3、退出redis ,也会产生rdb文件!
备份就自动生成一一个dump.rdb
如果恢复rdb文件!
1、只需要将rdb文件放在我们redis启动目录就可以, redis启动的时候会自动检查dump.rdb恢复其中的数据!
2、查看需要存在的位置
127.0.0. 1:6379> config get dir
1) "dir"
2) "/usr/1oca1/bin" # 如果在这个目录下存在dump.rdb 文件,启动就会自动恢复其中的数据
几乎就他自己默认的配置就够用了,但是我们还是需要去学习!
优点:
1、适合大规模的数据恢复!
2.对数据的完整性要不高!
缺点:
1、需要一定的时间间隔进程操作!如果redis意外宕机了,这个最后- -次修改数据就没有的了!
2、fork进程的时候,会占用一定的内容空间! !
AOF ( Append Only File )
将我们的所有命令都记录下来, history ,恢复的时候就把这个文件全部在执行一遍!
是什么
以日志的形式来记录每个写操作,将Redis执行过的所有指令记录下来(读操作不记录) , 只许追加文件但不可以改写文件, redis
启动之初会读取该文件重新构建数据,换言之, redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作
Aof保存的是appendonly.aof文件
append
默认是不开启的,我们需要手动进行配置!我们只需要将appendofly改为yes就开启了aof ! .
如果这个aof文件有错位,这时候redis 是启动不起来的吗,我们需要修复这个aof文件
redis给我们提供了一个工具redis-check-aof --fix
如果文件正常,重启就可以直接恢复了!
appendonly no
#默认是不开启aof模式的,默认是使用rdb方式持久化的,在大部分所有的情况下,rdb完 全够用!
appendfilename "appendonly.aof" # 持久化的文件的名字
# appendfsync always #每次修改都会sync。消耗性能
appendfsync everysec #每秒执行一次sync,可能会丢失这1s的数据!
# appendfsync no #不执行sync, 这个时候操作系统自己同步数据,速度最快!
重写规则说明
aof默认就是文件的无限追加,文件会越来越大!
如果aof文件大于64m ,太大了! fork- -个新的进程来将我们的文件进行重写!
优点和缺点
优点:
1、每一次修改都同步,文件的完整会更加好!
2.每秒同步一次 ,可能会丢失一秒的数据
3、从不同步,效率最高的!
缺点:
1、相对于数据文件来说, aof远远大于rdb ,修复的速度也比rdb慢!
2、Aof运行效率也要比rdb慢,所以我们redis默认的配置就是rdb持久化!
扩展
1、RDB持久化方式能够在指定的时间间隔内对你的数据进行快照存储
2、AOF 持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据, AOF命令以Redis协
议追加保存每次写的操作到文件末尾, Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大。
3、只做缓存,如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化
4、同时开启两种持久化方式
●在这种情况下,当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB
文件保存的数据集要完整。
●RDB的数据不实时,同时使用两者时服务器重启也只会找AOF文件,那要不要只使用AOF呢?作者建议不要,因为RDB更适合
用于备份数据库( AOF在不断变化不好备份) ,快速重启,而且不会有AOF可能潜在的Bug ,留着作为一个万一的手段。
5、性能建议
●因为RDB文件只用作后备用途,建议只在Slave.上持久化RDB文件,而且只要15分钟备份一 次就够了,只保留save 9001这条
规则。
●如果Enable AOF ,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只load自己的AOF文件就可以了,代价是带来了持续的I0 ,二是AOF rewrite的最后将rewrite过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。只要硬盘许可,应该尽量减少AOF rewrite的频率, AOF重写的基础大小默认值64M太了,可以设到5G以上,默认超过原大小100%大小重写可以改到适当的数值。
●如果不Enable AOF ,仅靠Master. Slave Repllcation实现高可用性也可以,能省掉一大笔I0 ,也减少了rewrite时带来的系统
波动。代价是如果Master/Slave 同时倒掉,会丢失十几分钟的数据,启动脚本也要比较两个Master/Slave中的RDB文件,载
入较新的那个,微博就是这种架构。
Redis发布订阅
通信 队列 发送者 订阅者
Redis发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。
Redis客户端可以订阅任意数量的频道。
订阅/发布消息图:
第一个:消息发送者,第二个:频道第三个:消息订阅者!
下图展示了 频道channel1,以及订阅这个频道的三个客户端-一client2 、client5 和client1之间的关系:
当有新消息通过PUBLISH命令发送给频道channel1时,这个消息就会被发送给订阅它的三个客户端:
命令
测试
订阅端:
127.0.0.1:6379> SUBSCRIBE kuangshenshuo #订阅一个频道
Reading messages... (press ctr1-C to quit)
1) "subscribe"
2) "kuangshenshuo"
3) (integer) 1
# 等待读取推送的信息
1) "message" #消息
2) "kuangshenshuo" #那个频道发送的消息
3) "he11o ,kuangshen"
1) "message"
2) "kuangshenshuo"
3) "hello,redis"
发送端:
127.0.0.1:6379> PUBLISH kuangshenshuo "he11o,kuangshen" #发布者发送消息到频道!
(integer) 1
127.0.0.1:6379> PUBLISH kuangshenshuo "he11o, redis" #发布者发送消息到频道!
(integer) 1
127.0.0.1:6379>
原理
Redis是使用C实现的,通过分析Redis源码里的pubsub.c文件,J解发布和订阅机制的底层实现1籍此加深对Redis的理解。
Redis通过PUBLISH、SUBSCRIBE 和PSUBSCRIBE等命令实现发布和订阅功能。
微信:
通过SUBSCRIBE命令订阅某频道后, redis-server里维护了一个字典,字典的键就是一个个channel , 而字典的值则是一个链
表,链表中保存了所有订阅这个channel的客户端。SUBSCRIBE 命令的关键,就是将客户端添加到给定channel的订阅链表中。
通过PUBLISH命令向订阅者发送消息, redis-server 会使用给定的频道作为键,在它所维护的channel字典中查找记录了订阅这个频道的所有客户端的链表,遍历这个链表,将消息发布给所有订阅者。
Pub/Sub从字面上理解就是发布( Publish )与订阅( Subscribe ) , 在Redis中,你可以设定对某一个key值进行消息发布及消息订
阅,当一个key值上进行了消息发布后,所有订阅它的客户端都会收到相应的消息。这一功能最明显的用法就是 用作实时消息系
统,比如普通的即时聊天,群聊等功能。
使用场景:
1、实时消息系统!
2、事实聊天! (频道当做聊天室,将信息回显给所有人即可! )
3、订阅,关注系统都是可以的!
稍微复杂的场景我们就会使用消息中间件MQ ( )
Redis主从复制
概念
主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master/leader).后者称为从节点
(slave/follower)I 数据的复制是单向的,只能由主节点到从节点。Master以写为主, Slave以读为主。
默认情况下,每台Redis服务器都是主节点;且-个主节点可以有多个从节点(或没有从节点),但-一个从节点只能有一个主节点。
主从复制的作用主要包括:
1、数据冗余:主从复制实现了数据的热备份,是持久化之外的-种数据冗余方式。
2、故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。
3、负载均衡:在主从复制的基础.上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点) , 分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大
大提高Redis服务器的并发量。
4、高可用(集群)基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。
一般来说 ,要将Redis运用于工程项目中,只使用一台Redis是万万不能的(宕机,一主二从),原因如下:
1、从结构上,单个Redis服务器会发生单点故障,并且一台服务器需要处理所有的请求负载,压力较大;
2、从容量上,单个Redis服务器内存容量有限1就算一台Redis服务 器内存容量为256G ,也不能将所有内存用作Redis存储内存,
一般来说 ,单台Redis最大使用内存不应该超过20G。
电商网站上的商品, -般都是一次上传,无数次浏览的,说专业点也就是“多读少写"。
对于这种场景,我们可以使如下这种架构: .
主从复制,读写分离! 80% 的情况下都是在进行读操作!减缓服务器的压力!架构中经常使用! 一主二从!
只要在公司中,主从复制就是必须要使用的,因为在真实的项目中不可能单机使用Redis !
环境配置
只配置从库,不用配置主库! .
# Replication
role:master #角色
maste r
connected_ slaves:0 #没有从机
master_ replid:b63c90e6c501143759cb0e7 f450bd1eb0c70882a
master_ rep1id2 00000000000000000000000000000000000000
master_ rep1_ offset:0
second_ rep1_ offset:-1
rep1_ back1og_ _active:0
rep1_ back1og_ size :1048576
rep1_ _back1og_ _first_ byte_ offset:0
rep1_ backlog. histlen:0
复制3个配置文件,然后修改对应的信息
1、端口
2、pid 名字
3、log文件名字
4、dump.rdb 名字
修改完毕之后,启动我们的3个redis服务器,可以通过进程信息查看!
一主二从
默认情况下,每台Redis服务器都是主节点; 我们一般情况下只用配置从机就好了!认老大!一主(79)二从(80,81)
127.0.0.1:6380> SLAVEOF 127.0.0.1 6379 # SL AVEOF host 6379
找谁当自己的老大!
)K
127.0.0.1:6380> info replication
# Replication
role:slave # 当前角色是从机
master_ host:127.0.0.1 # 可以的看到主机的信息
master_ port:6379
master_ link_ status :up
master. _last_ io_ seconds_ ago:3
master_ sync_ in_ progress:0
slave_ rep1_ offset:14
slave_ priority :100
slave_ read_ only:1 .
connected_ slaves:0
master_ replid :a81be8dd257636b2d3e7a9f595e69d73ff03774e
master_ rep1id2 :00000000000000000000000000000000000
master_ rep1_ _offset:14
second_ rep1_ offset:-1
rep1_ back1og_ active:1
rep1_ _back1og_ size: 1048576
rep1_ back1og_ _first_ byte_ ofset:1
rep1_ _backlog_ histlen:14
#在主机中查看!
127.0.0.1:6379> info replication
# Replication
role :master
connected_ s1aves:1 #多了从机的配置
slave0:ip=127.0.0.1, port=6380, state=on 1ine, offset=42,1ag=1
master_ replid: a8 lbe8dd257636b2d3e7a9f595e69d73ff03774e
master_ rep1id2 0000000000000000000000000000000000000
master_ rep1_ offset:42
second_ rep1_ offset:-1
rep1_ back1og_ active:1
rep1_ back1og_ size: 1048576
rep1_ back1og_ first_ byte_ offset:1
rep1_ back1og_ histlen:42
如果两个都配置完了,就是有两个从机的
真实的从主配置应该在配置文件中配置,这样的话是永久的,我们这里使用的是命令,暂时的!
使用配置文件配置是永久的
细节
主机可以写,从机不能写只能读!主机中的所有信息和数据,都会自动被从机保存!
主机写:
从机只能读取内容!
测试:主机断开连接,从机依旧连接到主机的,但是没有写操作,这个时候,主机如果回来了,从机依旧可以直接获取到主机写的信息!
如果是使用命令行,来配置的主从,这个时候如果重启了, 就会变回主机!只要变为从机,立马就会从主机中获取值!
复制原理
Slave启动成功连接到master后会发送一个sync同步 命令
Master接到命令,启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令,在后台进程执行完毕之后, master将传送整个数据文件到slave ,并完成-次完全同步。
全量复制:而slave服务在接收到数据库文件数据后,将其存盘并加载到内存中。
增量复制: Master继续将新的所有收集到的修改命令依次传给slave ,完成同步
但是只要是重新连接master , - -次完全同步(全量复制)将被自动执行I我们的数据一定可以在从机中看到!