Redis

NoSql特点

1、方便扩展(数据之间没关系)

2、大数据量高性能(单线程,一秒写8万次,读取11万次)

3、数据类型是多样型的(不需要事先设计数据库,随取随用)

redis hmset覆盖问题_数据库

4、关系型数据库(RDBMS)和Nosql

关系型数据库
  数据和关系都存在在单独的表中
  操作,数据定义语言
  严格的一致性
Nosql
  不仅仅是数据
  没有固定的查询语言
  键值对存储,列存储(HBASE)、文档存储,图数据库(社交关系)
  最终一致性
  CAP定理和BASE(异地多活)
  高性能,高可用,高可扩展性

阿里云的这群疯子

分布式文件系统FastDFS

搜索引擎Elasticsearch,淘宝Isearch:多隆

redis hmset覆盖问题_数据库_02

NoSQL四大分类

KV 键值对

文档型数据库(bson和JSON一样):MongoDB基于分布式文件存储的数据库,是NOSQL和关系型数据库中间的产品

列存储数据库:HBase、分布式文件系统

图关系数据库:存放的是关系

redis hmset覆盖问题_Redis_03

Redis

远程字典服务,C语言写的,支持网络,可持久化的key-value数据库。

可以干嘛

1、内存存储,持久化(没有持久化,数据断电即失去)

2、效率高,可用于高速缓存

3、地图信息分析

4、计时器、计数器

5、解决Session共享问题

默认端口号:6379

默认16个数据库,默认使用第0个,可以使用select选择数据库,select 3

不区分大小写

安装homebrew:/bin/zsh -c “$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)”

启动Redis服务,redis-server /opt/homebrew/etc/redis.conf

redis-benchmark压力测试 工具

redis-benchmark -h localhost -p 6379 -c 100 -n 100000

redis是单线程的,处理请求是单线程的,持久化的时候,多线程,不是所有的操作都是单线程

Redis是基于内存操作,CPU不是Redis性能瓶颈,Redis的瓶颈是根据机器的内存和网络带宽,可以使用单线程来实现,就使用单线程了

Redis为什么单线程这么快

redis是将所有数据放在内存中的,单线程操作效率是最高的,多线程CPU会上下文切换,耗时,对于内存系统来说,没有上下文切换效率是最高的

可以给Redis设置密码

//在控制台设置密码
config get requirepass      // 获取密码
config set requirepass      //设置密码
auth pwd                   //登录时输入密码命令

五大数据类型

Strings(字符串set get)
切换数据库:select 3
设置KV:set key value
获取值:get key
获取所有key:keys *
清空当前数据库:flushdb
清空所有库:flushall
判断是否存在key: exists key
删除key:move key index   (index为当前数据库)
  
设置key过期,过期后自动删除:expire key time  (time 是秒 )
查看key剩余时间:ttl key
查看key类型:type key
动态修改value:append key "str" (给之前的key对应的value值追加)
获取当前key对应value的长度:strlen key
  
value自增:incr  key
value自减:decr key
value 增加指定增长量:incrby key 10
value 减少指定增长量:decrby key 20
 
截取字符串:getrange key start end (例:getrange age 0,2)闭区间 
替换: setrange key start xx (从1开始替换)
  
设置过期时间:setex(set with expire)  setex key time value //设置key的值为value,time时间后过期
不存在设置:setnx(set if not exist) setnx key value  //不存在的时候新建k-v,存在的时候覆盖不了,  //分布式锁中会常用
  
批量设置k-v: mset k1 v1 k2 v2 k3 v3
批量获取value: mget k1 k2
批量不存在创建:msetnx k1 v1 k4 v4   //会失败,原子性操作,一个失败,就都失败,k1已存在,失败
  
//对象
 set user:1:{name:zhangsan,age:3}设置一个user:1对象
 mset user:1:name zhangsan user:1:age 2
 mget user:1:name user:1:age
 
 先get再set:getset key value

计数器,统计多单位数量 ,setnx可以做分布式锁

hashes(散列set get hdel)

Key-Map,跟String差不多

hset myhash key value    //插入值
hget myhash key          //取值
hmset myhash k1 v1  k2 v2   //批量设置key-value
hmget myhash k1 k2      //批量取值
hgetall myhash      //获取全部的数据,展示形式:key 和value按顺序单个显示
hdel myhash k1   //删除k1
hlen myhash      //取数量
hexists myhash k1  //是否key存在
hkeys myhash     //所有的key
hvals myhash   //获得所有的value

  
hincrby myhash k1 count  //给k1增加1
hdecrby myhash k1 count  //给k1减1
  
hsetnx myhash k1 value   //如果不存在,新建,存在,就没效果

存一些变更的信息,尤其是用户信息,经常变动的信息,更适合对象的存储

lists(列表push pop)

同JAVA,可以设置成栈、队列、阻塞队列(有序可重复)

//设置值
LPush list value                   // 从左侧放值
LRange list start end         //取值,从开始到结束,闭区间,是放进去顺序的倒序

RPUSH list value       //从右侧放值
LRange list 0 -1       //插入顺序和显示顺序一致,查看所有值

//弹出值
 LPop list            //从左边弹出值
 RPop list            //从右侧弹出值
  
 //取下标
 Lindex  list index    //从左侧取第index个值,从0开始
 
 //取长度
  Llen list
  
  //移除指定的值
  lrem list count value   //移除指定个数的值,lrem list 2 one
  
//截取指定长度,即list被删除了
  Ltrim mylist 1,2    //下标在[1,2]之间以外的都删除,Lrange是只显示,不删除
  
 //先把元素从一个list弹出,再将其移动到新的列表中
  rpoppush mylist myotherlist
  
  exists list //是否存在list
  
  lset list index othervalue //如果list的下标为index存在,会覆盖值为othervalue,否则会报错,提示不存在
  
  Linsert list before/after "value" "newValue" //给value的前面/后面加newvalue的值

redis hmset覆盖问题_redis hmset覆盖问题_04

Sets(集合add、srem)

set中的值不能重复(无序不重复)

sadd myset "hello"   //插入值
smembers myset      //列出元素
sismember myset value  //是否存在元素value
scard myset            //获取set中元素个数
srem myset value      //移除元素
srandmember myset         //随机取元素
spop myset         //随机弹出元素

smove myset myset2 "value"  //将myset中的value移动到myset2中

 //微博、b站共同关注(取交集)
sdiff set1 set2  //set1-set2,取set1不同于set2的元素
sinter set1 set2  //取set1和set2相同的元素,交集
sunion key1 key2  //取并集

共同关注、二度好友、好友推荐

ZSet(有序集合add rem)

在set 基础上增加了一个值,可以进行分组

zadd myset score value   //添加,中间的1可以是一类,或者倒序正序排 根据业务来
zadd myset 2 tow 3 three   //批量添加
  
//排序
zRangeByScore myset -inf +inf  //按中间值的,从小到大排序,inf是无穷,输出的都是value
zRangeByScore myset -inf +inf withscores  //作用如上,同时输出value和score
zRangeByScore myset min max   //升序排序,从最小到最大
zRevRangeByScore myset 0 -1       //降序,饭转
  
zrange myset 0 -1   //输出所有元素
zrem mysey value    //删除指定元素
zcard myset        //获取有序集合中的个数
  
zcount myset score1 score2  //获取[score1,score2]中间的值的个数

对set进行排序,存储班级成绩表,工资表排序(注意,可能值一样,就不行了)

加权判断、排行榜、获取TopN

三大特殊类型
geospatial地理位置

朋友的定位,附近的人,打车距离,方圆几里的人,索引半径查询

geoadd key value(经度 纬度 城市) //添加,两级无法添加,一般通过JAVA程序一次性导入

geopos key 城市1 城市2   //取所在城市的经纬度

geodist key 城市1 城市2 m/km/mi/ft   //获取两个城市之间的距离,后面的是单位 米/千米/英里/英尺
  
georadius key 经度  纬度  半径  m/km/mi/ft   //获取附近所有人的地址,定位,通过半径来查询
  
georadius key 经度  纬度  半径  m/km/mi/ft  withdist  //获取附近的半径人,附带距离

georadius key 经度  纬度  半径  m/km/mi/ft  withdist withcoord  count 2  //获取附近的半径人,附带距离,经度、纬度 限制两人
  
geoRadiusByMember key 城市1 半径 m/km/mi/ft  //找出指定元素周围的其他元素 根据城市,及半径找出附近的城市

Geo底层原理是ZSet,有序集合,可以使用zset的查看元素,移除元素

Hyperloglog

网页访问数(一个人访问网站多次,但是还算做一个人)

传统方式,set保存ID,占据空间大。目的是计数,用hyperloglog,只占12KB的内存

pfadd key a b c d e f g h i j   // 创建
pfadd key2 i j a b c m n        
pfcount key         //计数
pfmerge mykey3  key key2   //合并key 和key2 作为mykey3,并集 会将key和key2中的合并,并去重,相当于set
//查看mykey3的数量,就可以查看访问量,需要允许容错,不允许容错,用set或其他类型统计
Bitmaps

位存储

统计用户信息,活跃,不活跃;登录,未登录,365天打卡,两个状态的,都可以使用bitmaps,用0和1存储

setbit key 2 0 //例如,周一到周日用0-6表示,后面是打卡状态
getbit key 2   //获取周三是否打卡
bitcount key   //统计这周打卡记录

布隆过滤器,利用很长的二进制向量和随机映射函数。

事务

Redis事务本质:一组命令的集合!

一个事务中的所有命令都会被序列化,在事务执行过成功,会按照顺序执行!

具有一次性,排序行,排他性

Redis单条命令是保存原子性的,但是事务不保持原子性

原子性:要么同时成功,要么同时失败

Redis事务没有隔离级别的概念(幻读、脏读、不可重复读)单线程的

所有的命令在事务中,并没有被执行,只有发起执行命令的时候才会执行。

Redis的事务

1、开启事务(multi)

2、命令入队

3、执行事务(exec)

放弃事务discard,事务队列中的命令不会执行

编译型异常(语法错误代码有问题!命令有错),事务中所有命令都不会被执行

redis hmset覆盖问题_数据库_05

运行时异常(1/0),如果事务队列中存在语法性错误,那么执行命令的时候,其他命令是可以正常执行的,错误命令抛出异常!

redis hmset覆盖问题_redis hmset覆盖问题_06

监控 ! watch 可以做乐观锁(用于秒杀)

悲观锁:什么时候都会出现问题,无论做什么都会加锁。synchronized

乐观锁:认为什么时候都不会出现问题,所以不会上锁。更新数据的时候判断,获取version ,比较version 在此期间是否有人改过这个数据。

watch key,然后进行事务,不exec,另外一个线程修改了key的值,此时exec执行失败,需要unwatch解锁,再监视

SpringBoot整合

在SpringBoot2.x之后,Jedis换成了lettuce,因为Jedis是直连的,多线程操作不安全,需要使用Jedis pool连接池。

Lettuce:实例可以在多线程中共享,不存在线程不安全的情况,可以减少线程数据

序列化是为了可以通过网络传输对象,JAVA实体类,继承Serializable接口

Redis持久化

有两种模式aof 和rdb,在大部分情况下,rdb完全够用

RDB(Redis DataBase)

redis.conf中有配置

Redis会单独创建一个子进程持久化,在指定的时间内,将内存中的数据写到一个临时文件中(快照SnapShot),持久化过程结束后,将临时文件替换上次持久化后的文件。整个过程中,主进程不进行任何IO操作。进行大规模的数据恢复,RDB比AOF更高效,缺点是RDB在最后一个持久化后数据可能丢失。

save 900 1      ##900s内至少一个key修改了,就进行持久化操作
save 300 10     ##300s内至少10个key修改,持久化
save 60 10000   ##60s内至少10000个key修改,持久化

stop-writes-on-bgsave-error  yes  ##创建快照需要几秒,中间主进程还在接收客户端数据,在创建快照的过程中,磁盘满了或者宕机出错了,此时停止写入工作 
rdbcompression yes   ##是否压缩rdb文件,需要消耗一些cpu资源
rdbchecksum yes      ##保存rdb文件时,进行错误的检查校验
dir  path            ##rdb文件保存目录

保存的文件叫dump.rdb

触发机制

1、save规则满足

2、执行flushall

3、退出redis,shutdown

如何恢复rdb文件

将rdb文件放在redis启动目录下就可以

save和bgSave

相当于同步和异步,save的时候,将所有数据快照以rdb文件保存到硬盘上,会阻塞主进程,直到保存完成,主进程不会处理客户端的请求

bgsave的时候,会fork一个子线程,将数据保存到硬盘上,主进程继续接收客户端请求。

优点和缺点

##优点

在大量数据恢复时,性能比AOF好
##缺点
 每隔一段时间出发持久化,数据安全性低
AOF(Append Only File)

Append Only Mode将所有命令都记录下来,恢复的时候将这个文件执行一遍。

以日志的形式记录每个写操作(不记录读操作),只追加文件,不改写文件,Redis启动时,会读取该文件重新构建数据

保存的文件叫appendonly.aof

appendonly no   ##默认不开启
appendfilename "appendonly.aof"  ##保存AOF的文件名称

appendfsync always  ##每一次都球盖
appendfsync everysec  ##每一秒都修改
appendfsync no  ##从不修改

no-appendfsync-on-rewrite no    ##重写
auto-aof-rewrite-percentage 100  ##
auto-aof-rewrite-min-size 64mb   ##aof文件大于65M,fork一个新的进程将文件重写,不影响主进程处理命令请求

##重写的时候,fork一个子进程,重写好后给父进程发送一个完成信号,父进程接收到后,会调用信号处理函数:
1、将AOF重新缓存中的内容全部写入到新的AOF文件中,新的AOF文件保存的数据库状态和服务器当前数据库状态一致
2、对新的AOF文件进行改名,新的AOF文件覆盖原有的AOF文件,完成新旧两个AOF文件的替换

触发机制

重启的时候

同时开启两种持久化方式,AOF比RDB优先级更高,通常情况下AOF文件保存的数据集比RDB完整性更高

Redis发布订阅

消息发送者 》频道》消息订阅者

subscribe channel ##订阅一个频道,自动监听的
publish channel msg  ##发送信息到一个频道
主从复制

将主节点的数据复制到从节点,Master以写为主,Slave以读为主。80%都在 进行读操作,减缓服务器的压力

主从复制作用:

1、数据冗余:实现了数据的热备份,是持久化之外的一种数据冗余方式

2、故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余

3、负载均衡:在主从复制基础上,配合读写分离,主节点写,从节点读,分担服务器负载,可以大大提高Redis服务器的并发量

4、高可用(集群)基石:主从复制还是哨兵和集群能够实施的基础

单台Redis最大使用内存不应超过20G

每一台Redis服务器都默认自己为主节点,需要配置从机

##命令配置
info replacation  ##查看主机丛机配置信息

slaveof host port  ##认主机

slaveof no one ##没有主机,如果主机宕机了,可以自己设置为主机


##conf文件配置
replicaof masterHost masterPort  ##在从机上配置

主机中的所有信息都会被从机复制 ,Redis自己底层的

主机断掉了,从机依旧只有读,没有写。主机重新连接,还是主机

如果从机断掉了,就读不到主机的数据了,重新设置主机,会读到

复制原理

从机连到主机,master将传送整个数据文件到slave(全量复制),Master继续将新的修改命令依次传给slave(增量复制)

哨兵模式

在主机断了之后,谁为主机,Redis2.8后自动换主机

哨兵模式(sentinel):是一个独立的进程 ,通过发送命令,等待Redis服务器响应,监控运行的多个Redis实例

redis hmset覆盖问题_redis hmset覆盖问题_07

一个哨兵可能会出现问题,需要多个哨兵进行监控,各个哨兵之间还会进行监控,形成多哨兵模式。

配置哨兵配置文件sentinel.conf

sentinel monitor myredis host port quorum   ##被监控的名称 ,1是至少多少个哨兵挂了,才能使主机下线,故障转移failover

启动哨兵

redis-sentinel sentinel.conf

主机断开后,哨兵会投票选一个从机为主机,主机恢复后,会将原来的主机设置为从机

优点

1、哨兵集群,基于主从复制模式,是主从模式的升级,手动到自动,更加健壮

2、主从可以切换,故障可以转移,系统的可用性更好

缺点

1、Redis不好在线扩容,集群容量一旦达到上限,在线扩容十分麻烦

2、实现哨兵模式很麻烦,上述conf配置,只是其中核心的

Redis缓存穿透和雪崩(需细化)
缓存穿透(查不到)

用户查数据,缓存没有,就会向MySQL等数据库查,发现也没数据,查询失败。如果用户很多,缓存没有,就会都请求数据库,数据库有很大的压力,出现了缓存穿透。

解决方案

布隆过滤器

说存在不一定存在,说不存在一定不存在

缓存击穿(值有,但是缓存过期 访问量太大)

一个热点数据,在不停的扛着大并发,大并发集中对着一个点访问,这个缓存过期,持续的大并发就击破缓存,直接请求数据库,并回写缓存,导致数据库瞬间压力过大。例如微博服务器宕机

解决方案

1、设置热点数据永不过期

2、加互斥锁

缓存雪崩

在某一个时间段内,缓存集中过期失效。Redis宕机

解决方案