NoSQL?来学习使用Redis

162 阅读8分钟

Redis

#Go #缓存 github.com/go-redis/re…

Why NOSQL

  • Too much read, to decrease DQL, use Cache
  • Too much write: 分库分表

Mysql压力大: 数据量大 有些文件很大

  • NOSQL = Not Only SQL
  • 泛指非关系型数据库
  • map[string]interface{}

Features

  • 方便拓展
  • 大数据高性能
  • 数据类型多样,不用事先设计
  • 没有固定查询语言

NOSQL Classification

  • KV
  • 文档型
    • MongoDB
  • 列存储
  • 图关系
    • 存关系的

Redis入门

  • Remote dictionary server
  • 内存存储、持久化
  • 高效,作为缓存
  • 发布订阅
  • 计时计数

Basics

  • 默认16个数据库 0 in default
    • 用select切换
  • Flushdb 清空当前数据库
  • flushall 清空全部
  • Keys * 查看所有的key
  • Redis单线程,基于内存操作
    • 数据全在内存
    • 不需要CPU上下文切换

基本命令Keys

  • redis可用为DB,Cache,MQ
  • redis-key
    • set name kuangshen 》 name—狂神
    • get name 〉 查看name的值
    • exists name 〉 查看是否有name,返回bool(1/0)
    • move name 1 》 把name移到1号数据库
    • expire name 10 〉 name在10秒后过期
    • ttl name 》 name的生存时间
    • type name 〉 name的类型

String字符串

  • append name “hello” > 在name后面追加”hello“,不存在name时新建
  • strlen name 》 获取name长度
  • incr views 〉 views的值+1
  • decr views 》 views的值-1
  • incrby views 10 〉 views的值+10
  • decrby views 10 》 views的值-10
  • Getrange name 1 3 〉 从name中截取1~3的字符子串(第一个字符在0)
  • getrange name 0 -1 》 从name中截取全部字符串
  • setrange name 1 hello 〉从name的第1个位置(包含name【1】),设置为hello
  • setex name 30 “hello” 》 设置name值为“hello”,30秒过期
  • setnx name “Bob” 〉 如果不存在name,设置为“Bob”,返回bool(1/0)
  • mset k1 v1 k2 v2 k3 v3 ….. > 一次性set多个值,k1为v1,k2为v2……
  • mget k1 k2 k3 》 一次性get多个值k1,k2,k3
  • Msetnx k1 v1 k2 v2 〉 如果不存在一次性设置,这是一个原子性操作,两个设置有一次不成功,这个语句都不生效,只能一起成功/一起失败
  • set user:1{name:”kuangshen” ,age:3} > 设置一个user对象,json格式存储,user:{id}{field}
  • getset name “hello”》 先获取name的值,再设置为“hello”

List

  • List命令以l开头
  • Lpush list one 》 从头部向list当中推入“one”,每次新推入元素都会插入在头部,其余元素后移
  • lrange list 0 -1 〉 左侧开始,截取list的全部值
  • rpush list one 》 从尾部向list推入“one”
  • lpop list 〉 从list头部弹出一个元素
  • rpop list 》 从list尾部弹出一个元素
  • lindex list 1 〉 从list获取第1个值(从0开始计数)
  • llen list 》 返回list的长度
  • lrem list 1 one 〉 从list中移除值为“one”的一个元素
  • Ltrim list 1 3 》 将list截断,保留1~3的元素(包含1和3),其余元素移除
  • rpoplpush name words > 从name中移除最后一个元素,并加入在words 的头部
  • lset list 0 hello 》 将list的第0个值设为‘hello’ ( 前提是list存在,要创建一个list,先lpush第一个元素)
  • linsert list before “world” hello 》 在list中的world前面插入一个“hello”
  • linsert list after “hello” “world” 》 在list中的hello后插入一个“world”

Set

  • Set中值不能重复
  • 操作开头都是s
  • sadd set hello 》 在set中添加“hello”
  • smembers set 〉 查看set的元素
  • sismember set hello 》 检查“hello”是否为set的成员
  • scard set 〉 获取set中元素个数
  • srem set hello 》 从set中移除”hello“
  • srandmember set 〉 从set中随机获取一个值
  • srandmember set 2 》 从set中随机获取2个值
  • Spop set 〉 从set中随机删除一个元素
  • smove set1 set2 hello 》 把“hello”从set1移动到set2
  • sdiff set1 set2 〉 返回set1和set2的差集
  • sinter set1 set2 》 返回set1和set2的交集
  • sunion set1 set2 〉 返回set1 和 set2的并集

Hash

  • map集合
  • H开头
  • hset hash name shr 》 将hash设置name-shr键值对
  • hget hash name 〉 取hash中name的值
  • hmset hash name shr sex male 》 一次性设置hash的多个值
  • hmget hash name sex 〉 一次性获取hash中的多个值
  • hgetall hash 》 获取hash中的全部键和值
  • hdel hash name 〉 从hash中删除name
  • hlen hash 》 获得hash的长度(字段数量
  • hexist hash name 〉 判断hash中是否存在name
  • hkeys hash 》 获取hash中的所有键
  • hvals hash 〉 获取hash中的所有值
  • hincrby hash name 3 》 给hash中的name + 3
  • hdecrby hash name 3 〉 给hash中的name - 3
  • hsetnx hash name 4 》 若hash中name不存在,设置为4

Zset

  • 有序集合
  • Z开头
  • zadd zset 1 hello 》 给hello标号为1,加入zset
  • zrange zset 0 -1 〉 升序获得所有值
  • zrevrange zset 0 -1 》 降序返回所有值
  • zrangebyscore zset -inf +inf 》 按照标号顺序从负无穷到正无穷排序返回zset的元素值
  • zrangebyscore zset 100 200 withscore 》 按照标号从100到200,排序返回zset的元素值和标号
  • zrem zset name 〉 从zset中移除name
  • zcard zset 》 返回zset中元素个数
  • Zcount zset 1 2 〉 获取zset中标号1~2之间的元素个数(包含1,2)

Geospatial

  • 推算地理位置信息
  • geoadd china:city 116.4 39.9 beijing 》 向china:city增加北京的纬经度位置
  • geopos china:city beijing 〉 从china:city中取出beijing的位置
  • geodist china:city beijing Shanghai km 》 获得china:city中beijing与Shanghai的距离(km)(两者必须都已经加入china:city)
  • georadius china:city 110 30 1000 km count 3》 在china:city中,以110,30 为中心,1000 km为半径搜索3个地理位置
  • georadiusbymember china:city beijing 1000 km withcoord count 1 》 在china:city中,以beijing为中心,1000 km为半径,返回一个地理位置及其经纬度

Hyperloglog

  • 浏览量计数可以用
  • 用到的时候查官网
  • pfadd
  • pfcount
  • pfmerge

Bitmaps

  • 用户打卡,用户登录签到可以用
  • setbit bitmap 0 1 》 在bitmap的第0个记为1
  • getbit bitmap 1 〉 查看bitmap第1天的记录
  • bitcount bitmap 》 统计bitmap里1的个数

事务

  • 原子性
  • Redis的事务没有原子性,单条命令有原子性
  • 事务本质:一组命令的集合
  • 一次性、顺序性、排他性
  • multi 》 开启事务
    • 开启之后输入的指令就会入列等待执行
  • Exec 〉 执行事务
  • discard 》 取消事务
  • 事务异常
    • 如果是命令有错,那么命令全部都不会执行
    • 如果是运行时出现异常,事务执行不会回滚,其他命令正常,错误命令抛出异常
  • 乐观锁
    • watch money 》 监视money
    • 当监视的变量被其他线程操作修改,事务就会执行失败
    • Unwatch money > 事务执行失败的话解锁money,之后重新监视

Go-Redis

  • 先新建一个redis-client,传入redis.options配置
  • 用创建的client进行操作
  • 事务用tx := txpipeline()创建,然后调用tx的方法就行,在tx.exec()时,之前的所有调用会被包裹在multi和exec之间执行

Redis.conf

  • bind 》 绑定的ip
  • damon 》 守护进程
  • pidfile 〉pid进程文件
  • logfile 》 日志位置
  • databases 〉 数据库数量
  • Save 60 10 》 在60秒内如果有至少10个key修改,那么持久化操作
  • Dir 〉 持久化rdb保存的目录
  • requirepass 》 redis的密码设置
  • appendonly 〉 aof开启设置

持久化

  • RDB
    • RedisDataBase
    • 类似快照
    • 最后一次持久化数据可能丢失
    • 比AOF性能好
    • dump.rdb
    • flushall后会自动触发rdb规则
    • 适合大规模数据恢复
  • AOF
    • AppendOnlyFile
    • 类似历史记录,记录所有命令,恢复时全部执行
    • 只许追加不可改写
    • 默认不开启
    • 重启redis生效
    • Aof文件错误时无法启动redis
      • redis-check-aof —fix xxxx.aof > 修复aof
    • Aof修复慢,文件大
  • 只作缓存,不用持久化

发布订阅

  • 消息发布者、频道、消息订阅者
  • subscribe name 》 订阅name
  • publish name hello 〉 在name上发布消息
  • 原理: map [频道] ([ ]用户链表)
  • 实时消息
  • 实时聊天
  • 订阅、关注

主从复制

  • 数据复制只能 master->slave
  • Master以写为主,slave以读为主,读写分离
    • 数据冗余
    • 故障恢复
    • 负载均衡
    • 高可用
  • 不能单机使用redis,一定主从复制!
  • info replicaiton 》 查看当前库的复制信息
  • 复制原理
    • slaveof ip port 〉 将当前redis作为ip:port的从机
    • Slaveof no one > 将自己设为主节点
    • 命令设置只是暂时的,要去conf里永久性修改
    • 在conf中
      • replicaof > 设置为masterip:masterport的从机
  • 从机不能写(set)
  • 主机断线重连,从机没影响

哨兵模式 Sentinel

  • 自动选主机
  • 哨兵是个独立进程,哨兵向各服务器发送请求,等待响应,来监控redis服务
  • 多哨兵模式
    • 各哨兵之间也会互相监控
  • 主观下线 与 客观下线
    • 主观下线是指一开始哨兵发现主服务掉线
    • 客观下线指各slave投票后选出新的主服务器,原来的主为客观下线
  • 步骤
    • 哨兵配置sentinel.conf
      • Sentinel monitor myredis ip port 1 》1代表主机掉线时选举1
    • 启动哨兵
      • redis-sentinel sentinel.conf
    • 主机重新上线之后会成为从机

缓存穿透 和 雪崩

  1. 缓存穿透的概念(查不到
    1. 缓存服务器没有数据,用户向mysql请求依然 没有此数据,查询失败
      1. 解决方案:
        1. 布隆过滤器
        2. 缓存空对象
  2. 缓存击穿(量太大
    1. 有key成为热点,请求集中,key在失效(过期)瞬间大量并发请求访问持久化数据库
      1. 解决:
        1. 设置热点数据永不过期
        2. 分布式锁,只允许一个线程查询数据库
  3. 缓存雪崩
    1. 某个时间段缓存集中失效/缓存服务器宕机,大量的查询请求来到数据库,产生波峰压力
      1. 解决
        1. 多台redis,redis高可用
      2. 服务降级
        1. 加锁/减少服务,降低数据库压力
      3. 数据预热
        1. 在大并发访问前,手动把可能的key加入缓存并控制过期时间,使其均匀