Redis学习

1.什么是Redis

  • redis是一个NOSql数据库(非关系型数据库),区别于MySql,SQLServer,Oracle等有行列的关系型数据库。
  • 可以使用在高并发,但存储量不大的情况中。
  • 数据存储在内存中,也可以存储在磁盘中。

2.Redis使用场景

  • 数据缓存
    对于需要经常查询的数据,可以使用Redis,放到内存中,加快读取速度。
  • 计数器
    比如网站访问量,评论数,注册用户数,Redis有incr 和 incrby
  • 实时防攻击系统
    对于短时间发送多次比如登录操作的暴力破解请求,可以使用Redis进行记录,判断
  • 设定有效期的应用
    比如有些活动,并发量大,而且有有效期,可以Redis设置过期时间,到期自动移除活动
    vip,红包,session,到期失效。购物券,自动解锁。
  • 排行榜
    zset有排序功能,适用于各种排行榜
  • 数据去重
    使用set,自动去重,如果想要获取一段并发量高的数据并去重,则可使用Redis
  • 订阅发布
    构建实时消息系统
  • 队列
    使用list构建队列,sorted set构建有顺序的队列。在秒杀这种高并发环境下,可以把名额放到内存队列中。

3.Redis常用命令

1.key

  • keys *
  • del key
  • expire key second
  • ttl key
  • 数据库16个默认0,切换使用select index
  • flushall删除所有数据库数据
  • flushdb删除当前数据库数据

2.string

  • set key value
  • get key value
  • mset key value key value
  • mget key key

3.list

  • lpush key value value
  • rpush key value value
  • lpop key
  • rpop key
  • lrange key 0 -1
  • lindex key 0
  • lrem key count value
  • ltrim key start stop

4.set

  • sadd key member member
  • srem key member
  • smembers key

5.zset

  • zadd key member member
  • zcard key
  • zcount key
  • zrange key
  • zrevrange key

6.hash

  • hset user name zhangsan
  • hget user name
  • hmset user name zhangsan age 11
  • hmget user name age
  • hgetall
  • hkeys
  • hvals
  • hmset user:1 name zs age 11 sex 1
  • set user:1 {“id”:1,“name”:“zs”,“age”:“11”,“sex”:1}

7.事务

  • multi
  • set name zs
  • get name
  • exec
  • discard
  • 事务队列,错误不回滚

8.订阅发布

  • SUBSCRIBE channel
  • PUBLISH channel message
  • 先订阅后发布,区分大小写

9.设置密码

  • 配置文件中requirepass,记得启动服务端时指定使用配置文件
  • auth password密码认证

4.Java使用Redis-Jedis

  1. 导入jar
    在这里插入图片描述
  2. 使用
  • 创建连接的连接池工具类JedisPoolUtil
package com.qinjie.redis;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

/**
 * Jedis连接池
 * 用来优化数据库连接,减少开启关闭时间
 */
public class JedisPoolUtil {

    private static JedisPool pool = null;

    static{
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        //最大空闲
        jedisPoolConfig.setMaxIdle(2);
        //最大总连接
        jedisPoolConfig.setMaxTotal(200);
        //初始化连接池
        pool = new JedisPool(jedisPoolConfig, "127.0.0.1", 6379, 1000, "nice_qinjie");
    }

    //获取Jedis对象
    public static Jedis getJedis(){
        return pool.getResource();
    }

}

  • 测试
package com.qinjie.redis;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPubSub;
import redis.clients.jedis.Transaction;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class TestJedis {

    private Jedis jedis;

    @Before
    public void before(){
        //通过连接池获取Jedis对象
        jedis = JedisPoolUtil.getJedis();
        //清空当前数据库数据
        jedis.flushDB();
    }

    @After
    public void after(){
        jedis.close();
    }

    /**
     * 1.测试key
     * @throws Exception
     */
    @Test
    public void testKey() throws Exception{
        //1.keys:所有键==============
       /* keys();
        //2.del key:删除键=====================
        jedis.del("name");
        keys();
        //3.expire:过期======================
        jedis.expire("age",10);
        String age = jedis.get("age");
        System.out.println("age:"+age);
        //4.flushdb:删除当前库数据=========================
        jedis.flushDB();
        keys();*/
        //5.select:选择数据库,总共16个,默认使用下标0的======================
//        jedis.select(1);
//        jedis.set("name","任盈盈");
//        System.out.println("selectRet1:"+jedis.get("name"));
//        jedis.select(0);
//        jedis.set("name","田伯光");
//        System.out.println("selectRet0:"+jedis.get("name"));
//        //6.flushall:删除所有数据库数据=====================
//        jedis.flushAll();
//        System.out.println("selectRet0:"+jedis.get("name"));
//        jedis.select(1);
//        System.out.println("selectRet1:"+jedis.get("name"));
        //7.incr:加一,必须数字
        jedis.set("age","1");
        jedis.incr("age");
        System.out.println(jedis.get("age"));
        //8.decr:减一
        jedis.decr("age");
        System.out.println(jedis.get("age"));
        //9.incrBy:按照增长值增长
        jedis.incrBy("age",5);
        System.out.println(jedis.get("age"));
        //10.decrBy:按照减少值减少
        jedis.decrBy("age",1);
        System.out.println(jedis.get("age"));

    }

    private void keys(){
        Set<String> keys = jedis.keys("*");
        keys.forEach(e->{
            System.out.println("keyEach:"+e);
        });
    }

    @Test
    public void testKeys() throws Exception{
        keys();
    }

    /**
     * 2.测试字符串
     * @throws Exception
     */
    @Test
    public void testString() throws Exception{

        // 字符串设置
        jedis.set("name","令狐冲");
        System.out.println(jedis.get("name"));
        //字符串设置对象字符串
        jedis.set("user:1","{\n" +
                "    \"name\": \"小小\",\n" +
                "    \"age\":10\n" +
                "}");
        System.out.println(jedis.get("user:1"));

        //设置多个值
        jedis.mset("name","令狐冲","age","10");
        List<String> user = jedis.mget("name","age");
        user.forEach(e->System.out.println(e));

    }

    /**
     * 3.测试list
     * @throws Exception
     */
    @Test
    public void testList() throws Exception{
        //1.lpush:从头插入key的很多值
//        jedis.lpush("name","小白","小黑","小新");
//        printListRestVal();
//      //2.lpop:从头移除key中的第一个值(后进先出,栈)
//        String name = jedis.lpop("name");
//        System.out.println("lpop的值:"+name);
//        printListRestVal();
        //3.rpop:从尾移除key的第一个值
//        String name = jedis.rpop("name");
//        System.out.println("rpop的值:"+name);
//        printListRestVal();
//        //4.rpush:从尾插入key的很多值,先进先出,队列
//        jedis.rpush("name","小白","小黑","小新");
//        printListRestVal();
//        String name = jedis.lpop("name");
//        System.out.println("lpop:"+name);
        //5.lindex:根据下标取值
//        jedis.rpush("name","小白","小黑","小新");
//        printListRestVal();
//        String name = jedis.lindex("name", 0);
//        System.out.println("lindex的值:"+name);
        //6.lrem移除值:count>0,移除从头开始,value值的count个值
        jedis.rpush("name","小白","小黑","小新","小白","小白");
        printListRestVal();
        jedis.lrem("name",2,"小白");
        printListRestVal();
    }

    /**
     * 4.测试Set集合:
     * @throws Exception
     */
    @Test
    public void testSet() throws Exception{
        //saddd:增加无序不重复数据
        jedis.sadd("name","杨过","郭靖","小龙女","杨过");
        //smembers:获取key的所有数据
        jedis.smembers("name").forEach(e->System.out.println(e));
        //srem:移除set中的值
        jedis.srem("name","杨过");
        jedis.smembers("name").forEach(e->System.out.println(e));
    }

    /**
     * 5.有序set
     * @throws Exception
     */
    @Test
    public void testZSet() throws Exception{
        Map map = new HashMap();
        map.put("柯南",5d);
        map.put("小白",2d);
        map.put("小黑",6d);
        //zadd:添加有序不重复数据,添加多个,第二个参数是map,且map中的value为double类型;否则添加一个值
        jedis.zadd("name",map);
        //zcard:总数
        Long zcard = jedis.zcard("name");
        System.out.println("zcard:"+zcard);
        //zcount:一定范围内的计数
        Long zcount = jedis.zcount("name", 2, 6);
        System.out.println("zcount:"+zcount);
        //zrange:获取集合内容,按照score正序排序
        jedis.zrange("name",0,-1).forEach(e->System.out.println(e));
        //zrevrange:获取元素内容,按照score倒序排序
        jedis.zrevrange("name",0,-1).forEach(e->System.out.println(e));
    }

    /**
     *  6.事务
     *  一组操作,需要同时成功或者失败。
     *  比如Service的方法加上了@Transactional,
     *  那么该方法使用的mapper操作数据库增删改,就会出现要么一起成功/一起失败的情况。
     *  事务特性:
     *  原子性
     *  隔离性
     *  一致性
     *  持久性
     *  redis.clients.jedis.exceptions.JedisDataException:
     *  Cannot use Jedis when in Multi. Please use Transaction or reset jedis state.
     * @throws Exception
     */
    @Test
    public void testTransaction() throws Exception{
        //开启事务,获取Transaction
        Transaction transaction = jedis.multi();
        transaction.set("name","小白");
//        //失败,事务依然提交了
//        transaction.incr("name");
//        //事务提交
//        List<Object> list = transaction.exec();
//        boolean listEmpty = list.isEmpty();// 判断事务是否成功
//        if(listEmpty){
//            System.out.println("事务执行失败");
//        }
//        list.forEach(e->System.out.println(e));
//        System.out.println(jedis.get("name"));
        //不提交
        String discard = transaction.discard();
        System.out.println(discard);
        System.out.println(jedis.get("name"));
    }

    /**
     * 7.订阅测试
     * @throws Exception
     */
//    @Test
//    public void testSubscribe() throws Exception{
//        jedis.subscribe(new JedisPubSub( ) {
//            @Override
//            public void subscribe(String... channels) {
//                super.subscribe(channels);
//            }
//        });
//    }

    //打印list剩余的值
    private void printListRestVal(){
        System.out.println("in:printListRestVal===========================");
        List<String> name1 = jedis.lrange("name", 0, -1);
        name1.forEach(e->System.out.println("list中还有的值:"+e));
    }

}

5.相关面试题

  1. 项目中那些地方用到Redis?
    • vip到期,session过期,红包到期等有过期时间的情况
    • 对于需要经常查询,并发量大且消耗数据库资源的操作,可以使用Redis,将数据放在内存中,加快读写速度
    • 对于比如商品销量排行,客户订单排行等情况,可以使用zset的排序功能实现
    • 获取不可重复的数据,可以使用set
    • 订阅/发布,构建实时消息系统,比如在客户和商家的聊天功能中,使用Redis
    • 队列,使用list构建队列,sorted list构建有序队列。比如在秒杀情况中,使用Redis的队列,处理高并发的情况
    • 网站访问量,注册用户数,商品评论数等需要计数的情况,可以使用Redis
  2. 使用什么来操作Redis?
    java中使用Jedis来操作Redis,一般使用连接池来获取Jedis对象,再通过Jedis对象进行数据操作。
  3. 你使用过memcached?
    没有使用过,只是听说过。它和Redis都是非关系型数据库,不是有行列关系的关系型数据库。都是使用key-vlaue进行数据的存储。只是Redis除了将数据存储到内存里,也可持久化到磁盘中。
  4. 你为什么要使用redis?
    • redis可以应对高并发的情况,因为数据存储在内存中,不像(关系型数据库)磁盘中的数据可以很快获取到。
    • 可以实现实时消息系统。
    • 可以做网站数量统计。
    • 可以解决秒杀等高并发情况下的数据问题
  5. redis怎么实现栈和队列?
    • 使用list结构即可实现。
      • list的lpush和lpop即可实现后进先出的栈功能
      • list的rpush和lpop即可实现先进先出的队列功能
  6. 事务四大特性?
    • 原子性
      事务中的操作是最小单位,不可再进行分割。要么一起成功,要么一起失败。
    • 隔离性
      各个事务间的操作是相互分离的,互不影响的。
    • 一致性
      结果是一致的。比如转账,不能出现,转账的人减少了,收款的人没收到。
    • 持久性
      事务是需要提交,持久化到数据库中去的
  7. 为什么要使用连接池
    • 解决频繁创建对象带来的巨大开销
  8. 说一下redis是怎么存储数据的?
    • redis中的数据是存储到内存中的,可以加快访问速度。为了防止数据在redis死掉后丢失(比如断电),可以将其持久化存储。
    • redis提供了两种持久化的策略AOF和RDB
    • RDB:默认使用,可以在配置文件中配置在一定时间内有多少次改动,就保存一份数据
    • AOF:需要开启,在有数据写操作时,进行记录,在启动时,执行写操作。可以设置有改动则记录,某段时间记录,交给操作系统记录。
  9. redis数据满了时会怎么办?
    • 会启用淘汰策略
    • 一种根据有过期时间的键的数据进行淘汰
    • 一种根据所有键的数据进行淘汰
    • 过期时间的,有最近使用最少,即将过期,随机淘汰
    • 所有键的,有最近使用最少,随机
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值