nosql简介
主要作用:降低io读,串行+单线程+多路io复用技术
nosql不依赖业务逻辑方式存储,而以简单的key-value形式存储。大大增加了数据库的扩展能力
文章目录
简单命令
- set key value
- keys *
- exists key
- type key
- del key
- unlink//非异步阻塞
- expire key seconds
- ttl key//-1永不过期,-2已经过期
- select 5
- dasize
- flushdb
- flushall
常用数据类型
String
String的数据结构为简单动态字符串。是可以修改的字符串,内部结构有点类似于java的arrayList,采用预分配冗余空间的方式来减少内存的频繁分配。他是二进制安全的,意味着String包含任何数据,比如图片或者序列化对象
扩容方式:实际分配空间一般高于字符串长度,当字符串小于1M时,扩容都是现有空间加倍扩容。超过1M每次扩容1M,最高512M
常用命令
- set key value
- get key
- append key
- strlen key
- setnx key value
- incr key//具有原子性:不会被线程调度机制打断的操作叫原子操作
- 这种操作一旦开始,就一直运行到结束,中间不会有任何context switch(切换到另一个线程)
1.在单线程中,能够在单条指令中完成的操作都可以认为是原子操作,因为终端命令只发生在命令之间。
2.在多线程中,不能被其他线程(进程)打断的操作就叫原子操作
redis单命令的原子性操作得益于他的单线程
- 这种操作一旦开始,就一直运行到结束,中间不会有任何context switch(切换到另一个线程)
- decr key
- incrby key 步长
- decrby key 步长
- mget//同时设置一个或多个key-value
- mset//同时获取一个或多个key-value
- msetnx//具有原子性,要么全成功或全失败
- getrange key <起始范围> <结束范围>
- serrange key <起始范围> <结束范围>
- setex key <过期时间>
- getset key value
List
redis列表是简单的字符串列表,按照插入顺序排序。他的底层实际是个双向链表,对双端的操作性能很高,通过索引下标的操作中间节点性能会差
list的数据结构为快速链表quickList。首先在列表元素较少的情况下会使用一块连续的内存存储,这个结构是ziplist,即压缩列表,
它将所有的元素紧挨这一起储存,分配的是一块连续的内存,当数据量比较多的时候才会改为quicklist。因为普通链表需要的附加指针空间太大,会比较浪费空间。比如这个列表里只存int的类型的数据,结构上还需要两个额外的指针prev和next
redis将链表和ziplist结合起来组成了quicklist。也就是将多个ziplist使用双向指针串起来使用。这样既满足了快速的插入删除性能,又不会出现太多的空间冗余
常用命令
- lpush//从左插入一到多个值
- rpush//从右插入一到多个值
- lpop key//从左吐出一个值,吐完键也删除
- rpop key//从右吐出一个值
- rpoplpush key1 key2//从key1左边取一个值放到key2左边
- lrange key <起始范围> <结束范围>//-1取所有
- lindex key index/按索引下标获取指定位置元素(从左到右,0开始)
- llen key
- linsert key before value newvalue//在value后插入newvalue
- linsert key after value newvalue//在value前插入newvalue
- lrem key n value//从左边删除n个value元素
- lset key index value//替换
Set
Set功能类似与list,特殊之处是可以去重。Set是String类型的无序集合,他底层其实是一个value为null的hash表,所以添加,删除,查找的复杂度都是O(1)。
常用命令
- sadd key value1 value2
- smember key
- sismember key value//是否包含value
- scard key//返回value中的个数
- srem key value1 value2//移除key中的value1 value2
- spop key//随机从key中吐出一个值
- srandmember key n//随机取出n个值,不从集合中删除
- smove key1 key2 value//从key1中取出value移动到key2
- sinter key1 key2//返回两个集合中的交集数据
- sunion key1 key2//返回两个集合中的并集数据
- sdiff key1 key2//返回两个集合中的差集元素(只包含key1,不包含key2的)
Hash
hash是一个键值对集合。
redis hash是一个String类型的field和value的映射表,hash特别适合储存对象。类似于java中的map
hash类型对应的数据结构是两种:ziplist,hashtable。当数据少时,使用ziplist,多是使用hashtable
常用命令
- hset key field value
- hget key field
- hmset key field1 value field2 value2…
- hexists key field
- hkeys key//列出所有field
- hval key//列出所有value
- hincrby key field increment//field的值增加increment
- hsetnx key field value//是否成功添加field value的键值对,field不可重复
Zset
有序集合zset和普通set非常相似,是个没有重复元素的有序字符串集合
不同之处是有序集合的每一个成员都关联了一个评分(score),这个评分被用来按照从最低到最高的方式排序集合中的元素。集合中的元素是唯一的,但是评分可以重复。
因为元素有序,所以你可以很快的根据评分或者次序来获取一个范围的元素
访问有序集合的元素也是非常快的,因此你能够使用有序集合作为一个没有重复成员的智能列表
sortedSet是Redis提供的非常特殊的一个数据结构,一方面它等价于java中的map<String,Double>,可以给每一个元素赋予一个权重score,另外一方面他又类似于TreesSet,内部的元素会按照权重score进行排序,可以得到每个元素的名次,还可以通过score的范围获取元素的列表。
zset底层使用了两个数据结构
1.hash,hash的作用就是关联了value和score,保障了value的唯一性,还可以通过value找到对应的score
2.跳跃表,跳跃表的目的在于给元素value排序,根据score的范围获取元素
常用方法
- zadd key score1 value1 score2 value2
- zrange key start end [WHTHSCORES]
- zrangebyscore key min max [WITHSCORES]
- zrevrangebyscore key max min [WITHSCORES]
- zincrby key increment value
- zrem key value
- zcount key min max
- zrank key value 返回该值在集合中的排名(0开始)
事务和锁机制
事务简介
redis事务是一个单独的隔离操作:事务中的所有命令都会被序列化,按顺序执行。事务在执行过程中不会被打断
redis事务的主要作用就是串联多个命令防止别的命令插队
命令
- multi//组队阶段
- discard//放弃组队
- exec//执行阶段
锁
悲观锁:每次操作前先上锁,中间别人使用只能等你用完才能用。这种做法同一时间只能一人操作,效率较低
乐观锁:每次操作不上锁,再提交时判断下别人有没有提交,可以使用版本号时间戳等。乐观锁适用于多读应用,这样可以提高吞吐量,
命令
- watch key…:事务开启之前使用watch命令监视多个key,key有变化事务提交失败
- unwatch:取消所有key的监视
数据持久化
RDB
在指定的时间间隔内将内存中的数据集快照写入磁盘,恢复时是将快照文件读到内存中
dump.rdb//打开配置文件save 3600
配置
- save:手动保存,只做保存,其余全阻塞
- bgsave:redis会在后台做异步快照,同时还正常响应请求
- stop-writes-on-bgsave-error:磁盘满时关闭写操作,推荐yes
- rdbcompression:是否压缩
备份
redis单独创建一个子进程来进行持久化,会先将数据写入到一个临时文件中,等持久化文件都结束了,再将这个再将这个临时文件替换持久化文件,整个过程中,主进程是不需要任何IO操作的,这就确保了极高的性能,如果需要大规模的数据恢复,且对于数据的完整性不是非常敏感,那RDB方式要比AOF方式更加高效,RDB的缺点是最后一次持久化文件可能丢失
AOF
aof是以日志的形式来记录每个写操作(增量保存),将redis执行过的所有写指令记录下来,只需追加文件但不许改文件,redis启动之初会读取该文件重新构建数据
异常修复
1.修改默认配置appendonly no为yes
2.如果遇到文件损坏,可以通过reids-check-aof–fixappendonly.aof进行修复
3.备份被写坏的aof文件
4.恢复:重启redis,然后重新加载
优略势
备份机制更稳健,丢失数据概率更低
占用更多磁盘空间,恢复备份速度更慢,每次读写都同步,性能还有一定压力
两个同时开启默认读取aof
使用建议
官方建议都是用,如果对数据不敏感,可单独选用rdb
不建议单独使用aof,因为可能会出现bug
如果只是做缓存,可以都不使用
应用问题解决
缓存穿透
当用户查询一个数据时,redis中没有命中,直接访问数据库,这样的查询变多的时候,会给数据库造成高IO的压力,这就是缓存穿透。
还有一种情况就是用户造假数据,redis中根本没有,在访问数据库,数据库中也没有,本次查询失败,访问增高时会造成服务器瘫痪
解决方案
- 对空值缓存:如果一个查询返回空值,直接在redis中对空结果进行缓存,设置一个较短的过期时间,比如5分钟
- 设置白名单:使用bitmaps类型定义一个可以访问的白名单,名单id作为bitmaos的偏移量,每次访问和bitmaps里面的id进行比较,如果访问id不在bitmaps里面,进行拦截,不允许访问
- 采用布隆过滤器:它是一个很长的二进制向量(位图)和一系列随机映射函数(哈希函数)
- 实时监控:如果发现redis命中低急速降低,配合运维设置白名单
缓存击穿
redis中的某个key过期了,大量访问这个key,或者包含这个key
数据库访问压力瞬间增加
redis没有出现大量的key过期
redis运行正常,数据库崩溃
解决方案
- 预先设置热门数据:在redis访问高峰前,先给热门数据提前存入到redis中,并加时长
- 实时调整:现场监控热门数据,实时调整key的过期时长
雪崩
在极少时间段,大量key过期
解决方案
- 构建多级缓存架构:nginx缓存+redis缓存+ehcache缓存等
- 将缓存失效时间分散开:我们可以在原有失效时间上加一个随机值,比如1-5分钟,这样很难集体失效