redis入门篇学习笔记二

12. 三种特殊的数据类型

12.1 GEO地理位置

简介:

redis的GEO特性在redis3.2版本中推出,这个功能可以将用户给定的地理位置信息存储起来,并对这些信息进行操作。来实现诸如附近位置,摇一摇这类依赖于地理位置信息的功能GEO的数据类型为Zset。

GEO的数据结构总共有六个常用命令:geoadd ,geopos ,geodist , georadius, georadiusbymember , gethash

官方文档:https://www.redis.net.cn/order/3685.html

命令:

geoadd

将指定的地理空间位置(经度,纬度,名称)添加到指定的key中。这些数据会存储到有序集合Zset中,目的是为了方便使用georadius,或者georadiusbymember命令对数据进行半径查询等操作。

geoadd china:city 121.48 31.40 shanghai 113.88 22.55 shenzhen 120.21 30.20 hangzhou

注意点:该命令采用标准格式的参数xy,所以经度必须在纬度之前。有效的经度 -180~180度,有效的纬度 -85.05~85.05


geopos

从key里面返回所有给定位置元素的位置(经度和纬度)

geopos key member[member...]

geopos china:city shanghai hangzhou


geodist

返回两个给定位置之间的距离,指定的参数unit必须是以下中的一种。m 米 ,km 千米, mi 英里,ft 英尺,默认是m。

geodist key member1 member2 [unit]

geodist china:city shanghai hangzhou km


georadius

以给定的经纬度为中心,找出某一半径内的元素。

范围的单位:m米,km千米 ,mi英里 ,ft英尺

georadius key longitude(经度) latitude(纬度) radius m|km|ft|mi [withcoord] [withdist][withhash][asc|desc][count count]

withdist:返回位置元素的同时,将位置元素与中心之间的距离也一并返回。距离的单位与用户给定范围单位保持一致。

withcoord:将位置元素的经纬度也返回。

withhash:以 52 位有符号整数的形式, 返回位置元素经过原始 geohash 编码的有序集合分值。 这个选项主要用于底层应用或者调试, 实际中的作用并不大

asc: 根据中心的位置, 按照从近到远的方式返回位置元素。

desc:根据中心的位置, 按照从远到近的方式返回位置元素。

count:获取前n个匹配的元素,可以减少带宽当数据量大时。

举例:

georadius china:city 120 30 1500 km withdist


georadiusbymember

找出位于指定范围内的元素,中心点是由给定的位置元素决定的

georadiusbymember key member radius m|km|ft|mi [withcoord][withdist][withhash][asc|desc][count count]

举例:

georadiusbymember china:city shanghai 1500 km


geohash

返回一个或者多个位置元素的geohash表示。

redis使用geohash将二维经纬度转换为一维字符串,字符串越长表示位置更精确,两个字符串越相似表示距离越近。

geohash key member[member...]

举例:

geohash china:city shanghai hangzhou


zrem

geo没有提供删除成员的方法,但是因为geo的底层是zset,所有可以用zrem命令实现对地理位置信息的删除。

zrem china:city shanghai:移除元素

zrange china:city 0 -1:查看所有元素

命令演示

12.2 HyperLogLog

简介

redis在2.8.9版本添加了HyperLogLog结构。

redis HyperLogLog是用来做基数统计的算法。HyperLogLog的优点是在输入元素的数量和体积非常大时,计算基数所需的空间总是固定的,并且很小

基数:比如数据集 {1, 3, 5, 7, 5, 7, 8}, 那么这个数据集的基数集为 {1, 3, 5 ,7, 8}, 基数(不重复元素)为5。 基数估计就是在误差可接受的范围内,快速计算基数。

在redis里面,每个HyperLogLog键只需要花费12kb内存,就可以计算接近2^64个不同元素的基数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。

HyperLogLog则是一种算法,提供了不精确的去重基数方案。

举个栗子:假如我要统计网页的UV(浏览用户数量,一天内同一个用户多次访问只能算一次),传统的 解决方案是使用Set来保存用户id,然后统计Set中的元素数量来获取页面UV。但这种方案只能承载少量 用户,一旦用户数量大起来就需要消耗大量的空间来存储用户id。我的目的是统计用户数量而不是保存 用户,这简直是个吃力不讨好的方案!而使用Redis的HyperLogLog最多需要12k就可以统计大量的用户 数,尽管它大概有0.81%的错误率,但对于统计UV这种不需要很精确的数据是可以忽略不计的

基本命令

pfadd key element[element...]:添加指定元素到HyperLogLog

pfcount key[key...]:返回给定HyperLogLog的基数估计值。

pfmerge destkey sourcekey [sourcekey...]:将多个HyperLogLog合并为一个HyperLogLog,并集计算。

12.3 BitMap

简介

​ 在开发中,可能会遇到这种情况:需要统计用户的某些信息,如活跃或不活跃,登录或者不登录;又如 需要记录用户一年的打卡情况,打卡了是1, 没有打卡是0,如果使用普通的 key/value存储,则要记录 365条记录,如果用户量很大,需要的空间也会很大,所以 Redis 提供了 Bitmap 位图这种数据结构, Bitmap 就是通过操作二进制位来进行记录,即为 0 和 1;如果要记录 365 天的打卡情况,使用 Bitmap 表示的形式大概如下:0101000111000111…,这样有什么好处呢?当然就是节约内存 了,365 天相当于 365 bit,又 1 字节 = 8 bit , 所以相当于使用 46 个字节即可。

​ BitMap 就是通过一个 bit 位来表示某个元素对应的值或者状态, 其中的 key 就是对应元素本身,实际上 底层也是通过对字符串的操作来实现。Redis 从 2.2 版本之后新增了setbit, getbit, bitcount 等几个 bitmap 相关命令。

基本命令
  • setbit key n value:设置key的第n位的值value是0或1,n是从0开始的。
  • getbit key n:获取第n位的值。如果没有设置返回0
  • bitcount key[start,end]:统计key上值为1的个数。

13. redis事务

理论


redis事务的概念:

redis事务的本质是一组命令的集合。事务支持一次执行多个命令,一个事务中所有的命令都会被序列化。在事务执行过程中,会按照顺序串行化执行对列中 的命令,其他客户端提交的命令请求不会插入到事务执行的命令序列中。

总结的说:redis事务就是一次性,顺序性,排他性的执行一个对列中的一系列命令。


redis事务没有隔离级别的概念:

批量操作在发送exec命令前被放入对列缓存,并不会被实际执行。


redis不保证原子性:

redis中,单条命令是原子性执行的,但事务不保证原子性,且没有回滚。事务中任意命令执行失败,其余的命令仍会被执行。


redis事务的三个阶段:

开始事务

命令入队

执行事务


redis事务的相关命令

  • watch key1 key2...:监视一个或者多个key,如果在事务执行之前,被监视的key被其他命令改动,则事务被打断(类似乐观锁)
  • unwatch:取消watch对所有key的监控。
  • multi:标记一个事务块的开始(queued)
  • exec:执行所有事务块的命令(一旦执行exec后,之前加的监控锁都会被取消掉)
  • discard:取消事务,放弃事务块中所有 的命令

乐观锁和悲观锁?

  • 悲观锁(Pessimistic Lock),顾名思义就是很悲观,每次拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿到这个数据就会block知道它拿到锁。传统的关系型数据库里面就用到了很多这种锁机制,比如行锁,表锁等,读锁写锁等,都是在操作之前先上锁。
  • 乐观锁(Optimistic Lock)顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制,乐观锁适用于多读的应用类型,这样可以提高吞吐量,乐观锁策略:提交版本必须大于记录当前版本才能执行更新。

实践

  • 正常执行

  • 放弃事务

  • 如果事务对列中存在命令性错误(类似java编译性错误),执行exec命令时,所有命令都不会执行。

  • 如果在事务对列中存在语法性错误(类似于java的1/0运行时异常),则执行exec命令时,其他正确命令会被执行,错误命令抛出异常。

  • watch命令的演示(watch 用来监控key在事务执行的前后是否变化)–事务成功执行

  • 使用watch事务执行失败

    由于在事务执行过程中,watch监视的值出现了变化,因此导致了错误,这时要放弃监视,然后重来。

    **注意:**一旦exec执行事务后,无论事务执行成功还是失败,watch对变量的监控都被取消。因此当事务执行失败后,需要重新执行watch对变量进行监控,并开启新的事务进行操作。

    watch指令类似于乐观锁,事务提交时,如果watch监控的多个key中任何key的值被其他客户端更改,则使用exec执行事务时,事务对列不会被执行,同时返回Nullmulti-bulk 通知调用者事务执行失败。

14. springboot整合redis

步骤:

  • 导入依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  • 在application.properties中配置

    spring.redis.host=115.29.232.195
    spring.redis.port=6379
    spring.redis.database=0
    spring.redis.timeout=50000
    
  • 测试

    @SpringBootTest
    class SpringbootRedisApplicationTests {
        @Autowired
        private RedisTemplate redisTemplate;
    
        @Test
        void contextLoads() {
            /*
            * opsForList:操作list,类似string
            * opsForGeo 操作geo
            * opsForSet
            * .......
            * 和redis命令行一样。
            * */
           /*获取redis的连接对象*/
    //        RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
    //        connection.flushDb();
    //        connection.flushAll();
    
            redisTemplate.opsForValue().set("mykey","kuangshen");
            System.out.println(redisTemplate.opsForValue().get("mykey"));
            redisTemplate.opsForSet().add("k5","v9");
            Set k5 = redisTemplate.opsForSet().members("k5");
            System.out.println(k5);
    
        }
    }
    
    

redis要做的配置

  • 阿里云设置redis的安全组:端口6379


  • 修改redis.conf

    (在/usr/local/bin/myredisconfig/redis.conf)

    bind 127.0.0.1 改为 bind 0.0.0.0(注释掉也行

    protected-mode yes 改为 protected-mode no: (即该配置项表示是否开启保护模式,默认是开启,开启后Redis只会本地进行访问,拒绝外部访问)

    daemonize no 改为 daemonize yes

    注意:修改后要重新登录redis


  • 关于防火墙的设置

    rpm -qa|grep firewalld;rpm -qa|grep firewall-config :检查系统是否安装了firewalld和firewall-config.CentOS中系统默认安装firewalld, firewall-config要自己安装

    yum -y update firewalld:将firewalld更新为最新版

    yum -y install firewall-config:安装firewall-config

    systemctl start firewalld:启动firewalld服务

    systemctl status firewalld:查看firewalld的状态

    systemctl stop firewalld:停止firewalld服务

    systemctl enable firewalld:恢复开机自动启动firewalld服务

    推荐参考文章:CentOS7中firewalld的安装与使用详解 https://blog.csdn.net/solaraceboy/article/details/78342360


  • 检查阿里云的防火墙是否开放端口号

    firewall-cmd --query-port=6379/tcp 如果是 yes,则就是开放的。

    如果是no

    则永久开放6379端口:firewall-cmd --zone=public --add-port=6379/tcp --permanent

    重新加载:firewall-cmd --reload

    然后再查看端口是否开启:firewall-cmd --query-port=6379/tcp

  • 查看redis服务进程

     ps -ef | grep redis
    
  • 如果上面的方法都试过了但还是不行,试试重启服务器

推荐一个好用的redis客户端工具

​ Another Redis Desktop Manager

下载地址:https://github.com/qishibo/AnotherRedisDesktopManager、

可以用它来测试和管理redis

redis的序列化配置

  • 通过源码可知,springboot自动帮我们在容器中生成了一个redisTemplate和一个StringRedisTemplate,但是这个redisTemplate的泛型是<ooject,object>,写代码不方便,需要类型转换的代码,并且没有设置数据存在redis时,key及value的序列化方式。因此要自定义一个配置类。
  • 为什么要序列化:https://www.jianshu.com/p/cc5a29b06b3d
package com.kuang.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.net.UnknownHostException;

/**
 * Created with Intellij IDEA
 * Description:
 * user: CoderChen
 * Date: 2021-06-06
 * Time: 14:05
 */
@Configuration

public class redisConfig {
    /*编写自己的redisTemplate----固定模板*/
    @Bean
    @SuppressWarnings("all")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        /*为了自己开发方便,一般直接使用<String,object>*/
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        /*json序列化配置*/
        Jackson2JsonRedisSerializer Jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        Jackson2JsonRedisSerializer.setObjectMapper(om);
        /*string序列化配置*/
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();


        /*配置具体的序列化方式*/
        /*key采用string的序列化方式*/
        template.setKeySerializer(stringRedisSerializer);
        /*hash的key采用string的序列化方式*/
        template.setHashKeySerializer(stringRedisSerializer);
        /*value序列化方式采用jackson*/
        template.setValueSerializer(Jackson2JsonRedisSerializer);
        /*hash的value序列化方式采用jackson*/
        template.setHashValueSerializer(Jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

redis的工具类

  • 直接使用redisTemplate操作redis时,需要很多代码,因此直接封装一个redisUtils,这样写代码方便一点。这个redisUtils交给spring容器实例化,使用时直接注解注入。
package com.kuang.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
 * Created with Intellij IDEA
 * Description:
 * user: CoderChen
 * Date: 2021-06-06
 * Time: 14:52
 */
/*在真实开发中,经常使用*/
@Component
public final class RedisUtil {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    // =============================common============================

    /**
     * 指定缓存失效时间
     *
     * @param key  键
     * @param time 时间(秒)
     */
    public boolean expire(String key, long time) {
        try {
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 根据key 获取过期时间
     *
     * @param key 键 不能为null
     * @return 时间(秒) 返回0代表为永久有效
     */
    public long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    /**
     * 判断key是否存在
     *
     * @param key 键
     * @return true 存在 false不存在
     */
    public boolean hasKey(String key) {
        try {
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除缓存
     *
     * @param key 可以传一个值 或多个
     */
    @SuppressWarnings("unchecked")
    public void del(String... key) {
        if (key != null && key.length > 0) {
            if (key.length == 1) {
                redisTemplate.delete(key[0]);
            } else {
                redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(key));
            }
        }
    }
// ============================String=============================

    /**
     * 普通缓存获取
     *
     * @param key 键
     * @return 值
     */
    public Object get(String key) {
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }

    /**
     * 普通缓存放入
     *
     * @param key   键
     * @param value 值
     * @return true成功 false失败
     */
    public boolean set(String key, Object value) {
        try {
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 普通缓存放入并设置时间
     *
     * @param key   键
     * @param value 值
     * @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期
     * @return true成功 false 失败
     */
    public boolean set(String key, Object value, long time) {
        try {
            if (time > 0) {
                redisTemplate.opsForValue().set(key, value, time,
                        TimeUnit.SECONDS);
            } else {
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 递增
     *
     * @param key   键
     * @param delta 要增加几(大于0)
     */
    public long incr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递增因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, delta);
    }

    /**
     * 递减
     *
     * @param key   键
     * @param delta 要减少几(小于0)
     */
    public long decr(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递减因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, -delta);
    }
// ================================Map=================================

    /**
     * HashGet
     *
     * @param key  键 不能为null
     * @param item 项 不能为null
     */
    public Object hget(String key, String item) {
        return redisTemplate.opsForHash().get(key, item);
    }

    /**
     * 获取hashKey对应的所有键值
     *
     * @param key 键
     * @return 对应的多个键值
     */
    public Map<Object, Object> hmget(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * HashSet
     *
     * @param key 键
     * @param map 对应多个键值
     */
    public boolean hmset(String key, Map<String, Object> map) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * HashSet 并设置时间
     *
     * @param key  键
     * @param map  对应多个键值
     * @param time 时间(秒)
     * @return true成功 false失败
     */
    public boolean hmset(String key, Map<String, Object> map, long time) {
        try {
            redisTemplate.opsForHash().putAll(key, map);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 向一张hash表中放入数据,如果不存在将创建
     *
     * @param key   键
     * @param item  项
     * @param value 值
     * @return true 成功 false失败
     */
    public boolean hset(String key, String item, Object value) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 向一张hash表中放入数据,如果不存在将创建
     *
     * @param key   键
     * @param item  项
     * @param value 值
     * @param time  时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
     * @return true 成功 false失败
     */
    public boolean hset(String key, String item, Object value, long time) {
        try {
            redisTemplate.opsForHash().put(key, item, value);
            if (time > 0) {
                expire(key, time);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除hash表中的值
     *
     * @param key  键 不能为null
     * @param item 项 可以使多个 不能为null
     */
    public void hdel(String key, Object... item) {
        redisTemplate.opsForHash().delete(key, item);
    }

    /**
     * 判断hash表中是否有该项的值
     *
     * @param key  键 不能为null
     * @param item 项 不能为null
     * @return true 存在 false不存在
     */
    public boolean hHasKey(String key, String item) {
        return redisTemplate.opsForHash().hasKey(key, item);
    }

    /**
     * hash递增 如果不存在,就会创建一个 并把新增后的值返回
     *
     * @param key  键
     * @param item 项
     * @param by   要增加几(大于0)
     */
    public double hincr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, by);
    }

    /**
     * hash递减
     *
     * @param key  键
     * @param item 项
     * @param by   要减少记(小于0)
     */
    public double hdecr(String key, String item, double by) {
        return redisTemplate.opsForHash().increment(key, item, -by);
    }
// ============================set=============================

    /**
     * 根据key获取Set中的所有值
     *
     * @param key 键
     */
    public Set<Object> sGet(String key) {
        try {
            return redisTemplate.opsForSet().members(key);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 根据value从一个set中查询,是否存在
     *
     * @param key   键
     * @param value 值
     * @return true 存在 false不存在
     */
    public boolean sHasKey(String key, Object value) {
        try {
            return redisTemplate.opsForSet().isMember(key, value);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将数据放入set缓存
     *
     * @param key    键
     * @param values 值 可以是多个
     * @return 成功个数
     */
    public long sSet(String key, Object... values) {
        try {
            return redisTemplate.opsForSet().add(key, values);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 将set数据放入缓存
     *
     * @param key    键
     * @param time   时间(秒)
     * @param values 值 可以是多个
     * @return 成功个数
     */
    public long sSetAndTime(String key, long time, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().add(key, values);
            if (time > 0)
                expire(key, time);
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 获取set缓存的长度
     *
     * @param key 键
     */
    public long sGetSetSize(String key) {
        try {
            return redisTemplate.opsForSet().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 移除值为value的
     *
     * @param key    键
     * @param values 值 可以是多个
     * @return 移除的个数
     */
    public long setRemove(String key, Object... values) {
        try {
            Long count = redisTemplate.opsForSet().remove(key, values);
            return count;
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }
// ===============================list=================================

    /**
     * 获取list缓存的内容
     *
     * @param key   键
     * @param start 开始
     * @param end   结束 0 到 -1代表所有值
     */
    public List<Object> lGet(String key, long start, long end) {
        try {
            return redisTemplate.opsForList().range(key, start, end);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 获取list缓存的长度
     *
     * @param key 键
     */
    public long lGetListSize(String key) {
        try {
            return redisTemplate.opsForList().size(key);
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 通过索引 获取list中的值
     *
     * @param key   键
     * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0
     *              时,-1,表尾,-2倒数第二个元素,依次类推
     */
    public Object lGetIndex(String key, long index) {
        try {
            return redisTemplate.opsForList().index(key, index);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 将list放入缓存
     *
     * @param key   键
     * @param value 值
     */
    public boolean lSet(String key, Object value) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将list放入缓存
     *
     * @param key   键
     * @param value 值
     * @param time  时间(秒)
     */
    public boolean lSet(String key, Object value, long time) {
        try {
            redisTemplate.opsForList().rightPush(key, value);
            if (time > 0)
                expire(key, time);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将list放入缓存
     *
     * @param key   键
     * @param value 值
     * @return
     */
    public boolean lSet(String key, List<Object> value) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 将list放入缓存
     *
     * @param key   键
     * @param value 值
     * @param time  时间(秒)
     * @return
     */
    public boolean lSet(String key, List<Object> value, long time) {
        try {
            redisTemplate.opsForList().rightPushAll(key, value);
            if (time > 0)
                expire(key, time);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 根据索引修改list中的某条数据
     *
     * @param key   键
     * @param index 索引
     * @param value 值
     * @return
     */
    public boolean lUpdateIndex(String key, long index, Object value) {
        try {
            redisTemplate.opsForList().set(key, index, value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }


    /**
     * 移除N个值为value
     *
     * @param key 键
     * @param count 移除多少个
     * @param value 值
     * @return 移除的个数
     */
        public long lRemove (String key,long count, Object value){
            try {
                Long remove = redisTemplate.opsForList().remove(key, count,value);
                return remove;
            } catch (Exception e) {
                e.printStackTrace();
                return 0;
            }
        }
    }


测试代码

package com.kuang;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.kuang.pojo.User;
import com.kuang.utils.RedisUtil;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.Set;

@SpringBootTest
class SpringbootRedisApplicationTests {
    @Autowired
    @Qualifier("redisTemplate")/*避免与源码重合,跳转到自定义redisTempalte*/
    private RedisTemplate redisTemplate;

    @Autowired
    private RedisUtil redisUtil;


    @Test
    void contextLoads() {
        /*
        * opsForList:操作list,类似string
        * opsForGeo 操作geo
        * opsForSet
        * .......
        * 和redis命令行一样。
        * */
       /*获取redis的连接对象*/
//        RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
//        connection.flushDb();
//        connection.flushAll();

        redisTemplate.opsForValue().set("mykey","kuangshen");
        System.out.println(redisTemplate.opsForValue().get("mykey"));
        redisTemplate.opsForSet().add("k5","v9");
        Set k5 = redisTemplate.opsForSet().members("k5");
        System.out.println(k5);



    }

    @Test
    public void test() throws JsonProcessingException {
        /*真实开发一般使用json传递数据*/
        User user = new User("kuangshen", 3);
//        String jsonUser = new ObjectMapper().writeValueAsString(user);
//        redisTemplate.opsForValue().set("user", jsonUser);
        redisTemplate.opsForValue().set("user", user);
        System.out.println(redisTemplate.opsForValue().get("user"));

    }

    @Test
    public void test1() {
        redisUtil.set("username", "coderchen");
        System.out.println(redisUtil.get("username"));
    }

}

15. conf配置文件分析

熟悉基本配置

  • redis的配置文件的位置是/usr/local/redis/redis-6.06 redis.conf,但我们平时将它拷贝到/usr/local/bin/myredisconfig redis.conf 来进行操作和修改配置文件。确保初始文件的安全。

  • 在redis中查看基本的配置

    config get *:获取全部的配置,可以查看配置是否修改成功。

  • Units单位

    配置大小单位,开头定义了一些基本的度量单位,只支持bytes,不支持bit

    对大小写不敏感


  • includes包含

    和spring配置文件相似,可以通过includes包含,redis.conf作为总文件,可以包含其他文件。(读一下前面的英文说明即知)


  • network网络配置

    bind 0.0.0.0 :绑定的主机地址

    protected-mode no:保护模式,默认为yes, 默认是开启,开启后Redis只会本地进行访问,拒绝外部访问.需要设置为yes

    port 6379 :默认端口


  • general通用

    daemonize yes:默认情况下,redis不作为守护进程运行,需要开启的话,改为yes

    supervised no:可以通过upstart和systemd管理redis守护进程。

    pidfile /var/run/redis_6379.pid:以后台进程的方式运行redis,则需要指定pid文件。

    loglevel notice:日志级别。可选项有:

    debug:记录大量日志信息,适用于开发,测试阶段

    verbose:较多日志信息

    notice:适量日志信息,适用于生产环境

    warning:仅有部分重要,关键信息才会被记录

    logfile “”:日志文件的位置,当指定为空字符串时,为标准输出。

    databases 16:设置数据库的数目,默认数据库时DB 0

    always-show-logo yes:是否总是显示logo


  • snapshopting快照

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BF2Vf7Dn-1623067788419)(https://cdn.jsdelivr.net/gh/chenshuai-coder/CDN/img/20210607103041.png)]

    save 900 1:900秒(15分钟)内至少一个key值改变(则进行数据库保存–持久化)
    save 300 10:300秒(5分钟内)至少10个key值改变(则进行数据库保存–持久化
    save 60 10000: 60秒(1分钟)内至少10000个key值改变(则进行数据库保存–持久化)

    stop-writes-on-bgsave-error yes:持久化出现错误后是否依然进行工作

    rdbcompression yes:使用压缩rdb文件 yes:压缩 但是需要一些cpu 的消耗 no:不压缩,需要更多的磁盘空间。

    rdbchecksum yes:是否校验rdb文件,更有利于文件的容错性,但是在保存rdb文件的时 候,会有大概10%的性能损耗

    dbfilename dump.rdb:dbfilenamerdb文件名称

    dir ./: dir 数据目录,数据库的写入会在这个目录。rdb、aof文件也会写在这个目录


  • security安全

    获得和设置密码:(需要先进入redis)

    config get requirepass

    config set requirepass "123456"

    然后每次登录redis时,都要使用auth验证:

    auth password

    注意:如果服务器使用redis不设置密码,信息可能会收到攻击导致丢失。

    在conf中设置密码,将requirepass 这个注释去掉,后面的字段改成对应的密码。

  • clients客户端

    maxclients 10000:设置能连上redis的最大客户端连接数量

  • memory management 内存管理

    maxmemory :redis配置的最大内存容量

    maxmemory-policy noeviction:maxmemory-policy 内存达到上限的处理策略

    volatile-lru:利用LRU算法移除设置过过期时间的key。

    volatile-random:随机移除设置过过期时间的key。

    volatile-ttl:移除即将过期的key,根据最近过期时间来删除(辅以TTL)

    allkeys-lru:利用LRU算法移除任何key。

    allkeys-random:随机移除任何key。

    noeviction:不移除任何key,只是返回一个写错误

  • append only mode 只追加模式

常见的配置介绍

  1. redis默认不是以守护进程的方式进行,可以通过该配置项修改,使用yes启动守护进程

daemonize no —> daemonize yes

  1. 当Redis以守护进程方式运行时,Redis默认会把pid写入/var/run/redis.pid文件,可以通过pidfile指 定

pidfile /var/run/redis.pid

  1. 指定Redis监听端口,默认端口为6379,作者在自己的一篇博文中解释了为什么选用6379作为默认 端口,因为6379在手机按键上MERZ对应的号码,而MERZ取自意大利歌女Alessia Merz的名字

port 6379

  1. 绑定的主机地址

bind 127.0.0.1

  1. 当 客户端闲置多长时间后关闭连接,如果指定为0,表示关闭该功能

timeout 300

  1. 指定日志记录级别,Redis总共支持四个级别:debug、verbose、notice、warning,默认为 verbose

loglevel verbose

  1. 日志记录方式,默认为标准输出,如果配置Redis为守护进程方式运行,而这里又配置为日志记录方 式为标准输出,则日志将会发送给/dev/null

logfile stdout

  1. 设置数据库的数量,默认数据库为0,可以使用SELECT 命令在连接上指定数据库id

databases 16

  1. 指定在多长时间内,有多少次更新操作,就将数据同步到数据文件,可以多个条件配合

save

Redis默认配置文件中提供了三个条件:

save 900 1

save 300 10

save 60 10000

分别表示900秒(15分钟)内有1个更改,300秒(5分钟)内有10个更改以及60秒内有10000个更 改

  1. 指定存储至本地数据库时是否压缩数据,默认为yes,Redis采用LZF压缩,如果为了节省CPU时 间,可以关闭该选项,但会导致数据库文件变的巨大

rdbcompression yes

  1. 指定本地数据库文件名,默认值为dump.rdb

dbfilename dump.rdb

  1. 指定本地数据库存放目录

dir ./

  1. 设置当本机为slav服务时,设置master服务的IP地址及端口,在Redis启动时,它会自动从master 进行数据同步

slaveof

  1. 当master服务设置了密码保护时,slav服务连接master的密码

masterauth

  1. 设置Redis连接密码,如果配置了连接密码,客户端在连接Redis时需要通过AUTH 命令提供密码, 默认关闭

requirepass foobared

  1. 设置同一时间最大客户端连接数,默认无限制,Redis可以同时打开的客户端连接数为Redis进程可 以打开的最大文件描述符数,如果设置 maxclients 0,表示不作限制。当客户端连接数到达限制时, Redis会关闭新的连接并向客户端返回max number of clients reached错误信息

maxclients 128

  1. 指定Redis最大内存限制,Redis在启动时会把数据加载到内存中,达到最大内存后,Redis会先尝 试清除已到期或即将到期的Key,当此方法处理 后,仍然到达最大内存设置,将无法再进行写入操作, 但仍然可以进行读取操作。Redis新的vm机制,会把Key存放内存,Value会存放在swap区

maxmemory

  1. 指定是否在每次更新操作后进行日志记录,Redis在默认情况下是异步的把数据写入磁盘,如果不 开启,可能会在断电时导致一段时间内的数据丢失。因为 redis本身同步数据文件是按上面save条件来 同步的,所以有的数据会在一段时间内只存在于内存中。默认为no

appendonly no

  1. 指定更新日志文件名,默认为appendonly.aof

appendfilename appendonly.aof

  1. 指定更新日志条件,共有3个可选值:

no:表示等操作系统进行数据缓存同步到磁盘(快)

always:表示每次更新操作后手动调用fsync()将数据写到磁盘(慢,安全)

everysec:表示每秒同步一次(折衷,默认值)

appendfsync everysec

  1. 指定是否启用虚拟内存机制,默认值为no,简单的介绍一下,VM机制将数据分页存放,由Redis将 访问量较少的页即冷数据swap到磁盘上,访问多的页面由磁盘自动换出到内存中

vm-enabled no

  1. 虚拟内存文件路径,默认值为/tmp/redis.swap,不可多个Redis实例共享

vm-swap-file /tmp/redis.swap

  1. 将所有大于vm-max-memory的数据存入虚拟内存,无论vm-max-memory设置多小,所有索引数据 都是内存存储的(Redis的索引数据 就是keys),也就是说,当vm-max-memory设置为0的时候,其实是所有 value都存在于磁盘。默认值为0

vm-max-memory 0

  1. Redis swap文件分成了很多的page,一个对象可以保存在多个page上面,但一个page上不能被多 个对象共享,vm-page-size是要根据存储的 数据大小来设定的,作者建议如果存储很多小对象,page 大小最好设置为32或者64bytes;如果存储很大大对象,则可以使用更大的page,如果不 确定,就使用 默认值

vm-page-size 32

  1. 设置swap文件中的page数量,由于页表(一种表示页面空闲或使用的bitmap)是在放在内存中 的,,在磁盘上每8个pages将消耗1byte的内存

vm-pages 134217728

  1. 设置访问swap文件的线程数,最好不要超过机器的核数,如果设置为0,那么所有对swap文件的操作都 是串行的,可能会造成比较长时间的延迟。默认值为4

vm-max-threads 4

  1. 设置在向客户端应答时,是否把较小的包合并为一个包发送,默认为开启

glueoutputbuf yes

  1. 指定在超过一定的数量或者最大的元素超过某一临界值时,采用一种特殊的哈希算法

hash-max-zipmap-entries 64

hash-max-zipmap-value 512

  1. 指定是否激活重置哈希,默认为开启

activerehashing yes

  1. 指定包含其它的配置文件,可以在同一主机上多个Redis实例之间使用同一份配置文件,而同时各 个实例又拥有自己的特定配置文件

include /path/to/local.conf

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值