> 文章列表 > Redis-0406

Redis-0406

Redis-0406

Redis配置文件介绍

  1. Units单位
    • 配置大小单位,开头定义了一些基本的度量单位,只支持bytes,不支持bit,大小写不敏感
  2. INCLUDES
    • 类似jsp中的include,多实例的情况可以把公用的配置文件提取出来
  3. 网络配置
    1. bind
      • 默认情况bind=127.0.0.1只能接受本机的访问请求
      • 不写的情况下,无限制接受任何ip地址的访问
      • 生产环境肯定要写你应用服务器的地址;服务器是需要远程访问的,所以需要将其注释掉
      • 如果开启了protected-mode,那么在没有设定bind ip且没有设密码的情况下,Redis只允许接受本机的响应
    2. protected-mode
      • 本机访问保护模式设置
    3. port:默认6379
    4. tcp-backlog:
      • 设置tcp的backlog,backlog其实是一个连接队列,backlog队列总和=未完成三次握手队列 + 已经完成三次握手队列。
      • 在高并发环境下你需要一个高backlog值来避免慢客户端连接问题。
      • 注意Linux内核会将这个值减小到/proc/sys/net/core/somaxconn的值(128),所以需要确认增大/proc/sys/net/core/somaxconn和/proc/sys/net/ipv4/tcp_max_syn_backlog(128)两个值来达到想要的效果
    5. timeout
      • 一个空闲的客户端维持多少秒会关闭,0表示关闭该功能。即永不关闭。
    6. tcp-keepalive
      • 对访问客户端的一种心跳检测,每个n秒检测一次。
      • 单位为秒,如果设置为0,则不会进行Keepalive检测,建议设置成60
  4. GENERAL通用
    1. daemonize : 是否后台进程,设置yes。守护进程,后台启动
    2. pidfile:存放pid文件的位置
    3. loglevel:指定日志记录级别
      • Redis总共支持四个级别:debug、verbose、notice、warning,默认为notice
      • 四个级别根据使用阶段来选择,生产环境选择notice 或者warning
    4. logfile : 日志文件名称
    5. database 16
      • 设定库的数量 默认16,默认数据库为0,可以使用SELECT 命令在连接上指定数据库id
  5. SECURITY 安全
    1. 设置密码 requirepass :访问密码的查看、设置和取消
      • 在命令中设置密码,只是临时的。重启redis服务器,密码就还原了。
      • 永久设置,需要再配置文件中进行设置。
  6. LIMITS限制
    1. maxclients
      • 设置redis同时可以与多少个客户端进行连接。
      • 默认情况下为10000个客户端。
      • 如果达到了此限制,redis则会拒绝新的连接请求,并且向这些连接请求方发出“max number of clients reached”以作回应。
    2. maxmemory
      • 建议必须设置,否则,将内存占满,造成服务器宕机
      • 设置redis可以使用的内存量。一旦到达内存使用上限,redis将会试图移除内部数据,移除规则可以通过maxmemory-policy来指定。
      • 如果redis无法根据移除规则来移除内存中的数据,或者设置了“不允许移除”,那么redis则会针对那些需要申请内存的指令返回错误信息,比如SET、LPUSH等。
      • 但是对于无内存申请的指令,仍然会正常响应,比如GET等。如果你的redis是主redis(说明你的redis有从redis),那么在设置内存使用上限时,需要在系统中留出一些内存空间给同步队列缓存,只有在你设置的是“不移除”的情况下,才不用考虑这个因素。
    3. maxmemory-policy
      • volatile-lru:使用LRU算法移除key,只对设置了过期时间的键;(最近最少使用)
      • allkeys-lru:在所有集合key中,使用LRU算法移除key
      • volatile-random:在过期集合中移除随机的key,只对设置了过期时间的键
      • allkeys-random:在所有集合key中,移除随机的key
      • volatile-ttl:移除那些TTL值最小的key,即那些最近要过期的key
      • noeviction:不进行移除。针对写操作,只是返回错误信息
    4. maxmemory-samples
      • 设置样本数量,LRU算法和最小TTL算法都并非是精确的算法,而是估算值,所以你可以设置样本的大小,redis默认会检查这么多个key并选择其中LRU的那个。
      • 一般设置3到7的数字,数值越小样本越不准确,但性能消耗越小。

发布订阅

  1. 什么是发布订阅
    • Redis 发布订阅 (pub/sub) 是一种消息通信模式:发送者 (pub) 发送消息,订阅者 (sub) 接收消息。
    • Redis 客户端可以订阅任意数量的频道。
  2. 如何实现:
    1. 订阅:命令:subscribe channel1
    2. 发布:命令:publish channel1 hello

Redis6的新数据类型

Bitmaps

  1. 简介:二进制序列,就是一串0、1字符串
  2. 命令:
    1. setbit
      • 格式: setbit<key><offset><value>设置Bitmaps中某个偏移量的值(0或1)
      • 实例:
      • 每个独立用户是否访问过网站存放在Bitmaps中, 将访问的用户记做1, 没有访问的用户记做0, 用偏移量作为用户的id。
        设置键的第offset个位的值(从0算起) , 假设现在有20个用户,userid=1, 6, 11, 15, 19的用户对网站进行了访问。
    2. getbit
      • 格式:getbit<key><offset>获取Bitmaps中某个偏移量的值
      • 实例:获取id=8的用户是否在2020-11-06这天访问过, 返回0说明没有访问过
    3. bitcount
      1. 格式:bitcount<key>[start end] 统计字符串从start字节到end字节比特值为1的数量
      2. 案例:计算2022-11-06这天的独立访问用户数量
    4. bitop :有四种操作 AND OR XOP NOT 与 或 非 异或 非
      1. AND:
        • 命令格式:BITOP AND destkey key [key ...]
        • 说明:对一个或多个二进制字符串进行按位与操作,并将结果保存到一个目标key中。所有输入的二进制字符串都必须具有相同的长度。
        • 实例:获取两天里面,都访问的用户id
      2. OR操作:
        • 命令格式:BITOP OR destkey key [key ...]
        • 说明:对一个或多个二进制字符串进行按位或操作,并将结果保存到一个目标key中。所有输入的二进制字符串都必须具有相同的长度。
        • 实例:统计出这段时间访问过网站的用户id有哪些
      3. XOR操作:
        • 命令格式:BITOP XOR destkey key [key ...]
        • 说明:对一个或多个二进制字符串进行按位异或操作,并将结果保存到一个目标key中。所有输入的二进制字符串都必须具有相同的长度。
      4. NOT操作:
        • 命令格式:BITOP NOT destkey key
        • 说明:对一个二进制字符串进行按位取反操作,并将结果保存到一个目标key中。
    5. Bitmap和set对比
      1. 存储空间节省很多(用户量很大的时候)

HyperLogLog

  1. 简介:
    • HyperLogLog是一种基数估计算法,用于统计大规模数据集中不重复元素的数量。它通过使用小量的空间来估计一个大集合的基数,同时保证高度的精度和可靠性。它的核心思想是通过哈希函数将元素映射到不同的桶中,进而通过观察这些桶的状态来估算基数。
    • HyperLogLog在实现上可以通过使用BitMap和Hash函数实现。BitMap用于记录每个桶的状态,Hash函数用于将元素映射到桶中。它的计算精度可以通过调整BitMap的长度和Hash函数的选择来实现。HyperLogLog算法的复杂度很低,对于大规模数据集可以在保证较高精度的情况下使用较小的空间进行计算。常见的应用场景包括大规模数据的去重统计、UV统计等。
  2. 命令:
    1. pfadd:添加
      • 格式:pfadd <key>< element> [element …]
      • 添加指定元素到 HyperLogLog 中
    2. pfcount:计数
      • 格式:pfcount<key> [key …]
      • 计算HLL的近似基数,可以计算多个HLL,比如用HLL存储每天的UV,计算一周的UV可以使用7天的UV合并计算即可
    3. pfmerge :合并
      • 格式:pfmerge<destkey><sourcekey> [sourcekey …]
      • 将一个或多个HLL合并后的结果存储在另一个HLL中,比如每月活跃用户可以使用每天的活跃用户来合并计算可得

Goespatial

  1. 简介:一个地理信息工具,维护经纬度,计算两点之间的距离,以及在范围里的城市。
  2. 命令:
    1. geoadd
      • 格式:geoadd\\<key>< longitude>\\<latitude>\\<member> [longitude latitude member...]
      • 添加地理位置(经度,纬度,名称)
      • 实例: geoadd china:city 106.50 29.53 chongqing 114.05 22.52 shenzhen 116.38 39.90 beijing
    2. geopos:
      • 格式:geopos <key><member> [member…]
      • 获得指定地区的坐标值
    3. geodist:
      • 格式:geodist\\<key>\\<member1>\\<member2> [m|km|ft|mi ]
      • 获取两个位置之间的直线距离
    4. georadius:
      • 格式:georadius<key>< longitude><latitude>radius m|km|ft|mi
      • 以给定的经纬度为中心,找出某一半径内的元素

Jedis操作Redis6

测试基本数据类型

public class JedisDemo1 {private final static Jedis jedis = new Jedis("127.0.0.1",6379);public static void main(String[] args) {//  创建jedis对象Jedis jedis = new Jedis("127.0.0.1",6379);//  测试System.out.println(jedis.ping());}@Beforepublic void init(){System.out.println("Redis init..."+jedis.ping());}//String  操作key@Testpublic void TestString(){//  添加数据jedis.set("name","parzival");//  获取String name = jedis.get("name");System.out.println("name:"+name);//  msetjedis.mset("k1","v1","k2","v2");String[] strings = {"k1", "k2", "name"};//  可以单独入参jedis.mget("k1","k2","name");//  也可以输入stringSystem.out.println(jedis.mget(strings));Set<String> keys = jedis.keys("*");for(String key: keys){System.out.println(key);}}//  测试List@Testpublic void TestList(){jedis.lpush("key1","lucy","mary","jack");List<String> key1 = jedis.lrange("key1", 0, -1);System.out.println(key1);}//  测试set@Testpublic void TestSet(){jedis.sadd("sname","lucy","mary","jack");Set<String> name = jedis.smembers("sname");System.out.println(name);}//  测试hash@Testpublic void TestHash(){//  直接添加jedis.hset("users","age","20");//  使用map添加Map<String,String> mp = new HashMap();mp.put("id","10086");jedis.hset("users",mp);Set<String> hkeys = jedis.hkeys("users");System.out.println(hkeys);}//  测试zset@Testpublic void TestZset(){//  直接添加jedis.zadd("china",100d,"beijing");//  map添加Map<String,Double> mp = new HashMap<>();mp.put("hangzhou",600d);mp.put("shanghai",200d);mp.put("shenzhen",300d);jedis.zadd("china",mp);Set<String> china = jedis.zrange("china", 0, -1);System.out.println(china);}@Afterpublic void close(){jedis.close();}}

Jedis实例:手机验证码

要求:

  1. 输入手机号,点击发送后随机生成6位数字码,2分钟有效
  2. 输入验证码,点击验证,返回成功或失败
  3. 每个手机号每天只能输入3次
public class VerificationCode {private final static String phoneNum = "1591234567";private final static String countKey = "VerifyCode" + phoneNum + ":count";private final static String codeKey = "VerifyCode" + phoneNum + ":code";private final static Jedis jedis = new Jedis("127.0.0.1", 6379);public static void main(String[] args) {System.out.println(getCode());//verifyCode(phoneNum);getRedisCode(phoneNum, "");}@Testpublic void SendCode() {verifyCode(phoneNum);System.out.println("验证码:" + jedis.get(codeKey));}@Testpublic void testCode() {getRedisCode(phoneNum, "968009");}@Testpublic void testKeys() {Jedis jedis = new Jedis("127.0.0.1", 6379);System.out.println(jedis.keys("*"));System.out.println("验证码:" + jedis.get(codeKey));jedis.close();}//  获取验证码public static String getCode() {return Integer.toString((int) (Math.random() * 1000000));}//  发送验证码public static void verifyCode(String phoneNum) {Jedis jedis = new Jedis("127.0.0.1", 6379);String count = jedis.get(countKey);if (count == null) {//  取不到值,说明第一次,设置为1jedis.setex(countKey, 24 * 60 * 60, "1");} else if (Integer.parseInt(count) <= 2) {jedis.incr(countKey);} else if (Integer.parseInt(count) > 2) {System.out.println("今天验证码次数用完");jedis.close();return;}//  把验证码存入redisString vcode = getCode();jedis.setex(codeKey, 120, vcode);jedis.close();}//  验证码校验public static void getRedisCode(String phoneNum, String code) {Jedis jedis = new Jedis("127.0.0.1", 6379);String redisCode = jedis.get(codeKey);if (redisCode.equals(code)) {System.out.println("success!");} else {System.out.println("fail");}jedis.close();}
}

SpringBoot整合Redis

  1. 依赖&配置:
    • <!-- redis -->
      <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-redis</artifactId>
      </dependency><!-- spring2.X集成redis所需common-pool2-->
      <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-pool2</artifactId>
      <version>2.6.0</version>
      </dependency>
      
    • #Redis服务器地址
      spring.redis.host=127.0.0.1
      #Redis服务器连接端口
      spring.redis.port=6379
      #Redis数据库索引(默认为0)
      spring.redis.database= 0
      #连接超时时间(毫秒)
      spring.redis.timeout=1800000
      #连接池最大连接数(使用负值表示没有限制)
      spring.redis.lettuce.pool.max-active=20
      #最大阻塞等待时间(负数表示没限制)
      spring.redis.lettuce.pool.max-wait=-1
      #连接池中的最大空闲连接
      spring.redis.lettuce.pool.max-idle=5
      #连接池中的最小空闲连接
      spring.redis.lettuce.pool.min-idle=0
      
  2. 添加配置类
    • @EnableCaching
      @Configuration
      public class RedisConfig extends CachingConfigurerSupport {@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {RedisTemplate<String, Object> template = new RedisTemplate<>();RedisSerializer<String> redisSerializer = new StringRedisSerializer();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);template.setConnectionFactory(factory);//key序列化方式template.setKeySerializer(redisSerializer);//value序列化template.setValueSerializer(jackson2JsonRedisSerializer);//value hashmap序列化template.setHashValueSerializer(jackson2JsonRedisSerializer);return template;}@Beanpublic CacheManager cacheManager(RedisConnectionFactory factory) {RedisSerializer<String> redisSerializer = new StringRedisSerializer();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);// 配置序列化(解决乱码的问题),过期时间600秒RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(600)).serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer)).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)).disableCachingNullValues();RedisCacheManager cacheManager = RedisCacheManager.builder(factory).cacheDefaults(config).build();return cacheManager;}
      }
      
    • 注意,新的配置类加进去之后,还需要添加依赖,不然不支持:
      <dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.10.0</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-beans</artifactId><version>5.2.1.RELEASE</version></dependency>
      
  3. 创建controller进行访问:
    • @RestController
      @RequestMapping("/redisTest")
      public class RedisTestController {@Autowiredprivate RedisTemplate redisTemplate;@GetMapping("/redis")public String testRedis(){//  设置值到redisredisTemplate.opsForValue().set("name","Michael");//  获取值return (String)redisTemplate.opsForValue().get("name");}}
      

Redis事务和锁机制

  1. 和MySQL里面的事务不是一回事
  2. 定义:
    • Redis事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
    • Redis事务的主要作用就是**串联多个命令防止别的命令插队**。