一、Nosql概述
1、单机Mysql时代
90年代,一个网站的访问量一般不会太大,单个数据库完全够用。随着用户增多,网站出现以下问题
网站80%的情况都是在读,每次都要去查询数据库的话就十分的麻烦!所以说我们希望减轻数据库的压力,我们可以使用缓存来保证效率!
优化过程经历了以下几个过程:
3、分库分表 + 水平拆分 + Mysql集群
4、如今最近的年代
如今信息量井喷式增长,各种各样的数据出现(用户定位数据,图片数据等),大数据的背景下关系型数据库(RDBMS)无法满足大量数据要求。Nosql数据库就能轻松解决这些问题。
目前一个基本的互联网项目
为什么要用NoSQL ?
用户的个人信息,社交网络,地理位置。用户自己产生的数据,用户日志等等爆发式增长!这时候我们就需要使用NoSQL数据库的,Nosql可以很好的处理以上的情况!
NoSQL = Not Only SQL(不仅仅是SQL)
Not Only Structured Query Language
关系型数据库:列+行,同一个表下数据的结构是一样的。
非关系型数据库:数据存储没有固定的格式,并且可以进行横向扩展。
NoSQL泛指非关系型数据库,随着web2.0互联网的诞生,传统的关系型数据库很难对付web2.0时代!尤其是超大规模的高并发的社区,暴露出来很多难以克服的问题,NoSQL在当今大数据环境下发展的十分迅速,Redis是发展最快的。
了解:3V + 3高
大数据时代的3V :主要是 描述问题的
大数据时代的3高 : 主要是 对程序的要求
真正在公司中的实践:NoSQL + RDBMS 一起使用才是最强的。
商品信息- 一般存放在关系型数据库:Mysql,阿里巴巴使用的Mysql都是经过内部改动的。# 商品描述、评论(文字居多)- 文档型数据库:MongoDB# 图片- 分布式文件系统 FastDFS- 淘宝:TFS- Google: GFS- Hadoop: HDFS- 阿里云: oss# 商品关键字 用于搜索- 搜索引擎:solr,elasticsearch- 阿里:Isearch 多隆# 商品热门的波段信息- 内存数据库:Redis,Memcache# 商品交易,外部支付接口- 第三方应用12345678910111213141516171819202122
KV键值对
- 新浪: Redis
- 美团:Redis + Tair
- 阿里、百度:Redis + Memcache
文档型数据库(bson数据格式):
- MongoDB(掌握)
- 基于分布式文件存储的数据库。C++编写,用于处理大量文档。
- MongoDB是RDBMS和NoSQL的中间产品。MongoDB是非关系型数据库中功能最丰富的,NoSQL中最像关系型数据库的数据库。
- ConthDB
列存储数据库
- HBase(大数据必学)
- 分布式文件系统
图关系数据库
用于广告推荐,社交网络
- Neo4j、InfoGrid
二、Redis入门
Redis是什么?
Redis(Remote Dictionary Server ),即远程字典服务。
是一个开源的使用ANSI C语言编写、支持网络、可基于
与memcached一样,为了保证效率, 数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
Redis能该干什么?
推荐使用Linux服务器学习。
windows版本的Redis已经停更很久了…
redis-benchmark:Redis官方提供的性能测试工具,参数选项如下:
简单测试:
测试:100个并发连接 100000请求redis-benchmark -h localhost -p 6379 -c 100 -n 10000012
redis默认有16个数据库
默认使用的第0个;
16个数据库为:DB 0~DB 15 默认使用DB 0 ,可以使用 select n
切换到DB n, dbsize
可以查看当前数据库的大小,与key数量相关。
127.0.0.1:6379> config get databases # 命令行查看数据库数量databases1) "databases"2) "16"127.0.0.1:6379> select 8 # 切换数据库 DB 8OK127.0.0.1:6379[8]> dbsize # 查看数据库大小(integer) 0# 不同数据库之间 数据是不能互通的,并且dbsize 是根据库中key的个数。127.0.0.1:6379> set name sakura OK127.0.0.1:6379> SELECT 8OK127.0.0.1:6379[8]> get name # db8中并不能获取db0中的键值对。(nil)127.0.0.1:6379[8]> DBSIZE(integer) 0127.0.0.1:6379[8]> SELECT 0OK127.0.0.1:6379> keys *1) "counter:__rand_int__"2) "mylist"3) "name"4) "key:__rand_int__"5) "myset:__rand_int__"127.0.0.1:6379> DBSIZE # size和key个数相关(integer) 512345678910111213141516171819202122232425262728
keys *
:查看当前数据库中所有的key。
flushdb
:清空当前数据库中的键值对。
flushall
:清空所有数据库的键值对。
Redis是单线程的,Redis是基于内存操作的。
所以Redis的性能瓶颈不是CPU,而是机器内存和网络带宽。
那么为什么Redis的速度如此快呢,性能这么高呢?QPS达到10W+
Redis为什么单线程还这么快?
- 误区1:高性能的服务器一定是多线程的?
- 误区2:多线程(CPU上下文会切换!)一定比单线程效率高!
核心:Redis是将所有的数据放在内存中的,所以说使用单线程去操作效率就是最高的,多线程(CPU上下文会切换:耗时的操作!),对于内存系统来说,如果没有上下文切换效率就是最高的,多次读写都是在一个CPU上的,在内存存储数据情况下,单线程就是最佳的方案。
三、五大数据类型
Redis是一个开源(BSD许可),内存存储的数据结构服务器,可用作 数据库, 高速缓存和 消息队列代理。它支持
在redis中无论什么数据类型,在数据库中都是以key-value形式保存,通过进行对Redis-key的操作,来完成对数据库中数据的操作。
下面学习的命令:
exists key
:判断键是否存在del key
:删除键值对move key db
:将键值对移动到指定数据库expire key second
:设置键值对的过期时间type key
:查看value的数据类型
127.0.0.1:6379> keys * # 查看当前数据库所有key(empty list or set)127.0.0.1:6379> set name qinjiang # set keyOK127.0.0.1:6379> set age 20OK127.0.0.1:6379> keys *1) "age"2) "name"127.0.0.1:6379> move age 1 # 将键值对移动到指定数据库(integer) 1127.0.0.1:6379> EXISTS age # 判断键是否存在(integer) 0 # 不存在127.0.0.1:6379> EXISTS name(integer) 1 # 存在127.0.0.1:6379> SELECT 1OK127.0.0.1:6379[1]> keys *1) "age"127.0.0.1:6379[1]> del age # 删除键值对(integer) 1 # 删除个数127.0.0.1:6379> set age 20OK127.0.0.1:6379> EXPIRE age 15 # 设置键值对的过期时间(integer) 1 # 设置成功 开始计数127.0.0.1:6379> ttl age # 查看key的过期剩余时间(integer) 13127.0.0.1:6379> ttl age(integer) 11127.0.0.1:6379> ttl age(integer) 9127.0.0.1:6379> ttl age(integer) -2 # -2 表示key过期,-1表示key未设置过期时间127.0.0.1:6379> get age # 过期的key 会被自动delete(nil)127.0.0.1:6379> keys *1) "name"127.0.0.1:6379> type name # 查看value的数据类型string1234567891011121314151617181920212223242526272829303132333435363738394041424344
关于 TTL
命令
Redis的key,通过TTL命令返回key的过期时间,一般来说有3种:
关于重命名 RENAME
和 RENAMENX
RENAME key newkey
修改 key 的名称RENAMENX key newkey
仅当 newkey 不存在时,将 key 改名为 newkey 。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wBVZtGVm-1597890996517)(狂神说 Redis.assets/image-20200813114228439.png)]
普通的set、get直接略过。
String类似的使用场景:value除了是字符串还可以是数字,用途举例:
- 计数器
- 统计多单位的数量:uid:123666:follow 0
- 粉丝数
- 对象存储缓存
Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)
一个列表最多可以包含 232 – 1 个元素 (4294967295, 每个列表超过40亿个元素)。
首先我们列表,可以经过规则定义将其变为队列、栈、双端队列等
正如图Redis中List是可以进行双端操作的,所以命令也就分为了LXXX和RLLL两类,有时候L也表示List例如LLEN
Redis hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象。
Set就是一种简化的Hash,只变动key,而value使用默认值填充。可以将一个Hash表作为一个对象进行存储,表中存放对象的信息。
应用案例:
- set排序 存储班级成绩表 工资表排序!
- 普通消息,1.重要消息 2.带权重进行判断
- 排行榜应用实现,取Top N测试
四、三种特殊数据类型
使用经纬度定位地理坐标并用一个 有序集合zset保存,所以zset命令也可以使用
有效经纬度
- 有效的经度从-180度到180度。
- 有效的纬度从-85.05112878度到85.05112878度。
指定单位的参数 unit必须是以下单位的其中一个:
- m表示单位为米。
- km表示单位为千米。
- mi表示单位为英里。
- ft表示单位为英尺。
关于GEORADIUS的参数
通过
georadius
就可以完成 附近的人功能
withcoord:带上坐标
withdist:带上距离,单位与半径单位相同
COUNT n : 只显示前n个(按距离递增排序)
如果允许容错,那么一定可以使用Hyperloglog !
如果不允许容错,就使用set或者自己的数据类型即可 !
使用位存储,信息状态只有 0 和 1
Bitmap是一串连续的2进制数字(0或1),每一位所在的位置为偏移(offset),在bitmap上可执行AND,OR,XOR,NOT以及其它位操作。
应用场景
签到统计、状态统计
事务中每条命令都会被序列化,执行过程中按顺序执行,不允许其他命令进行干扰。
* 一次性
* 顺序性
* 排他性
1. Redis事务没有隔离级别的概念
2. Redis单条命令是保证原子性的,但是事务不保证原子性!
* 开启事务( multi
)
* 命令入队
* 执行事务( exec
)
所以事务中的命令在加入时都没有被执行,直到提交时才会开始执行(Exec)一次性完成。
127.0.0.1:6379> multi # 开启事务OK127.0.0.1:6379> set k1 v1 # 命令入队QUEUED127.0.0.1:6379> set k2 v2 # ..QUEUED127.0.0.1:6379> get k1QUEUED127.0.0.1:6379> set k3 v3QUEUED127.0.0.1:6379> keys *QUEUED127.0.0.1:6379> exec # 事务执行1) OK2) OK3) “v1″4) OK5) 1) “k3” 2) “k2” 3) “k1″1234567891011121314151617181920
**取消事务(discurd
)**
127.0.0.1:6379> multiOK127.0.0.1:6379> set k1 v1QUEUED127.0.0.1:6379> set k2 v2QUEUED127.0.0.1:6379> DISCARD # 放弃事务OK127.0.0.1:6379> EXEC (error) ERR EXEC without MULTI # 当前未开启事务127.0.0.1:6379> get k1 # 被放弃事务中命令并未执行(nil)123456789101112
> 代码语法错误(编译时异常)所有的命令都不执行
127.0.0.1:6379> multiOK127.0.0.1:6379> set k1 v1QUEUED127.0.0.1:6379> set k2 v2QUEUED127.0.0.1:6379> error k1 # 这是一条语法错误命令(error) ERR unknown command error
, with args beginning with: k1
, # 会报错但是不影响后续命令入队 127.0.0.1:6379> get k2QUEUED127.0.0.1:6379> EXEC(error) EXECABORT Transaction discarded because of previous errors. # 执行报错127.0.0.1:6379> get k1 (nil) # 其他命令并没有被执行1234567891011121314
> 代码逻辑错误 (运行时异常) **其他命令可以正常执行**>>> 所以不保证事务原子性
127.0.0.1:6379> multiOK127.0.0.1:6379> set k1 v1QUEUED127.0.0.1:6379> set k2 v2QUEUED127.0.0.1:6379> INCR k1 # 这条命令逻辑错误(对字符串进行增量)QUEUED127.0.0.1:6379> get k2QUEUED127.0.0.1:6379> exec1) OK2) OK3) (error) ERR value is not an integer or out of range # 运行时报错4) “v2” # 其他命令正常执行# 虽然中间有一条命令报错了,但是后面的指令依旧正常执行成功了。# 所以说Redis单条指令保证原子性,但是Redis事务不能保证原子性。123456789101112131415161718
**悲观锁:**
* 很悲观,认为什么时候都会出现问题,无论做什么都会加锁
**乐观锁:**
* 很乐观,认为什么时候都不会出现问题,所以不会上锁!更新数据的时候去判断一下,在此期间是否有人修改过这个数据
* 获取version
* 更新的时候比较version
使用 watch key
监控指定数据,相当于乐观锁加锁。
> 正常执行
127.0.0.1:6379> set money 100 # 设置余额:100OK127.0.0.1:6379> set use 0 # 支出使用:0OK127.0.0.1:6379> watch money # 监视money (上锁)OK127.0.0.1:6379> multiOK127.0.0.1:6379> DECRBY money 20QUEUED127.0.0.1:6379> INCRBY use 20QUEUED127.0.0.1:6379> exec # 监视值没有被中途修改,事务正常执行1) (integer) 802) (integer) 20123456789101112131415
> 测试多线程修改值,使用watch可以当做redis的乐观锁操作(相当于getversion)
我们启动另外一个客户端模拟插队线程。
线程1:
127.0.0.1:6379> watch money # money上锁OK127.0.0.1:6379> multiOK127.0.0.1:6379> DECRBY money 20QUEUED127.0.0.1:6379> INCRBY use 20QUEUED127.0.0.1:6379> # 此时事务并没有执行123456789
模拟线程插队,线程2:
127.0.0.1:6379> INCRBY money 500 # 修改了线程一中监视的money(integer) 60012
回到线程1,执行事务:
127.0.0.1:6379> EXEC # 执行之前,另一个线程修改了我们的值,这个时候就会导致事务执行失败(nil) # 没有结果,说明事务执行失败127.0.0.1:6379> get money # 线程2 修改生效”600″127.0.0.1:6379> get use # 线程1事务执行失败,数值没有被修改”0″1234567
> 解锁获取最新值,然后再加锁进行事务。 unwatch
进行解锁。
注意:每次提交执行exec后都会自动释放锁,不管是否成功
## 六、Jedis
使用Java来操作Redis,Jedis是Redis官方推荐使用的Java连接redis的客户端。
* 操作命令 **TestPing.java**
public class TestPing { public static void main(String[] args) { Jedis jedis = new Jedis(“192.168.xx.xxx”, 6379); String response = jedis.ping(); System.out.println(response); // PONG }}1234567
* 断开连接
## 七、SpringBoot整合
org.springframework.boot spring-boot-starter-data-redis1234
springboot 2.x后 ,原来使用的 Jedis 被 lettuce 替换。
> jedis:采用的直连,多个线程操作的话,是不安全的。如果要避免不安全,使用jedis pool连接池!更像BIO模式
lettuce:采用netty,实例可以在多个线程中共享,不存在线程不安全的情况!可以减少线程数据了,更像NIO模式
我们在学习SpringBoot自动配置的原理时,整合一个组件并进行配置一定会有一个自动配置类xxxAutoConfiguration,并且在spring.factories中也一定能找到这个类的完全限定名。Redis也不例外。
那么就一定还存在一个RedisProperties类
之前我们说SpringBoot2.x后默认使用Lettuce来替换Jedis,现在我们就能来验证了。
先看Jedis:
@ConditionalOnClass注解中有两个类是默认不存在的,所以Jedis是无法生效的
然后再看Lettuce:
完美生效。
现在我们回到RedisAutoConfiguratio
只有两个简单的Bean
* **RedisTemplate**
* **StringRedisTemplate*
当看到xxTemplate时可以对比RestTemplat、SqlSessionTemplate,通过使用这些Template来间接操作组件。那么这俩也不会例外。分别用于操作Redis和Redis中的String数据类型。
在RedisTemplate上也有一个条件注解,说明我们是可以对其进行定制化的
说完这些,我们需要知道如何编写配置文件然后连接Redis,就需要阅读RedisProperties
这是一些基本的配置属性。
还有一些连接池相关的配置。注意使用时一定使用Lettuce的连接池。
## 八、自定义Redis工具类
使用RedisTemplate需要频繁调用 .opForxxx
然后才能进行对应的操作,这样使用起来代码效率低下,工作中一般不会这样使用,而是将这些常用的公共API抽取出来封装成为一个工具类,然后直接使用工具类来间接操作Redis,不但效率高并且易用。
工具类参考博客:
## 九、Redis.conf
> 容量单位不区分大小写,G和GB有区别
> 可以使用 include 组合多个配置问题
> 网络配置
> 日志输出级别
> 日志输出文件
> 持久化规则
由于Redis是基于内存的数据库,需要将数据由内存持久化到文件中
持久化方式:
* RDB
* AOF
> RDB文件相关
> 主从复制
> Security模块中进行密码设置
> 客户端连接相关
maxclients 10000 最大客户端数量maxmemory 最大内存限制maxmemory-policy noeviction # 内存达到限制值的处理策略123
redis 中的 **默认**的过期策略是 **volatile-lru**。
**设置方式**
config set maxmemory-policy volatile-lru 1
**1、volatile-lru:**只对设置了过期时间的key进行LRU(默认值)
**2、allkeys-lru :**删除lru算法的key
**3、volatile-random:**随机删除即将过期key
**4、allkeys-random:**随机删除
**5、volatile-ttl :**删除即将过期的
**6、noeviction :**永不过期,返回错误
> AOF相关部分
## 十、持久化—RDB
RDB:Redis Databases
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C0mm1D4A-1597890996524)(狂神说 Redis.assets/image-20200818122236614.png)]
在指定时间间隔后,将内存中的数据集快照写入数据库 ;在恢复时候,直接读取快照文件,进行数据的恢复 ;
默认情况下, Redis 将数据库快照保存在名字为 dump.rdb的二进制文件中。文件名可以在配置文件中进行自定义。
在进行 ** RDB
**的时候, ** redis
**的主线程是不会做 ** io
**操作的,主线程会 ** fork
**一个子线程来完成该操作;
这种工作方式使得 Redis 可以从写时复制(copy-on-write)机制中获益(因为是使用子进程进行写操作,而父进程依然可以接收来自客户端的请求。)
使用 save
命令,会立刻对当前内存中的数据进行持久化 ,但是会阻塞,也就是不接受其他操作了;
> 由于 save
命令是同步命令,会占用Redis的主进程。若Redis数据非常多时, save
命令执行速度会非常慢,阻塞所有客户端的请求。
flushall
命令也会触发持久化 ;
满足配置条件中的触发条件 ;
> 可以通过配置文件对 Redis 进行设置, 让它在" N 秒内数据集至少有 M 个改动"这一条件被满足时, 自动进行数据集保存操作。
bgsave
是异步进行,进行持久化的时候, redis
还可以将继续响应客户端请求 ;
**bgsave和save对比**
**优点:**
**缺点:**
## 十一、持久化AOF
**Append Only File**
将我们所有的命令都记录下来,history,恢复的时候就把这个文件全部再执行一遍
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z8wr9lBW-1597890996525)(狂神说 Redis.assets/image-20200818123711375.png)]
> 以日志的形式来记录每个写的操作,将Redis执行过的所有指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。
快照功能(RDB)并不是非常耐久(durable): 如果 Redis 因为某些原因而造成故障停机, 那么服务器将丢失最近写入、以及未保存到快照中的那些数据。 从 1.1 版本开始, Redis 增加了一种完全耐久的持久化方式: AOF 持久化。
如果要使用AOF,需要修改配置文件:
appendonly no yes
则表示启用AOF
默认是不开启的,我们需要手动配置,然后重启redis,就可以生效了!
如果这个aof文件有错位,这时候redis是启动不起来的,我需要修改这个aof文件
redis给我们提供了一个工具 redis-check-aof --fix
> 优点和缺点
appendonly yes # 默认是不开启aof模式的,默认是使用rdb方式持久化的,在大部分的情况下,rdb完全够用appendfilename “appendonly.aof”# appendfsync always # 每次修改都会sync 消耗性能appendfsync everysec # 每秒执行一次 sync 可能会丢失这一秒的数据# appendfsync no # 不执行 sync ,这时候操作系统自己同步数据,速度最快123456
## 十二、RDB和AOP选择
一般来说, 如果想达到足以媲美 PostgreSQL 的数据安全性, 你应该同时使用两种持久化功能。
如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失, 那么你可以只使用 RDB 持久化。
有很多用户都只使用 AOF 持久化, 但并不推荐这种方式: 因为定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快。
## 十三、Redis发布与订阅
Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IBT2pjCa-1597890996526)(狂神说 Redis.assets/image-20200818162849693.png)]
下图展示了频道 channel1 , 以及订阅这个频道的三个客户端 —— client2 、 client5 和 client1 之间的关系:
当有新消息通过 PUBLISH 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端:
Original: https://www.cnblogs.com/wscsdn/p/16008178.html
Author: 扬帆起航$
Title: Redis
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/580545/
转载文章受原作者版权保护。转载请注明原作者出处!