likes
comments
collection
share

redis入门终极简略版

作者站长头像
站长
· 阅读数 12

Nosql概念

Nosql :Not Only sql

表示并不只是sql,是一种非关系型数据库,通常同Key-value的方式存储

比如很多地理位置信息,它并不适合用Mysql来存储

Nosql特点

  • 方便扩展
  • 大数据量,高性能:1s写八万次,读十一万次
  • 数据类型多样
  • 不需要设计数据库

大数据时代的3V+3高:

数据海量、多样、快速

服务高并发、高性能、高可扩

其他的数据库类型

文档型数据库:MangoDB

图片型(对象型文件系统):FastDFS,OSS

Nosql四大分类

redis入门终极简略版

redis

概念

redis是一种远程字典服务,用c语言编写,使用k-v存储

linux下安装

在官网下载压缩包,使用tar命令解压在/opt文件下,然后安装c/c++环境:yum install gcc-c++,成功后使用make命令等待完成后再使用make install命令即可。

redis默认安装文件路径是 /usr/local/bin

由于redis启动默认不是后台启动,我们需要修改配置文件:在redis.conf文件中daemonize配置为yes

最后启动服务可以指定配置文件启动:redis-server addr-config

查看进程是否启动 ps -ef | grep redis

关闭服务:在redis命令行中使用 shutdown

基础知识

redis默认有16个数据库,使用第0个数据库

一些命令

  • 使用select切换数据库:select [number]
  • 查看数据库大小:DBSIZE
  • 查看库内容:keys *
  • redis是单线程的,因为CPU不是它的性能瓶颈,而内存和网络带宽才是,所以单线程也能实现高性能
  • 判断某个值存在:exists key
  • 存储数据:set key value
  • 数据限时:expire key number\setex key number value限时多少秒后数据失效,ttl key查询还有多久截止
  • 查看key类型:type key
  • 删除数据库:flushall or flushdb DbName
  • 批量插入、获取:mset k1 v1 k2 v2...\mget k1 k2 k3...
    • msetnx当批量插入的时候其中有存在的就失败,原子性的操作

redis为什么这么快

五大基本类型

  • String:

    • 拼接key的value和value,并返回新value的长度:append keyName value
    • 获取字符串长度:strlen key
    • 对数字内容String自增、自减:incr keydecr key
    • 任意步长自增:incrby key number
    • 截取子串:getrange key start end(截取后包括start end所在的值,end为-1则获取所有项,java则不会包含end)
    • 替换子串:setrange key offset value(从offset开始将value替换后面的内容)
    • 如果存在则:setnx key value
    • 插入一个json字符串:set user:1 {name:JayChou,id:1}
  • List:所有的List命令都是l开头

    • 插入:lpush listName value(头插法)\rpush listName value(尾插法)}
    • 获取:lrange listName start endlindex listName index
    • 移除:lpop\rpop:头删\尾删lrem listName number vlue:移除特定集合的特定值的number个
    • 长度:llen listName
    • 集合子集:ltrim listName start end
    • 移动元素到新集合:rpoplpush oldlist newlist:尾删头插
    • 替换:lset listName index newValue,将list中index位置的值替换为newValue
    • 插入:linsert list after|before value1 value2在value1前|后插入value2
  • Set:无序集合

    • 添加:sadd key value
    • 查看所有:smembers setName
    • 访问大小:scard setName
    • 移除:srem setName keys
    • 随机抽取元素:srandmember setName [number]:随机抽取number个元素
    • 随机移除:spop setName
    • 移动元素:smove setName newSet member
    • 差、并、交集:sdiff\sinter\sunion set1 set2
  • hash

    • hashset
      • 添加:hset\hsetnx\hmset name field value
      • 获取:hget\hmget\hgetall\hkeys\hvals
      • 删除:hdel set field
      • 计算长度:hlen set
      • 自增任意数:hincrby set field number
  • Zset:有序集合,按照score排序

    • 添加:zadd key scores values
    • 查看:zrange key start endzrangebyscore set min max [withscores] [limit offset count:offset表示元素下标,count表示展示多少]zrevrange key start end和zrange是反序的
    • 移除:zrem zset value
    • 访问大小:zcard zsetzcount zset min max:指定范围内元素个数

特殊数据类型

  • Geo:地理位置,用Zset实现的,所以Zset的命令也能对Geo生效

    • 添加:geoadd key latitude pair value:latitude pair纬度对
      • 有效的经度从-180度到180度。 有效的纬度从-85.05112878度到85.05112878度
    • 获取元素:geopos key value:返回latitude pair
    • 计算两个元素的距离:geodist key value1 value2,返回单位千米
    • 筛选元素:GEORADIUS key longitude latitude radius m|km|ft|mi [WITHcooRD][WITHDIST] [WITHHASH][count [ANY]][ASC|DESC],在圆心为latitude pair半径为radius内的所有元素value,count限制数量 georadiusbymember key ....,将某个元素作为中心,半径为radius
    • 元素hash:geohash key value:返回11位hash值
  • hyperloglog:基数统计(集合的基数),可用于网站访问量统计

    • 添加:pfadd key values
    • 访问大小:pfcount key
    • 集合合并:pfmege key3 key1 key2,将两个集合合并在key3里,没有重复元素
  • BitMaps:位存储,如打卡记录就能用它存储

    • 插入:setbit key index value
    • 查询:getbit key index

事务

redis命令保证了原子性,但是事务不保证原子性,因为redis的事务是一组命令的集合,单个命令有原子性,一组命令没有原子性。redis事务没有隔离级别概念,当发起执行的时候才会执行命令

基本

事务操作分为三步:

  • 开启事务
    • multi命令开启事务
  • 放入命令
  • 执行事务
    • exec执行事务
  • 取消事务:
    • discard

执事务中可能出现的异常:

  • 编译型异常

    • 命令符合格式,但是语法错误,导致事务中的命令都不会执行
    • 如:zadd set f ff
  • 运行时异常

    • 只会影响该条命令,对其他不影响

乐观锁

  • watch key :对key加上乐观锁
  • unwatch:放弃监视

当加上乐观锁后,另一个线程对这个key进行修改,会造成监控key的线程的操作失败。

并且失效的场景只会在事务中执行对key的操作命令生效,发生失败后,watch功能失效。

也就是说watch撤销的情况只有两种:

  • 一是 unwatch主动撤销
  • 二是在事务中引发了版本修改

持久化

持久化主要有两种方式:

  • RDB:Redis DataDase
    • 默认的保存文件格式是dump.rdb,保存数据需要配置持久化策略
    • 在配置文件中会有指定保存方式,指定间隔时间,当进行文件持久化的时候,需要将文件快照写入磁盘, Redis会单独创建一个子进程,将数据写入临时文件中,持久化完成后,将这个文件替换上次持久化的临时文件, 主进程不会进行io。save 100 10:如果100s内有10条数据更改,就会发生一次快照保存。 恢复快照的时候,只需要将rdb文件位置放在配置文件中就行。
    • 优点是适合大规模数据恢复
    • 缺点是最后一次持久化可能会数据丢失。 fork需要占用一定内存空间。
  • AOF:Append Only File
    • 开启AOF:在配置文件中更改为 appendly yes
    • 如果AOF文损坏之后,就不能启动redis-server,因为内存加载数据的时候会发现文件不可使用。 redis提供了一个工具:redis-check-aof,可以修复aof文件
    • 以日志的形式将每个写操作记录下来,在加载redis-server服务的时候通过再次执行命令恢复数据库数据。
    • 缺点是aof的文件大小远远大于rdb,运行效率比rdb慢

Redis中AOF持久化有三种策略:

  1. always:默认的策略,每个写命令都被立即记录到AOF文件中,保证数据的实时性。这种方式对AOF文件的更新比较频繁,同时也可以在Redis意外退出时最大程度地恢复数据。
  2. everysec:每秒钟执行一次AOF日志记录,减少AOF文件记录的频率,也减少了将数据写入磁盘的次数,降低了对性能的影响,但是在Redis意外退出时可能会丢失不足一秒钟的数据。
  3. no:即不开启AOF持久化,Redis只使用内存作为数据存储,这种方式对性能影响最小,但对于对数据完整性有特殊要求的应用程序来说,风险较大。

消息订阅

redis发布订阅是一种消息通信模式:pub发送消息,sub接收消息,它是单向通信。

  • 建立并订阅频道:subscribe name
  • 发布消息:publish name message

使用场景:

实时消息系统

聊天室

订阅关注系统

更复杂的我们需要用MQ

主从复制

将一台redis服务器作为主节点,多个redis服务器作为从节点,主节点专门负责写数据,从节点专门负责读数据,实现读写分离,从主从复制就是主节点数据复制到从节点, 数据流向只能是单向的。

主从复制的作用:

  • 数据冗余
  • 故障恢复
  • 负载均衡
  • 高可用

默认情况下每个Redis服务器都是主节点,当设置为从节点的时候,只能有一个主节点,而一个主节点可以有多个从节点

info replication:查看当前库的信息

配置方式

  1. 使用多个配置文件启动多个redis-server
    1. 每个服务一个不同的端口,配置文件需要修改每个server的信息 比如端口号、数据存放路径、pidfile、lo等等
  2. 配置从机:slaveof host port

主机的所有写操作都会刷入到从机,从机不能进行任何写操作

当没有哨兵的时候,如果主机宕机导致连接端口,从机仍然认为主机存在,当主机恢复连接依然可用,但是断开的时候不能写,虽然这样保持了高可用性,但断开的时候应该自主选择一个从机作为主机;如果从机断开,重连后从机配置消失,只有将主从配置写入配置文件才能继续保持高可用

一个从机可以作为另一个从机的主机,主从嵌套。

哨兵模式

哨兵是一个独立进程,原理是通过哨兵发送redis命令确定redis-server存活,同时一般来讲我们会配置多个哨兵,相互监控redis-server和别的哨兵。

假设主服务器机,哨兵1先检测到这个结果,系统并不会马上进行failover过程,仅仅是哨兵主观的认为主服务器不可用,这个现象成为主观下线。当后面的哨兵也检测到主服务器不可用,并数量达到一定值时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行failover故障转移操作。切换成功后,就会通过发布订阅模式,让各个哨兵把自已监控的从服务器实现切换主机,这个过程称为客观下线

创造一个哨兵过程

  • 配置文件:sentinel.conf
    • sentinel minitor redis-name host port id[id自己取名,是数字]用于注册一个被监视redis-server 一般监视主机,它会自动发现从机
  • 启动哨兵:redis-sentinel config-address

当哨兵发现主机宕机后,会自动选取一个从机作为其余从机的主机

优点: 1、哨兵集群,基于主从复制模式,所有的主从配置优点,它全有 2、主从可以切换,敌障可以转移,系统的可用性就会更好 3、哨兵模式就是主从模式的升级,手动到自动,更加健壮! 缺点: 1、Redis不好啊在线扩容的,集群容量一旦到达上限,在线扩容就十分麻烦! 2、实现哨兵模式的配置其实是很麻烦的,里面有很多选择!

一些哨兵配置:

#Examplesentinel.conf #哨兵sentine1实例运行的端口默认26379 port26379 #哨兵sentinel的工作目录 dir /tmp #哨兵sentine监控的redis主节点的ipport #master-name可以自己命名的主节点名字只能由字母A-z、数字o-9、这三个字符",-"组成。 #quorum配置多少个sentine1哨兵统一认为master主节点失联那么这时客观上认为主节点失联了

sentinel monitor <master-name><ip>``<redis-port><quorum>

sentine1 monitor mymaster 127.0.0.1 6379 2 #当在Redis实例中开启了requirepassfoobared授权密码这样所有连接Redis实例的客户端都要提供密码 #设置哨兵sentine连接主从的密码注意必须为主从设置一样的验证密码 #sentinelauth-pass <master-name><password> sentine1 auth-pass mymaster MysuPER--secret-0123passwOrd #指定多少毫秒之后主节点没有应答哨兵sentine1此时哨兵主观上认为主节点下线默认3o秒 #sentineldown-after-mi11iseconds <master-name><mii1iseconds> sentinel down-after-mi11iseconds mymaster 30oo0 #这个配置项指定了在发生failover主备切换时最多可以有多少个slave同时对新的master进行同步 这个数字越小,完成fai1over所需的时间就越长 但是如果这个数字越大,就意味着越多的slave因为rep1ication而不可用。 可以通过将这个值设为1来保证每次只有一个s1ave处于不能处理命令请求的状态。

缓存穿透

读的请求会先在缓存redis中查询,如果没有,则会进入mysql进行查询,如果mysql中也没有(有则会缓存在redis中)

当大量的查询请求涌向mysql就会造成mysql奔溃。

解决方案

  1. 在redis中加上一层过滤
  2. 对于查询为空的请求返回""

布隆过滤器和缓存空对象

缓存击透

在redis进行持久化数据的过程中,可能有大量相同key的请求涌向redis,而redis没有处理请求而涌向了mysql,造成了缓存击透

解决方案:

设置热点数据永不过期

加上互斥锁:在相同请求涌向mysql的时候,保证只有一个线程进入mysql

缓存雪崩

缓存雪崩指的是某一个时间段,缓存几种过期失效,redis宕机

在写入redis的时候,突然写入大量的数据,而且他们的缓存时间设置相同,当时间已过,缓存过期,同时有大量的访问进入导致走向了mysql,造成雪崩。还有一中是redis宕机,导致所有请求走向mysql数据库

解决方案:

保持redis高可用

限流降级 这个解决方案的思想是,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个ke只充许一个线程查询数据和写缓存,其他线程等待。

数据预热 数据加热的含义就是在正式部署之前,我先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中。在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。