现在是大数据时代,一般的关系型数据库无法快速处理那么大的访问量。
单机Mysql时代
那时候更多使用静态网页Html,访问量不会很大,单个数据库已经足够。
这样的模式存在什么问题?
数据量如果太大,一台服务器放不下数据的索引(B+Tree),一台服务器也放不下读写混合,一台服务器无法承受Memcached(缓存) + MySQL + 垂直拆分
实际场景80%都是进行查询的操作,每次都去查数据库效率很低。所以我们希望使用缓存减轻数据库压力。
发展历程:优化数据结构和索引——》文件缓存(IO)——》Memcached
分库分表 + 水平拆分 + MySQL集群
以前使用MylSAM : 表锁 查一行数据锁住了整张表 高并发下有严重的问题
Innodb使用行锁解决
MySQL在那个时代推出了表分区,但是使用的不多。
集群在这时已经能满足大部分需求。
大数据时代
2010-2020发生了翻天覆地的变化,各种实时变化的数据。
MySQL等关系型数据库的弊端就暴露了,无法应付这种场景。
为什么要用NoSQL
用户的每天生成的个人信息,社交网络,地理位置等等以PB计,呈爆发式增长。
NoSQL非常适合应对这种场景。
NoSQL = Not Only SQL 不仅仅是SQL 泛指非关系型数据库,随着互联网2.0时代的诞生,传统的数据库很难处理,尤其是超大规模高并发的社区,暴露很多无法解决的问题。
很多的数据类型不需要一个固定格式(行 列)
传统的RDBMS(关系型)
结构化组织SQL数据和表的关系都在单独的表中数据定义语言严格的一致性基础的事务…NoSQL
不仅仅是数据没有固定的查询语言键值对存储,列存储,文档存储,图形数据库最终一致性CAP定理 和 BASE高性能 高可用 高可扩真正在实际环境用的肯定是NoSQL+RDBMS 技术没有之分 只看如何使用
商品名称 价格 商品信息
使用关系型数据库分布式文件系统 FastDFS
淘宝 TFS
GOOLE GFS
Hadoop HDFS
阿里云 oss
秒杀之类的
内存数据库Redis Tair MemacheKV键值对:
新浪:Redis美团:Redis + Tair阿里:Redis + memcache Redis Remove Dictionary Service 远程字典服务
是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、KeyValue数据库,并提供多种语言的API
免费且开源,是当下最热门的NoSQL技术之一,也被人们称为结构化数据库
使用单线程的好处就是不会受到CPU的太大局限,而仅仅是机器的内存和网络带宽的局限。
Redis采用纯内存操作,避免大量访问数据库,减少直接读取磁盘数据,redis将数据存储在内存里面,读写数据的时候不会受到硬盘I/O速度的限制。单线程操作避免了不必要的上下文切换和资源竞争,也不需要上锁解锁,没有因为出现死锁而导致的性能损耗。采用了非阻塞I/O多路复用机制string更适合字符串存储 hash存储对象更直观 更适合存储对象 hash可以只更新一个字段而string不行
append key名 "字符串" #记得要加双引号 拼接key的字符串 如果当前key不存在 则新建 getrange key名 起始index(从0开始) 终止index #截取字符串 setrange key名 起始index 终止index #替换字符串 setnx #set if not exist 不存在才设置 常用于分布式锁 mset key名1 values值1 key名2 values值2 .... #一次设置多个key value mset key名1 values值1 key名2 values值2 .... #一次设置多个key value 如果其中有已存在的key 则本次所有key设置失败 mset user:1:name zhangsan user:1:age 2 #设置对象键值对 mget user:1:name #获取对象键值对 getset key名 值 #获取当前值 替换成新值 若不存在则新建set的值 无序不能重复
sadd set名 值 #往set添加元素 smembers set名 #查看set的所有元素 sismember set名 值 #相当于contains scard set名 #获取set中元素数量 sremove 值 #移除元素 srandmember set名 个数 #随机获取元素 spop set名 #随机弹出元素并输出 sdiff set1名 set2名 #输出set1中set2没有的元素 sinter set1名 set2名 #输出两个set的交集 sunion set1名 set2名 #并集hash存储对象更直观 更适合存储对象 string更适合字符串存储 hash可以只更新一个字段
hset key名 field名 值 #存值 field和值类似键值对 hget key名 field名 #获取值 hmset key名 field1名 值 field2名 值 #存多个值 若存在则覆盖 hgetall key名 #获取所有值 hdel key名 field #删除键值对 hlen key名 #获取当前hash中键值对的个数 hexists key名 field #检查field是否存在 hkeys key名 #获得所有的key hvals key名 #获取所有value hincrby key名 field 步长 #设置自增 可为负数 也可以用decrby hsetnx key名 field名 值 #不存在则设置 hset user:1 name rush #存储user信息 hget user:1 name #获取user信息 两极无法直接添加,一般下载城市数据,通过java一键添加。
geoadd 国家名:city 经度 纬度 Geo名 # 添加地理位置 geopos 国家名:city Geo名 # 获取地理位置 geodist 国家名:city Geo名1 Geo名2 # 获取两个地理位置的距离 geodist 国家名:city Geo名1 Geo名2 km # 获取两个地理位置的距离 以千米表示 georadius 国家名:city 经度 纬度 半径 单位 #找到Geo中在这个范围中的所有元素 georadius 国家名:city 经度 纬度 半径 单位 withdist withcoord count 数量 #找到Geo中在这个范围中的所有元素 附带经纬度 限定数量 georadiusbymember 国家名:city 元素名 半径 单位 #查找元素周围的元素 zrange 国家名:city index index #查看geo元素 zrem 国家名:city geo名 #删除geo元素 Hyperloglog数据结构,基数统计的算法(元素不重复)。
计数如果使用set记录用户id,再获取set中的元素个数,这样就会保存大量不必要知道的用户id信息,这样做效率很低。
Hyperloglog使用的内存是固定的,只需要12k内存
PFadd key名 元素 元素 ... #添加元素 PFcount key名 #获取元素个数 pfmerge key1名 key2名 key3名 #合并key2和key3生成key1位存储
存储是否已经打卡,登录状态等信息
setbit sign名 变量 值 #变量只能为数字 值只能为0或1 getbit sign名 变量 #获取值 bitcount sign名 #获取值为1的个数入队的时候命令还没有执行,直到执行事务(exec)才真正执行了命令
Redis单条命令保证原子性,但Redis事务不保证原子性。事务中的所有命令都会被顺序化,命令会按照顺序执行。命令执行有以下特点。
一次性顺序性排他性Redis事务没有隔离级别的概念,所以不存在幻读、脏读、不可重复读的情况
multi #开启事务 exec #执行事务 discard #放弃事务编译型异常
事务中的所有命令都不会执行执行时异常
除了异常命令,其他命令都可以正常执行乐观锁
认为什么时候都不会出问题,不会上锁,更新数据的时候判断一下在此期间是否修改过这个数据。会获取并比对该数据的version,version不一致则不进行操作
悲观锁
认为什么时候都会出问题,都会上锁。
watch相当于乐观锁
multi #开启事务 watch 变量名 #给变量上乐观锁 unwatch #解锁 exec #执行事务exec和discard会自动解锁
使用Java操作redis,是Redis官方推荐的java连接开发工具。
在SpringBoot2.x.x版本中,底层使用的是Jedis。在SpringBoot3之后替换成了Lettuce
Jedis采用直连的方式,线程不安全。如果想要避免这个问题,需要使用Jedis Pool,采用阻塞的方式,有性能损耗。类似Bio模式Lettuce是用netty做的,实例可以在多个线程共享,不存在线程不安全的情况。类似Nio模式在指定的时间间隔内对你的数据进行快照存储
配置
appendonly no # 默认的 不开启aofm模式的,默认使用rbd方式持久化 appendfilename "appendonly.aof" #持久化文件的名字 # appendfsync always 每次修改都会sync 消耗性能 appendfsync everysec #每秒sync一次 可能会丢失这一秒的数据 # appendsync no 不执行sync 性能最佳 每当进行了操作,就往AOF文件添加记录,这样就不会丢失数据。重启后,Redis会自动加载AOF回载数据。
AOF可以做到每一次操作都会保存,数据不会丢失
AOF文件的大小远远大于RDB文件,修复的速度也比rdb慢
80%的情况都是进行读取的操作,所以将读写分离,可以有效地提高我们的效率。
必须使用集群
上面这样配置只能起到暂时的作用,如果想要永久生效则需要配置配置文件,修改主机的replicaof信息
复制三个配置文件,然后修改对应信息。
改端口pid名字log文件名字dump.rdb名字默认情况下,每一台机器都是主机。
主机可以写,从机不能写。
如果使用命令行配置,重启后就会变回主机
成为从机会立即复制主机的数据
全量复制:slave服务在接收到数据库文件数据后,将其存盘并加载到内存中增量复制:Master继续将新的所有收集到的修改命令依次传给slave,完成同步 6379作为主机,6380作为6379的从机,作为6381的主机,6379宕机后6380可以正常写入,若无宕机则不能写入。
slaveof no one #让自己变成主机新建配置文件 sentinel.conf
sentinel monitor redis名 ip 端口 1 # 1代表主机挂了启动哨兵
redis-sentinel sentinel.conf
优点
基于主从复制,保留了主从复制的优点系统的稳定性、健壮性更佳自动选出新主机,实时性更好缺点
在线扩容并不方便配置很麻烦 用户查的数据在redis中不存在,即缓存没有命中,于是向持久层数据库查询,当很多用户的查询,缓存都没有命中,就会给持久层数据库很大的压力,黑客故意查询redis中不存在的数据,导致持久层宕机,这就是缓存穿透。
和穿透不同的是,击穿特指缓存的某个点被大量访问而被击破,并且数据是查得到的。
到了某个时间,缓存集体失效。导致持久层数据库宕机