美文网首页
Redis 基础知识

Redis 基础知识

作者: Minnakey | 来源:发表于2020-02-24 17:46 被阅读0次
    Install
    $ wget http://download.redis.io/releases/redis-5.0.7.tar.gz
    $ tar xzf redis-5.0.7.tar.gz
    $ cd redis-5.0.7
    $ make
    $ src/redis-server
    
    $ src/redis-cli   // Redis using the built-in client
    redis-cli -h host -p port -a password   // remote server  --raw 防止乱码
    redis> set foo bar
    OK
    redis> get foo
    "bar"
    CONFIG GET  # check config
    
    Redis五种数据类型

    Redis 命令参考
    all commands

    del key
    dump key
    exists key
    expire key s
    expireat key timestamp
    pexpire key ms
    pexpireat key milliseconds-timestamp
    KEYS pattern  //查找所有符合给定模式( pattern)的 key 
    MOVE key db_name
    persist key  
    PTTL key
    TTL key
    randomkey
    rename key newkey
    renameNX key newkey
    TYPE key
    

    string(字符串): k,v
    hash(哈希): key field:value {"id":1,"name":"zs","age":18}
    list(列表): key ["v1","v2","v3"]
    set(集合): key (score1 member1, score2 member2)
    zset(sorted set:有序集合) : zrevrange key 0 3

    • 字符串
    get key
    getrange key start end
    getset key new-value
    getbit key
    mget key1 key2
    setbit key
    setnx key
    setex key
    setrange key start end
    strlen key
    mset key value [...]
    msetnx ...
    psetex key 
    incr key // num+1
    incrby key  increment // 将 key 所储存的值加上给定的增量值(increment)
    incrbyfloat key increment
    decr key // num-1
    decrby key decrement   // key 所储存的值减去给定的减量值(decrement)
    append key value  // +value
    
    • HASH
    hset key field value 
    hsetnx key field value
    hmget key field1 field2
    hmset key field1 field2
    hdel key field1 
    hexists key field
    hget key field
    hgetall key
    hincrby key field increment  // 
    hincrbyfloat key field increment
    hkeys key
    hlen key
    hvals  key   // 获取哈希表中所有值
    HSCAN key cursor [MATCH pattern] [COUNT count]  //迭代哈希表中的键值对。
    
    • List
    lpush key value  // 插入表头
    lpushx key value
    blpop key timeout
    brpop key timeout
    brpoplpush source destination timeout
    lindex key index 
    linsert key before|after pivot value
    llen key
    lpop key   // 移出并获取列表的第一个元素
    lrange key start stop
    lrem key count value
    lset key index value 
    ltrim key start stop
    rpop key   // 移出并获取列表的最一个元素
    rpoplpush source destination
    rpush key value  // 在列表中添加一个或多个值
    rpushx key value 
    
    • Set
    sadd key member1
    scard key   // 获取集合的成员数
    sdiff key1   //差集
    sdiffstore destination key1 [key2]]
    sinter key  //交集
    sinterstore  destination key1   // 返回给定所有集合的交集并存储在 destination 中
    sismember key member   //判断 member 元素是否是集合 key 的成员
    smembers key
    smove source destination member   // 将 member 元素从 source 集合移动到 destination 集合
    spop key    //移除并返回集合中的一个随机元素 
    srandmember key [count]   //返回集合中一个或多个随机数
    srem key member1   // 移除集合中一个或多个成员
    sunion key1 // 并集
    sunionstore destination key1   //所有给定集合的并集存储在 destination 集合中
    sscan key cursor [match pattern][Count count]  //迭代集合中的元素
    
    Sorted set
    zadd  key1 member1
    zcard key
    zcount key min max 
    zincrby key increment member
    zinterstore destination numbers key
    zlexcount key min max 
    zrange key start stop
    zrangebylex key min max
    zrangebyscore key min max [WITHSCORES] [LIMIT]
    
    • Redis HyperLogLog 基数统计
    pfadd key element
    pfcount key
    pfmerge key destkey sourcekey
    
    pub/sub 发布订阅
    SUBSCRIBE channel [channel ...] // SUBSCRIBE redisChat
    PUBLISH channel message // PUBLISH redisChat "Redis is a great caching technique"  
    PSUBSCRIBE pattern 
    PUBSUB subcommand [argument [argument ...]]  //查看订阅与发布系统状态
    PUNSUBSCRIBE [pattern [pattern ...]]  //退订所有给定模式的频道
    UNSUBSCRIBE [channel [channel ...]]  //退订给定的频道
    
    事务与Scripting
    MULTI   // 开始
    EXEC  //退出
    DISCARD  //取消事务
    UNWATCH  //
    WATCH  //监听
    
    EVAL script numkeys key [key ...] arg [arg ...] // EVAL "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second
    script kill
    script flush  //从缓存中移除所有脚本
    script load script
    script existis script
    
    connect
    CONFIG set requirepass "password"
    auth "password"
    echo message
    ping
    quit
    select index
    Jedis jedis = new Jedis("IP", 6379);
    jedis.auth("你给redis设置的密码")
    
    服务器
    info
    bgsave save //数据备份
    CONFIG GET dir //如果需要恢复数据,只需将备份文件 (dump.rdb) 移动到 redis/bin并启动服务即可
    config get maxclients  // 最大连接数
    
    
    问题
    • 数据为什么会过期? redis是用来做数据缓存的: 过期时间到了,内存溢出
    • redis的过期策略是什么样的?定期删除+惰性删除
    • 内存淘汰机制
      ① noeviction: 当内存不足以容纳新写入数据时,新写入操作会报错,无法写入新数据,一般不采用。
      ②allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key,这个是最常用的。
      ③allkeys-random:当内存不足以容纳新写入的数据时,在键空间中,随机移除key,一般也不使用。
      ④volatile-lru:volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key(这个一般不太合适) 。
      ⑤volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key 。
      ⑥volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。
    maxmemory 100mb  # 设置最大内存
    maxmemory-policy volatile-lru   #默认是noeviction,即不进行数据淘汰 config
    
    • 手写一个LRU算法
    //基于JavaLinkedHashMap实现
    public class LRUCache<K,V> extends LinkedHashMap<K,V>{
    private final int CACHE_SIZE;
          
    //保存传递进来的最大数据量
    public LRUCache(int cacheSize){
    //设置hashmap的初始大小,同时最后一个true指的是让linkedhashmap按照访问顺序来进行排序,
    //最近访问的放在头,最老访问的放在尾
    super((int)Math.ceil(cacheSize/0.75)+1,0.75f,true);
              CACHE_SIZE = CacheSize;
          }
    
    @Override
    protected boolean removeEldestEntry(Map.Entry eldest){
    //当map中的数据量大于指定的缓存个数的时候,就自动删除最老的数据。
    return size() > CACHE_SIZE;
          }
    }
    
    • 库存控制(DECR)和自增序列(INCR)
    Redis性能调优
    • 避免长耗时命令
      SSCAN/HSCAN/ZSCAN等命令,分别用于对Set/Hash/Sorted Set中的元素进行游标式遍历
      slowlog 自动记录耗时较长的命令
    slowlog-log-slower-than xxxms  #执行时间慢于xxx毫秒的命令计入Slow Log
    slowlog-max-len xxx  #Slow Log的长度,即最大纪录多少条Slow Log
    SLOWLOG GET [number]
    SLOWLOG RESET
    
    • 网络引发的延迟 : 尽可能使用长连接或连接池, 使用Pipeline
    • 数据持久化引发的延迟
    • 制定合理的持久化策略
      1. AOF + fsync always的设置虽然能够绝对确保数据安全,但每个操作都会触发一次fsync,会对Redis的性能有比较明显的影响。
      2. AOF + fsync every second是比较好的折中方案,每秒fsync一次
      3. AOF + fsync never会提供AOF持久化方案下的最优性能
      4. 使用RDB持久化通常会提供比使用AOF更高的性能,但需要注意RDB的策略配置
      5. 每一次RDB快照和AOF Rewrite都需要Redis主进程进行fork操作。fork操作本身可能会产生较高的耗时,与CPU和Redis占用的内存大小有关。根据具体的情况合理配置RDB快照和AOF Rewrite时机,避免过于频繁的fork带来的延迟
    • Swap引发的延迟
    • 数据淘汰引发的延迟
    • 引入读写分离机制
    主从复制与集群分片
    • 主从复制
      Redis支持一主多从的主从复制架构。一个Master实例负责处理所有的写请求,Master将写操作同步至所有Slave。
      1. 主从之间的数据以Redis Protocol进行同步
      2. 借助Redis Sentinel实现高可用
    • 集群分片
      1.为何要做集群分片?
      Redis中存储的数据量大,一台主机的物理内存已经无法容纳
      Redis的写请求并发量大,一个Redis实例以无法承载
      2.分片方案:Redis Cluster
      能够自动将数据分散在多个节点上
      当访问的key不在当前分片上时,能够自动将请求转发至正确的分片
      当集群中部分节点失效时仍能提供服务
      3.Redis Cluster分片原理

    Redis Cluster中共有16384个hash slot,Redis会计算每个key的CRC16,将结果与16384取模,来决定该key存储在哪一个hash slot中,同时需要指定Redis Cluster中每个数据分片负责的Slot数,Slot的分配在任何时间点都可以进行重新分配。
    客户端在对key进行读写操作时,可以连接Cluster中的任意一个分片,如果操作的key不在此分片负责的Slot范围内,Redis Cluster会自动将请求重定向到正确的分片上。
    hash tags -- 在基础的分片原则上,Redis还支持hash tags功能,以hash tags要求的格式明明的key,将会确保进入同一个Slot中
    使用Redis Cluster时,pipelining、事务和LUA Script功能涉及的key必须在同一个数据分片上,否则将会返回错误。
    如要在Redis Cluster中使用上述功能,就必须通过hash tags来确保一个pipeline或一个事务中操作的所有key都位于同一个Slot中。
    有一些客户端(如Redisson)实现了集群化的pipelining操作,可以自动将一个pipeline里的命令按key所在的分片进行分组,分别发到不同的分片上执行。
    但是Redis不支持跨分片的事务,事务和LUA Script还是必须遵循所有key在一个分片上的规则要求。

    • 主从复制 vs 集群分片

    Redis Cluster能够解决单节点上数据量过大的问题
    Redis Cluster能够解决单节点访问压力过大的问题
    Redis Cluster包含了主从复制的能力
    在主从复制和集群分片两个方案中做出选择时,应该从应用软件的功能特性、数据和访问量级、未来发展规划等方面综合考虑,只在确实有必要引入数据分片时再使用Redis Cluster。

    开发

    Redis的Java客户端很多,官方推荐的有三种:Jedis、Redisson和lettuce。
    Jedis:轻量,简洁,便于集成和改造
    Redisson: 性能高,文档丰富

    //todo: 自定义redis source 
    /**
     * 
     * hset areas AREA_US US
     * hset areas AREA_CT TW,HK
     * hset areas AREA_AR PK,KW,SA
     * hset areas AREA_IN IN
     *
     * HashMap
     *
     * US,AREA_US
     * TW,AREA_CT
     * HK,AREA_CT
     *
     */
    public class KkbRedisSource implements SourceFunction<HashMap<String,String>> {
    
        private Logger logger = LoggerFactory.getLogger(KkbRedisSource.class);
    
        private Jedis jedis;
        private boolean isRunning=true;
    
        @Override
        public void run(SourceContext<HashMap<String, String>> cxt) throws Exception {
            this.jedis = new Jedis("192.168.1.120",6379);
    
            HashMap<String, String> map = new HashMap<>();
            while(isRunning){
              try{
                  map.clear();
                  Map<String, String> areas = jedis.hgetAll("areas");
                  for(Map.Entry<String,String> entry: areas.entrySet()){
                      String area = entry.getKey();
                      String value = entry.getValue();
                      String[] fields = value.split(",");
                      for(String country:fields){
                          //
                          map.put(country,area);
                      }
                  }
                  if(map.size() > 0 ){
                      cxt.collect(map);
                  }
                  Thread.sleep(60000);
              }catch (JedisConnectionException e){
                  logger.error("redis连接异常",e.getCause());
                  this.jedis = new Jedis("192.168.167.254",6379);
              }catch (Exception e){
                  logger.error("数据源异常",e.getCause());
              }
    
            }
    
        }
    
        @Override
        public void cancel() {
            isRunning=false;
            if(jedis != null){
                jedis.close();
            }
    
        }
    }
    

    相关文章

      网友评论

          本文标题:Redis 基础知识

          本文链接:https://www.haomeiwen.com/subject/fgchqhtx.html