美文网首页
Redis高级特性

Redis高级特性

作者: maven_hz | 来源:发表于2018-01-13 23:02 被阅读0次

    本文涉及到的命令和数据类型请参见: Redis数据类型和基本操作

    命令

    • keys * (可模糊匹配)
    > set name mawenxia
    OK
    > set age 18
    OK
    > keys *
    1) "age"
    2) "name"
    > keys n*  //模糊匹配
    1) "name"
    
    • exists 判断key是否存在
    > exists name
    (integer) 1
    
    • 设置过期时间expire,查看过期时间ttl
    > expire name 3600
    (integer) 1
    > ttl name
    (integer) 3598
    
    • 取消过期时间persist
    > persist name
    (integer) 1
    
    • select 选择数据库。一个redis实例,数据库为0到15 一共16个,默认进入的是0数据库。可根据业务逻辑规则,或系统分类进行划分,存储在不同的库中
    >SELECT 1
    OK
    >127.0.0.1:6379[1]> keys *    //这边多了一个[1]
    (empty list or set)         //会发现之前的name和age都不在这里
    
    • move 移动数据到其他库中
    127.0.0.1:6379[1]> set sex nan    //在库[1]中存入sex
    OK
    127.0.0.1:6379[1]> keys *
    1) "sex"
    127.0.0.1:6379[1]> move sex 0   //移动sex到库[0]中
    (integer) 1
    127.0.0.1:6379[1]> select 0         //切换到[0]数据库
    OK
    127.0.0.1:6379> get sex             //获取sex
    "nan"                               //数据库[0]中已存在该数据
    
    • randomkey 随机返回数据库中的一个key 使用场景:抽奖。
    • rename 重命名key
    • echo 打印命令
    • dbsize 查看数据库key的数量
    • info 数据库信息查看 (CPU、内存、主从、集群、Client等信息) 。可用于监控平台
    • config get * 查看配置信息。可用于监控平台
    • flushdb 清空当前数据库 flushall 清空所有数据库

    位操作

    • bitset
    • bitcount 计算二进制位中所有1的数量
    • bitop and/or destkey [key1,key2] 按位与按位或操作
    • 位操作在实时统计记录用户活跃量、点击量等应用中能提供高效的应用
    • 基于这个应用,在本公司实际项目中可统计每个实验室每天发生的阳性样本数。以各实验室id为key,366个二进制字节位表示每一天。只要某一天有阳性。就以天为offset把当前位的0改为1。最后可以用bitcount key offset1 offset2统计其他对比指标随意时间段内的阳性数。并且可以用bitop操作来快速对比各实验室间情况。

    安全

    由于redis速度极快,一个外部用户一秒内可以进行15万次的密码尝试,这意味着需要设定一个非常强大的密码来防止暴力破解,但是再复杂的密码,在大量的尝试下还是会被hit。建议加入计数器,超过数量之后,不允许再登录,锁定一段时间。

    • 密码设置

      vi redis.conf 找得到 requirepass *** 设置密码
      设置密码之后。需要先使用 auth *** 登录

    事务

    • 使用multi 打开事务。执行完所有数据操作后,使用 exec 执行生效。
    > multi
    OK
    set p1 1
    QUEUED
    > set p2 2
    QUEUED
    > set p3 3
    QUEUED
    > exec
    1) OK
    2) OK
    3) OK
    > keys p*
    1) "p1"
    2) "p3"
    3) "p2"
    
    • 使用discard 取消事务
    > multi
    OK
    > set p4 4
    QUEUED
    > set p5 5
    QUEUED
    > discard
    OK
    127.0.0.1:6379> keys p*
    1) "p1"
    2) "p3"
    3) "p2"
    
    • 注意。目前redis还不能保证操作的原子性,可能部分回滚失败,比如事务中间出现报错的时候。
    > get name
    "mawenxia"
    > get age
    "19"
    > multi    //开启事务
    OK
    > incr age    //年龄+1
    QUEUED
    > incr name    //名字+1
    QUEUED
    > exec      //执行
    1) (integer) 20    //年龄+1成功
    2) (error) ERR value is not an integer or out of range  //名字+1报错
    > get age
    "20"    //年龄+1成功。说明事务的回滚失败
    

    Redis JavaAPI

    • 单实例下的redis是单线程的。
    • 单节点java操作redis一般使用Jedis
    • 在redis cluster 集群模式下,需要使用sharedJedis
    /**
     * ShardJedis的测试类
     */
    public class ShardJedisTest {
    
        private ShardedJedisPool sharedPool;
    
        @Before
        public void initJedis(){
            //Jedis池配置
            JedisPoolConfig config =new JedisPoolConfig();
            config.setTestOnBorrow(true);
            String hostA = "127.0.0.1";
            int portA = 6381;
            String hostB = "127.0.0.1";
            int portB = 6382;
            List<JedisShardInfo> jdsInfoList =new ArrayList<JedisShardInfo>(2);
            JedisShardInfo infoA = new JedisShardInfo(hostA, portA);
            JedisShardInfo infoB = new JedisShardInfo(hostB, portB);
            jdsInfoList.add(infoA);
            jdsInfoList.add(infoB);
            sharedPool =new ShardedJedisPool(config, jdsInfoList);
        }
    
        @Test
        public void testSetKV() throws InterruptedException {
            try {
                for (int i=0;i<50;i++){
                    String key = "test"+i;
                    ShardedJedis jedisClient = sharedPool.getResource();
                    System.out.println(key+":"+jedisClient.getShard(key).getClient().getHost()+":"+jedisClient.getShard(key).getClient().getPort());
                    System.out.println(jedisClient.set(key,Math.random()+""));
                    jedisClient.close();
                }
            }catch (Exception e){
                e.printStackTrace();
            }
    
        }
    
    }
    
    • 其他操作数据的API和命令行的操作基本一致,可参考Redis的基本命令来查找JedisAPI。
    • Jedis事务操作
    //开启事务
    Transaction tx = jedis.multi(); 
    //执行操作1.2.3...
    act1...
    act2...
    act3...
    //提交。并接收操作结果
    List<Object> resList = tx.exec();
    
    • Jedis Pool 使用连接池提高性能.操作结束必须返还连接
    SharedJedis sj = pool.getResource();
    ...各类操作
    //操作结束后返还连接
    pool.returnResourceObject(sj);
    

    Redis复杂查询技巧

    比如做一个类似 select * from user where age = 18 and sex='m'的操作

    • 关键是设计一个合理的存储结构。存储结构如下。说明:把Set当做查询的条件存储,只存放数据的id。然后根据id取全部数据
    数据类型 key (field) data
    Hash id {"name":"zhangsan","age",18,"sex":"m"}...
    Set userage18 key1,key2,key3...
    Set usersexm key1,key2...
    • Hash类型的中存储全部user数据。set 类型的“userage18”中存储所有年龄为18的用户的id。同理,“usersexm”存储性别为"m"的用户id

    • 查询时,只要查询Set中符合条件的id,然后根据id取出需要的user数据即可

    • Set集合有"sinter"取交集,“sunion”取并集命令。来取出符合"age=18 and sex=m" 或 "age=18 or and sex=m" 的用户id

      数据结构 sinter sunion 查询到用户key
    • 最后根据key来获取用户信息。

    • 这么做的好处:如果直接取出全部user数据在代码中判断符合条件的数据,很可能因为数据量过大,导致内存溢出。

    分布式系统中的应用

    • setnx 可作为分布式锁使用。对指定的key,线程获取锁时进行setnx 如果能设置成功,即获得锁。操作结束删除。如果未结束。其他线程setnx将失败。
    • incr incrby 可作为各系统的主键生成器,既保证了主键的唯一性,还保证了主键的连续性,利于数据的合并及查找。
    • list push 和pop 可作为消息队列使用
    • zset实现延迟队列 业务场景:订单需要在未支付的30分钟后失效/72小时后设置默认评论等,如果轮询查库,数据量大了会拖死数据库。利用zset的score排序特性,以下单时间作为score,key来区分不同类型的延迟队列,value为mq消息体。这样每种类型的延迟队列只需要有一个任务来查询最小的score跟当前时间做对比判断是否超过时间,如果超过就取出value,发送mq消息,让对应的服务来消费,做对应的业务(订单失效/设置默认评论)。

    Redis底层存储结构为 字节数组 故存储和取出时,注意要使用相同的字符集,否则将出现乱码。使用strlen 命令计算字符串长度时,也要特别注意,它计算的是字节数组的长度,并不是字符串长度,比如一个中文字,可能占用多个字节,故长度是会大于1的

    相关文章

      网友评论

          本文标题:Redis高级特性

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