Redis从入门到精通

Redis笔记

此笔记部分根据狂神说redis部分视频记录,感谢狂神说!下面为视频路径:https://www.bilibili.com/video/BV1S54y1R7SB?t=951&p=35

一:简介
nosql

​ no only sql 不仅仅是sql
​ 一般称之为非关系型数据库
​ web2.0存在一些问题:(3高)
​ 1.高并发
​ 2.大数据
​ 3.高扩展,高可用
​ 分类:
​ key/value的格式
​ 文档型格式
​ 列类型
​ 图

redis

redis是使用c语言开发的一个高性能键值对的数据库,可以用作数据库、缓存和消息中间件
支持的数据类型(五大基础类型,三种特殊类型)

​ String(★) 字符串
​ hash(理解) 散列
​ list 列表
​ set 集合
​ sortedSet(zset) 有序集合

​ geospatial 地理空间

​ hyperloglog 范围查询

​ bitmaps

默认16个数据库,默认0号

select 3 #切换数据库
DBSIZE	#查看数据库的数据量
keys *	#查看数据库锁有的key
flushall #清空所有数据库
flushdb #清空当前数据库

   
   
  • 1
  • 2
  • 3
  • 4
  • 5

redis是单线程的(6.0以后支持多线程),官方表示redis是基于内存操作的,redis的瓶颈是根据计算机的内存和网络宽带。

  1. 多线程(CPU上下文切换:耗时)不一定比单线程快。redis是将所有数据放在内存中的,所以使用单线程去操作效率就是最高的。多次读写都是在一个cpu上的。
二:安装
1.下载redis
2.上传到linux
3.安装redis	
	mkdir /usr/local/redis
	mv /root/redis.tar /usr/local/redis
	cd /usr/local/redis
	tar -xvf redis.tar
4.编译redis 依赖 gcc
	yum install gcc-c++
	make
5.安装redis
	make PREFIX=/usr/local/redis install
6.配置
	复制一个redis.conf 到bin目录下
	启动服务器的方式1:	
		前台启动的方式:
			cd /usr/local/redis/bin
			./redis-server redis.conf
		后台的方式:
			配置一下redis.conf
				修改:daemonize yes
			保存退出
	启动客户端
		简单的方式:
			./redis-cli    #连接本地端口号为 6379的服务器
		推荐的方式
			./redis-cli -h 连接ip -p 端口号

redis的停止
方式1:通过kill -9 进程号(不推荐)
方式2:通过客户端发送命令
./redis-cli -h ip -p port shutdown

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

性能测试:redis-benchmark.exe

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

三:基本操作方式
  1. string
    • 赋值:格式: set key value
      例如:set username tom

    • 取值:格式: get key
      例如: get username

    • 先获取再设置:getset key value,如果不存在值,返回nil
      例如: getset username jack

    • 删:del key
      例如: del d

    • 了解

      • 对于数字类型 自增和自减

        incr key ++

        decr key –

      • 增加或减少指定的数量

        incrby key int

        decrby key int

      • 拼接字符串,如果当前key不存在,就相当于set key value

        append key value

      • 字符串范围

        getrange key1 0 3 设置key1的值

        getrange key1 0 -1 相当于get key1

        setrange key2 1 xx 替换指定位置开始的字符串

      • setex(set with expire)设置过期时间

        setex key3 30 hello

      • setnx(set if not exist)如果不存在设置,在分布式锁中使用

        setnx mykey redis

      • 批量设置

        mset key1 v1 key2 v2 key3 v3

        mget key1 key2

        msetnx k4 v4 key1 v1 mset是原子性的操作,要么一起成功,要么一起失败

        user:{id}:{filed}

        mset user:1:name shangsan user:1:age 2

        mget user:1:name user:1:age

  2. list
    • 赋值:
      左边:lpush key value value2 value3
      右边:rpush key value value2 value3

    • 取值:
      左边:lpop key
      右边:rpop key

    • 获取所有元素
      lrange key 0 -1

    • 获取元素的个数
      llen key

    • 扩展:
      lpushx key value :若有则添加 若没有则不添加
      rpushx key value :若有则添加 若没有则不添加

      		lrem key count value:从左边移除count个value
      			若count>0 :从左边移除count个value
      			若count<0 :从右边移除count个value
      			若count=0 :从右边移除所有的value
      
      	lset key index value
      		设置链表中指定索引的元素值  0 代表是第一个
      			-1代表的是最后一个
      
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • hash:
    • 了解
      • 存入一个map集合
        user username tom
        age 18
        sex 1
      • 存值:
        存入一个值
        hset key subkey subvalue
        存入多个值
        hmset key subkey1 subvalue1 subkey2 subvalue2
      • 获取:
        获取一个值
        hget key subkey
        获取多个值
        hmget key subkey1 subkey2
      • 移除值:
        hdel key subkey subkey
        给一个key添加指定的数字
        hincrby key subkey int
  • set
    • 添加
      sadd key value1 valuse2
    • 删除
      srem key value1 valuse2
    • 获取
      smembers key
    • 判断是否是是set中的一员
      sismember key value
    • 运算
      差集: sdiff s1 s2
      交集: sinter s1 s2
      并集: sunion s3 s4
    • 获取数量
      scard key
      srandmember key:随机获取一个
  • sortedSet
    • 添加元素
      zadd key score m1 score m2
    • 获取元素
      zscore key m:获取指定成员的得分
      zcard key:获取key的长度
    • 删除元素
      zrem
  • 通用的操作
    • keys * :查看所有的key
    • del key:删除指定的key
    • exists key:判断一个key是否存在
    • rename oldkey newkey:重命名
    • expire key 秒数:
      ttl key :查看一个key剩余存活时间
      -1:持久存活
      -2:不存在
    • type 判断一个可以属于什么类型
  • 发布订阅
    • subscribe channel 订阅给定的频道的信息
    • psubscribe pattern 订阅一个或多个符合给定模式的频道
    • unsubscribe channel 指退订给定的频道
    • unpsubscribe [pattern] 退订所有给定模式的频道。
    • publish channel message 将信息发送到指定的频道
  • 事务

    Redis单条命令是保存原子性的,但是事务不保证原子性。redis事务没有隔离级别的概念,redis事务的本质是一组命令的集合,一个事务中的所有命令都会被序列化,在发起命令后,才会按照顺序执行。一致性、顺序性、排他性

    • 简介:

      • 批量操作在发送 EXEC 命令前被放入队列缓存。
      • 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
      • 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。
    • 命令:

      • multi 开始事务
      • exec 执行事务
      • discard 回滚
      127.0.0.1:6379[3]> multi
      OK
      127.0.0.1:6379[3]> set k1 v1
      QUEUED
      127.0.0.1:6379[3]> set k2 v2
      QUEUED
      127.0.0.1:6379[3]> get k2
      QUEUED
      127.0.0.1:6379[3]> set k3 v3
      QUEUED
      127.0.0.1:6379[3]> exec
      1) OK
      2) OK
      3) "v2"
      4) OK
      
          
          
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
    • 编译型异常(命令出错),所有事务都不会被执行

    • 运行时异常(如:0/1),其他的命令可以正常执行,错误命令抛出异常

    监控:

    • 悲观锁:认为什么时候都会出问题,无论做什么都会加锁
    • 乐观锁:认为什么时候都不会出现问题,不会上锁,更新数据时去判断在此期间是否有人修改过这个数据。获取version,更新时比较version
    #正常
    127.0.0.1:6379[3]> set money 100
    OK
    127.0.0.1:6379[3]> set out 0
    OK
    127.0.0.1:6379[3]> watch money
    OK
    127.0.0.1:6379[3]> multi
    OK
    127.0.0.1:6379[3]> decrby money 20
    QUEUED
    127.0.0.1:6379[3]> incrby out 20
    QUEUED
    127.0.0.1:6379[3]> exec
    1) (integer) 80
    2) (integer) 20
    
  • #多线程修改时,使用watch可当作乐观锁操作
    127.0.0.1:6379[3]> watch money
    OK
    127.0.0.1:6379[3]> multi
    OK
    127.0.0.1:6379[3]> decrby money 10
    QUEUED
    127.0.0.1:6379[3]> incrby out 10
    QUEUED
    #在执行前,另外一个线程修改了值,会导致执行失败
    127.0.0.1:6379[3]> exec
    (nil)
    #解决办法
    127.0.0.1:6379[3]> unwatch #先解锁,再获取锁
    OK
    127.0.0.1:6379[3]> watch money
    OK

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
  • 连接命令
    • AUTH password 验证密码是否正确
    • ECHO message 打印字符串
    • PING 查看服务是否运行
    • QUIT 关闭当前连接
    • SELECT index 切换到指定的数据库
  • geospatial地理位置,可以推算地理位置的信息,底层原理zset
    • geoadd 将指定的地理空间位置(纬度【-180度到180度】、经度【-85.05112878度到85.05112878度】、名称)添加到指定的key中,两极无法添加。一般会下载城市数据,直接通过java程序一次性导入。

      127.0.0.1:6379[3]> geoadd china:city 120.15 30.28 hangzhou
      (integer) 1
      127.0.0.1:6379[3]> geoadd china:city 113.62 34.75 zhengzhou
      
          
          
      • 1
      • 2
      • 3
    • geopos获取当前定位

      127.0.0.1:6379[3]> geopos china:city beijing tianjin
      1) 1) "116.39999896287918"
         2) "39.900000091670925"
      2) 1) "117.19999998807907"
         2) "39.120000488192183"
      
          
          
      • 1
      • 2
      • 3
      • 4
      • 5
    • 两个位置之间的直线距离

      127.0.0.1:6379[3]> geodist china:city beijing tianjin km
      "110.6313"
      
          
          
      • 1
      • 2
    • georadius:以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素。

      127.0.0.1:6379[3]> georadius china:city 110 30 1000 km
      1) "hangzhou"
      2) "zhengzhou"
      
          
          
      • 1
      • 2
      • 3

    georadiusbymember:指定成员的位置被用作查询的中心。

    127.0.0.1:6379[3]> georadiusbymember china:city beijing 1000 km
    1) "zhengzhou"
    2) "tianjin"
    3) "beijing"
    
      
      
    • 1
    • 2
    • 3
    • 4

    geohash:返回一个或多个位置元素的 Geohash (11位)表示。

    底层原理:zset

    127.0.0.1:6379[3]> zrange china:city 0 -1
    1) "hangzhou"
    2) "zhengzhou"
    3) "tianjin"
    4) "beijing"
    127.0.0.1:6379[3]> zrem china:city tianjin
    (integer) 1
    127.0.0.1:6379[3]> zrange china:city 0 -1
    1) "hangzhou"
    2) "zhengzhou"
    3) "beijing"
    
      
      
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
  • hyperloglog:数据结构,作基数统计的算法,如网页的UV(一个人访问一个网站多次,还算作一个人)

    基数:不重复的元素,允许容错

    127.0.0.1:6379[3]> pfadd mykey a d b  d d s k	#创建
    (integer) 1
    127.0.0.1:6379[3]> pfcount mykey	#统计数量
    (integer) 5
    127.0.0.1:6379[3]> pfadd mykey2 k i y g p w v
    (integer) 1
    127.0.0.1:6379[3]> pfmerge mykey3 mykey mykey2	#合并,并集
    OK
    127.0.0.1:6379[3]> pfcount mykey3
    
      
      
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
  • bitmaps:位图,数据结构,非0即1

    位运算,可用作打卡、签到等场景

    127.0.0.1:6379[3]> setbit sign 0 0	#设置
    (integer) 0
    127.0.0.1:6379[3]> setbit sign 1 1
    (integer) 0
    127.0.0.1:6379[3]> setbit sign 2 1
    (integer) 0
    127.0.0.1:6379[3]> getbit sign 2	#获取某个index的值
    (integer) 1
    127.0.0.1:6379[3]> bitcount sign	#统计记录
    (integer) 2
    
      
      
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
  • 四:Jedis

    Jedis是官方推荐的java连接开发工具,

    1. 导入对应的依赖

      <!--导入jedis-->
      <dependency>
          <groupId>redis.clients</groupId>
          <artifactId>jedis</artifactId>
          <version>3.2.0</version>
      </dependency>
      

    <!–fastjson–>
    <dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.62</version>
    </dependency>

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
  • 编码测试

    • 连接数据库

    • 操作命令

    • 断开连接

      public static void main(String[] args) {
          Jedis jedis = new Jedis("127.0.0.1", 6379);
          System.out.println(jedis.ping());
      
  • }

    • 1
    • 2
    • 3
    • 4
    • 5
  • API与命令相似

  • 例:事务

    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);
    
    jedis<span class="token punctuation">.</span><span class="token function">flushDB</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    
    JSONObject jsonObject <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">JSONObject</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    jsonObject<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"hello"</span><span class="token punctuation">,</span> <span class="token string">"world"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    jsonObject<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token string">"name"</span><span class="token punctuation">,</span> <span class="token string">"kuangshen"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    
    Transaction multi <span class="token operator">=</span> jedis<span class="token punctuation">.</span><span class="token function">multi</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    String s <span class="token operator">=</span> jsonObject<span class="token punctuation">.</span><span class="token function">toJSONString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">try</span> <span class="token punctuation">{</span>
        multi<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">"user1"</span><span class="token punctuation">,</span> s<span class="token punctuation">)</span><span class="token punctuation">;</span>
        multi<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">"user2"</span><span class="token punctuation">,</span> s<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">1</span> <span class="token operator">/</span> <span class="token number">0</span><span class="token punctuation">;</span>
        multi<span class="token punctuation">.</span><span class="token function">exec</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        multi<span class="token punctuation">.</span><span class="token function">discard</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{</span>
        System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>jedis<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">"user1"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>jedis<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">"user2"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        jedis<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    
  • }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    五:spring boot整合

    springboot操作数据:spring-data, jpa, mongodb, redis

    SpringBoot2.x以后,jedis被改成了lettuce
    jedis:采用的直连,多个线程操作不安全,若要避免,则要使用jedis pool连接池,更像Bio模式
    lettuce:采用netty,可以在多个线程中共享,不存在线程不安全的情况,可以减少线程数量,更像Nio模式
    
     
     
    • 1
    • 2
    • 3
    • 源码

      @Bean
      @ConditionalOnMissingBean(name = "redisTemplate")//不存在就生效
      public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
            throws UnknownHostException {
          //默认的RedisTemplate没有过多的设置,redis对象需要序列化
          //两个泛型都是Object,需要强制转换
         RedisTemplate<Object, Object> template = new RedisTemplate<>();
         template.setConnectionFactory(redisConnectionFactory);
         return template;
      }
      

    @Bean
    @ConditionalOnMissingBean //由于string类型是最长使用的类型,所以单独提取出来的
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
    throws UnknownHostException {
    StringRedisTemplate template = new StringRedisTemplate();
    template.setConnectionFactory(redisConnectionFactory);
    return template;
    }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
  • 导入依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    
      
      
    • 1
    • 2
    • 3
    • 4
  • 配置连接

    spring.redis.host=127.0.0.1
    spring.redis.port=6379
    
      
      
    • 1
    • 2
  • 测试及编码

  • @Autowired
    private RedisTemplate redisTemplate;
    
  • @Test
    void contextLoads() {
    //opsForValue操作string
    //opsForList list
    //opsForHyperLogLog HyperLogLog
    //除了基本的操作,常用的方法都可以直接通过RedisTemplate操作,比如事务
    //清空数据库通过connection操作
    /RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
    connection.flushDb();
    /

    redisTemplate<span class="token punctuation">.</span><span class="token function">opsForValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token string">"mykey"</span><span class="token punctuation">,</span> <span class="token string">"关注狂神说Java的公众号"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>redisTemplate<span class="token punctuation">.</span><span class="token function">opsForValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">"mykey"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    

    }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
  • 序列化配置,默认为JdkSerializationRedisSerializer。自定义序列化配置网上查询。

    @Nullable
    private RedisSerializer keySerializer = null;
    @Nullable
    private RedisSerializer valueSerializer = null;
    @Nullable
    private RedisSerializer hashKeySerializer = null;
    @Nullable
    private RedisSerializer hashValueSerializer = null;
    
      
      
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    若不序列化,会报错SerializationException

  • 六:Redis config

    配置文件redis.windows.conf

    1. 大小写不敏感

    2. 包含,INCLUDES,

    3. NETWORK,网络

      bind 127.0.0.1	#绑定IP、端口号
      port 6379
      protected-mode yes	#以守护线程的方式运行,默认为no
      
         
         
      • 1
      • 2
      • 3
    4. GENERAL

      # This can be one of:
      # debug (a lot of information, useful for development/testing)
      # verbose (many rarely useful info, but not a mess like the debug level)
      # notice (moderately verbose, what you want in production probably)生产环境使用
      # warning (only very important / critical messages are logged)
      loglevel notice
      

    logfile “” #日志文件位置名
    databases 16 #数据库数量

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
  • SNAPSHOTTING,快照

    持久化,在规定的时间内,执行了多少次操作,则会持久化到文件.rdb, .aof

    redis是内存数据库,若没有持久化,就会断电即失

    #持久化规则,如果xxxs内,至少有xxx个key进行修该,进行持久化操作
    save 900 1
    save 300 10
    save 60 10000
    
  • stop-writes-on-bgsave-error yes #持久化出差后,是否还进行工作
    rdbcompression yes #是否压缩rdb文件,需要消耗CPU资源

    rdbchecksum yes #保存rdb检查
    dir ./ #文件保存目录

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
  • REPLICATION配置主从复制,具体查看

  • SECURITY,安全,redis密码两种方式,配置文件修该或下述方式。

    127.0.0.1:6379> config get requirepass	#查看密码
    1) "requirepass"
    2) ""
    127.0.0.1:6379> config set requirepass "123456"	#设置密码
    OK
    127.0.0.1:6379> ping
    (error) NOAUTH Authentication required.
    127.0.0.1:6379> auth 123456	#登录
    OK
    127.0.0.1:6379> config get requirepass
    1) "requirepass"
    2) "123456"
    
      
      
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
  • LIMITS,服务端限制

    # maxclients 10000
    # maxmemory <bytes>
    # maxmemory-policy noeviction	内存达到上限后的策略
    	# volatile-lru -> remove the key with an expire set using an LRU algorithm
    	# allkeys-lru -> remove any key according to the LRU algorithm
    	# volatile-random -> remove a random key with an expire set
    	# allkeys-random -> remove a random key, any key
    	# volatile-ttl -> remove the key with the nearest expire time (minor TTL)
    	# noeviction -> don't expire at all, just return an error on write operations
    
      
      
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
  • APPEND ONLY MODE,aof配置

    appendonly no	#默认不开启,使用rdb方式
    appendfilename "appendonly.aof"
    
  • # appendfsync always #每次修改后同步,消耗性能
    appendfsync everysec #每秒执行一次
    # appendfsync no #不执行同步

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    七:持久化

    redis是内存数据库,若没有持久化,就会断电即失。在主从复制中,rdb是在从机上备用的,aof几乎不使用

    rdb:redis database

    RDB持久化方式能够在指定的时间间隔能对你的数据进行快照存储。redis会单独创建(fork)一个子进程来进行持久化,会将数据写入到一个临时rdb文件中,待持久化过程结束后,再用这个临时文件替换上次持久化好的文件,成为正式的rdb文件,子线程退出。缺点是最后一次持久化后的数据可能丢失。

    文件默认是dump.rdb

    触发机制:

    1. save的规则满足的条件下
    2. 执行flushall命令
    3. 退出redis

    恢复rdb文件:只需将rdb文件放在redis启动目录即可。

    aof:append only file

    将所有数据都记录下来,在恢复时会重新把所有命令都执行一遍。以日志的形式来记录每个写操作。只需要将appendonly no #默认不开启,使用rdb方式改为yes,然后重启即可生效。

    文件默认是appendonly.aof,若aof文件被破坏,则会启动失败。可使用redis-check-aof进行修复,会直接将错误命令删除

    redis-check-aof --fix appendonly.aof
    
     
     
    • 1

    当同时开启两种持久化方式,重启时会优先载入aof文件来恢复原始数据。因为rdb只用作后备,所以一般只用save 900 1即可。

    八:主从复制

    是指将一台redis服务器的数据,复制到其他redis服务器,前者称为master(主节点),后者称为从节点(slave/follower),数据的复制是单向的,只能从主节点到从节点,主节点只能写,从节点只能读。默认情况下,每台主节点都是主节点,且一个主节点可以有多个从节点,一个从节点只能有一个主节点。一个集群至少有三个服务器,一主二从。

    作用:

    1. 数据冗余
    2. 故障恢复
    3. 负载均衡
    4. 高可用(集群)基石

    环境配置:

    只配置从库,不配置主库。

    127.0.0.1:6379> info replication
    # Replication	查看当前的信息
    role:master
    connected_slaves:0	#从机
    master_repl_offset:0
    repl_backlog_active:0
    repl_backlog_size:1048576
    repl_backlog_first_byte_offset:0
    repl_backlog_histlen:0
    

    #复制redis的配置文件,对端口号、pidfile、logfile、dbfilename进行更改
    cp redis.conf redis79.conf
    cp redis.conf redis80.conf
    cp redis.conf redis81.conf

    #更改完成后用配置文件启动,并用ps -ef|grep redis查看启动线程
    redis-server kconfig/redis79.config
    redis-server kconfig/redis80.config
    redis-server kconfig/redis81.config

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    一主二从

    slaveof 127.0.0.1 6379	#在从机上配置从机的主机地址
    info replication	#查看主机信息
    
     
     
    • 1
    • 2

    上述命令行配置的主从复制时暂时的,真实的主从配置应该在配置文件中配置

    # slaveof <masterip> <masterport>
    

    # masterauth <master-password>

    • 1
    • 2
    • 3

    细节

    主机可以写,从机只能读,主机中的所有数据都会被从机保存。若主机shutdown,从机依旧是从机,主机重新启动后,从机会自动连接上主机,能获取到主机写入的新主机。

    如果使用命令行配置主从复制,当redis重新启动后,会默认恢复成主机,所以,当从机shutdown后,需要重新配置。

    复制原理

    slave启动成功连接到master后,会发送sync同步命令,master接到命令,会启动存盘进程,将整个数据文件传送给slave,并完成一次同步。

    • 全量复制:slave接到数据库文件后,将其存盘并加载到内存中
    • 增量复制:master继续将新的收集到的修改命令依次传送给slave,完成同步

    只要重新连接到主机,一次全量复制将被执行。

    主机–>从机/主机–>从机方式(层层链路)

    配置完成之后中间的redis依旧是从节点,当主节点shutdown后,需要进行如下配置

    slaveof no one	#让自己变成主机
    
     
     
    • 1

    如果第一个主节点恢复了,就需要重新配置。哨兵模式不需要配置上述步骤。

    九:哨兵模式

    redis2.8+提供了Sentinel(哨兵)架构。能够后台监控主机是否故障,如果发生故障,根据投票数自动将从库转换为主库

    。通过发送命令,等待redis服务器响应,从而监控多个redis实例。使用多个哨兵进行监控,形成多哨兵模式。

    如果主服务器宕机,哨兵1先检测到这个结果,系统不会马上进行投票(主观下线),当其他哨兵也检查到并数量达到一定后,哨兵就会发起投票,进行故障转移,切换成功之后,就会通过发布订阅模式,实现票数最多的从机切换主机(客观下线)。

    配置sentinel.config

    #主机名称 端口号 宕机时,从机投票
    sentinel monitor myredis 127.0.0.1 1
    

    #启动哨兵
    redis-sentinel kconfig/sentinel-conf

    • 1
    • 2
    • 3
    • 4
    • 5

    如果主机恢复之后,只能归并到新的主机下,当作从机。

    1. 哨兵集群,基于主从复制模式
    2. 主从可以切换,故障可以转移,是主从模式的升级,系统的可用性更好
    3. 集群数量一旦达到上限,在线扩容十分麻烦。
    4. 哨兵模式的配置麻烦,可以在网络上查询
    十:缓存穿透和雪崩

    服务的高可用问题

    缓存穿透(查不到)

    概念

    缓存穿透的概念很简单,用户想要查询一个数据,发现redis内存数据库没有,也就是缓存没有命中,于是向持久层数据库查询。发现也没有,于是本次查询失败。当用户很多的时候,缓存都没有命中,于是都去请求了持久层数据库。这会给持久层数据库造成很大的压力,这时候就相当于出现了缓存穿透。

    解决方案

    1. 布隆过滤器

      是一种数据结构,对所有可能查询的参数以hash形式存储,在控制层先校验,不符合丢弃,从而避免了底层存储系统的查询压力。

    2. 缓存空对象

      当存储层不命中后,即使返回空对象也会被存储起来,同时设置一个过期时间,之后访问这个数据会直接从缓存中获取,保护后端数据源。

      • 如果空值能被缓存起来,这就意味着缓存需要更多的空间存贮更多的键,因为可能会有很多空值的键。
      • 即使对空值设置了过期时间,还是会存在存储层和缓存层的数据会有一段时间的不一致,这对于需要保证一致性的业务会有影响
    缓存击穿(量太大,缓存过期)

    概念

    缓存击穿,是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。

    解决方案

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

    2. 加互斥锁

      分布式锁,使用分布式锁,保证每个key同时只有一个线程去查询后端服务,其他的线程没有获得分布式锁的权限,因此只需要等待即可。这种方式把高并发的压力转移到了分布式锁,因此对分布式锁的考验很大。

    缓存雪崩

    概念

    缓存雪崩是指,缓存层出现了错误,不能正常工作了(redis宕机,缓存集中过期失效等)。于是所有的请求都会达到存储层,存储层的调用量会暴增,造成存储层也会挂掉的情况。

    解决方案

    1. redis高可用

      多设置几台redis,搭建redis集群。(异地多活)

    2. 限流降级

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

    3. 数据预热

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

    sh形式存储,在控制层先校验,不符合丢弃,从而避免了底层存储系统的查询压力。

    1. 缓存空对象

      当存储层不命中后,即使返回空对象也会被存储起来,同时设置一个过期时间,之后访问这个数据会直接从缓存中获取,保护后端数据源。

      • 如果空值能被缓存起来,这就意味着缓存需要更多的空间存贮更多的键,因为可能会有很多空值的键。
      • 即使对空值设置了过期时间,还是会存在存储层和缓存层的数据会有一段时间的不一致,这对于需要保证一致性的业务会有影响
                                    </div><div data-report-view="{&quot;mod&quot;:&quot;1585297308_001&quot;,&quot;dest&quot;:&quot;https://blog.csdn.net/qq_40940940/article/details/106517983&quot;,&quot;extend1&quot;:&quot;pc&quot;,&quot;ab&quot;:&quot;new&quot;}"><div></div></div>
                <link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-60ecaf1f42.css" rel="stylesheet">
                                </div>
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值