- 4、Redis新增的数据类型
- 4.1 Bitmaps
- 4.1.1 基本命令
- 4.1.2 实例
- 4.1.3 Bitmaps和set的对比
- 4.2 HyperLogLog
- 4.2.1 基本命令
- 4.2.2 实例
- 4.3 Geospatial
- 4.3.1 基本命令
- 4.3.2 实例
- 5、Jedis操作redis
- 5.1 Jedis-String
- 5.2 Jedis-List
- 5.3 Jedis-Set
- 5.4 Jedis-Hash
- 5.5 Jedis-Zset
- 6、Springboot整合Redis
- 1、pom依赖
- 2、配置config
- 3、测试redisTemplate
- 4、配置RedisUtil类,对redisTemplate进行封装
接上文:Redis入门-----五种基本数据类型
4、Redis新增的数据类型 4.1 BitmapsRedis提供了Bitmaps这个“数据类型”可以实现对二进制位的操作:
-
Bitmaps本身不是一种数据类型, 实际上它就是字符串(key-value) , 但是它可以对字符串的位进行操作。
-
Bitmaps单独提供了一套命令, 所以在Redis中使用Bitmaps和使用字符串的方法不太相同。 可以把Bitmaps想象成一个以位为单位的数组, 数组的每个单元只能存储0和1, 数组的下标在Bitmaps中叫做偏移量
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BcWJWK5T-1639188520085)(C:UsersBanditDesktopnotenote.assetsimage-20211210100111428.png)]
setbit4.1.2 实例#设置Bitmaps中某个偏移量的值(0或1) getbit #获取Bitmaps中某个偏移量的值 bitcount [start end] #统计字符串从start字节到end字节比特值为1的数量 bitop and(or/not/xor) [key…] #bitop是一个复合操作,它可以做多个Bitmaps的and(交集) 、 or(并集)not(非) xor(异或) 操作并将结果保存在destkey中。
①每个独立用户是否访问过网站存放在Bitmaps中, 将访问的用户记做1, 没有访问的用户记做0, 用偏移量作为用户的id。
设置键的第offset个位的值(从0算起) , 假设现在有20个用户,userid=1,
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pLqr7e4g-1639188520086)(C:UsersBanditDesktopnotenote.assetsimage-20211210101006955.png)]
unique:users:20201106代表2020-11-06这天的独立访问用户的Bitmaps
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MZHlIq4T-1639188520087)(C:UsersBanditDesktopnotenote.assetsimage-20211210100953625.png)]
注意:很多应用的用户id以一个指定数字(例如10000) 开头, 直接将用户id和Bitmaps的偏移量对应势必会造成一定的浪费, 通常的做法是每次做setbit操作时将用户id减去这个指定数字。
在第一次初始化Bitmaps时, 假如偏移量非常大, 那么整个初始化过程执行会比较慢, 可能会造成Redis的阻塞。
②获取id=8的用户是否在2020-11-06这天访问过, 返回0说明没有访问过:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BCVKXU5r-1639188520087)(C:UsersBanditDesktopnotenote.assetsimage-20211210103515062.png)]
③计算2022-11-06这天的独立访问用户数量
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dJOBzruh-1639188520088)(C:UsersBanditDesktopnotenote.assetsimage-20211210103603953.png)]
④bitop复合操作:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d9lKKmPr-1639188520089)(C:UsersBanditDesktopnotenote.assetsimage-20211210103922744.png)]
4.1.3 Bitmaps和set的对比1、假设网站有1亿用户, 每天独立访问的用户有5千万, 如果每天用集合类型和Bitmaps分别存储活跃用户可以得到表
set和Bitmaps存储一天活跃用户对比
| 数据类型 | 每个用户id占用空间 | 需要存储的用户量 | 全部内存量 |
| 集合类型 | 64位 | 50000000 | 64位*50000000 = 400MB |
| Bitmaps | 1位 | 100000000 | 1位*100000000 = 12.5MB |
set和Bitmaps存储独立用户空间对比
| 数据类型 | 一天 | 一个月 | 一年 |
| 集合类型 | 400MB | 12GB | 144GB |
| Bitmaps | 12.5MB | 375MB | 4.5GB |
2、 假如该网站每天的独立访问用户很少, 例如只有10万(大量的僵尸用户) , 那么两者的对比如下表所示, 很显然, 这时候使用Bitmaps就不太合适了, 因为基本上大部分位都是0。
set和Bitmaps存储一天活跃用户对比(独立用户比较少)
| 数据类型 | 每个userid占用空间 | 需要存储的用户量 | 全部内存量 |
| 集合类型 | 64位 | 100000 | 64位*100000 = 800KB |
| Bitmaps | 1位 | 100000000 | 1位*100000000 = 12.5MB |
处理基数问题:求集合中不重复元素个数的问题称为基数问题。
解决基数问题有很多种方案:
(1)数据存储在MySQL表中,使用distinct count计算不重复个数
(2)使用Redis提供的hash、set、bitmaps等数据结构来处理
以上的方案结果精确,但随着数据不断增加,导致占用空间越来越大,对于非常大的数据集是不切实际的。
HyperLogLog是降低精度来平衡存储空间的
优点:在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。
4.2.1 基本命令pfadd4.2.2 实例< element> [element ...] #添加指定元素到 HyperLogLog 中 pfcount [key ...] #计算HLL的近似基数,可以计算多个HLL pfmerge [sourcekey ...] #将一个或多个HLL合并后的结果存储在另一个HLL中
①将所有元素添加到指定HyperLogLog数据结构中时:
- 如果执行命令后HLL估计的近似基数发生变化,则返回1
- 否则返回0。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vNgIbMhQ-1639188520090)(C:UsersBanditDesktopnotenote.assetsimage-20211210132522089.png)]
②用HLL存储每天的UV,计算一周的UV可以使用7天的UV合并计算即可(计算每一个合并返回)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xLbynL8l-1639188520091)(C:UsersBanditDesktopnotenote.assetsimage-20211210132622048.png)]
③每月活跃用户可以使用每天的活跃用户来合并计算可得(计算每一个合并到最后一个返回最后一个)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FkXjGAsX-1639188520091)(C:UsersBanditDesktopnotenote.assetsimage-20211210132638752.png)]
4.3 GeospatialGEO,Geographic,地理信息的缩写。该类型,就是元素的2维坐标,在地图上就是经纬度。redis基于该类型,提供了经纬度设置,查询,范围查询,距离查询,经纬度Hash等常见操作。
4.3.1 基本命令#添加地理位置(经度,纬度,名称),无法添加南北极, geoadd4.3.2 实例[longitude latitude member...] #获得指定地区的坐标值 geopos [member...] #获取两个位置之间的直线距离 geodist [m|km|ft|mi ] #以给定的经纬度为中心,找出某一半径内的元素 georadius < longitude> radius m|km|ft|mi
①添加:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HstpxlDU-1639188520092)(C:UsersBanditDesktopnotenote.assetsimage-20211210144147174.png)]
②获取:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yqpfzj5Y-1639188520093)(C:UsersBanditDesktopnotenote.assetsimage-20211210144200925.png)]
③计算直线距离:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zfgIwMJy-1639188520094)(C:UsersBanditDesktopnotenote.assetsimage-20211210144217375.png)]
④找出半径内元素
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u5KgtyiS-1639188520094)(C:UsersBanditDesktopnotenote.assetsimage-20211210144244613.png)]
5、Jedis操作redis
需要导入的包:
redis.clients jedis 3.7.0
基本用法:
private static final String host = "xx.xx.xx.xx";
private static final String password = "xxxxxx";
public void jedisDemo(){
Jedis jedisConnection = new Jedis(host,6379);//建立连接
jedisConnection.auth(password); //密码验证
//....
jedisConnection.close(); //用完后要关闭连接
}
以下操作均可在第③节基本操作找到对应,仅演示部分
5.1 Jedis-String@Test
public void jedisString(){
Jedis jedisConnection = new Jedis(host,6379);
jedisConnection.auth(password);
jedisConnection.flushDB();
//set
jedisConnection.set("k1", "v1");
System.out.println(jedisConnection.get("k1"));
//append
jedisConnection.append("k1","v2");
System.out.println(jedisConnection.get("k1"));
//mset
jedisConnection.mset("k11","v11","k22","v22","k33","v33");
System.out.println(jedisConnection.keys("*"));
//mget
List mget = jedisConnection.mget("k11", "k22", "k33", "k1");
System.out.println(mget);
jedisConnection.close();
}
5.2 Jedis-List
@Test
public void jedisList(){
Jedis jedisConnection = new Jedis(host,6379);
jedisConnection.auth(password);
jedisConnection.flushDB();
jedisConnection.lpush("k1","1","2","3","4","5");
System.out.println(jedisConnection.lrange("k1",0,-1));
jedisConnection.rpush("k2","1","2","3","4","5");
System.out.println(jedisConnection.lrange("k2",0,-1));
jedisConnection.rpoplpush("k1","k2");
System.out.println(jedisConnection.lrange("k1",0,-1));
System.out.println(jedisConnection.lrange("k2",0,-1));
jedisConnection.linsert("k2", ListPosition.AFTER,"1","插入");
System.out.println(jedisConnection.lrange("k2",0,-1));
jedisConnection.close();
}
5.3 Jedis-Set
@Test
public void jedisSet(){
Jedis jedisConnection = new Jedis(host,6379);
jedisConnection.auth(password);
jedisConnection.flushDB();
jedisConnection.sadd("k1","s1","s2","s3","s4","s5");
jedisConnection.sadd("k2","s2","s6","s10","s3");
System.out.println("k1:"+jedisConnection.smembers("k1"));
System.out.println("k1随机两个数:"+jedisConnection.srandmember("k1", 2));
System.out.println("交集"+jedisConnection.sinter("k1", "k2"));
System.out.println("并集"+jedisConnection.sunion("k1", "k2"));
System.out.println("差集"+jedisConnection.sdiff("k1", "k2"));
jedisConnection.smove("k1","k2","s5");
System.out.println("k1:"+jedisConnection.smembers("k1"));
System.out.println("k2:"+jedisConnection.smembers("k2"));
jedisConnection.close();
}
5.4 Jedis-Hash
@Test
public void jedisHash(){
Jedis jedisConnection = new Jedis(host,6379);
jedisConnection.auth(password);
jedisConnection.flushDB();
HashMap map = new HashMap<>();
map.put("name","zhangbing");
map.put("age","20");
jedisConnection.hset("user1",map);
jedisConnection.hset("user2","name","wangwu");
jedisConnection.hset("user2","age","17");
System.out.println(jedisConnection.hget("user1", "age"));
System.out.println(jedisConnection.hvals("user2"));
jedisConnection.close();
}
5.5 Jedis-Zset
@Test
public void jedisZSet(){
Jedis jedisConnection = new Jedis(host,6379);
jedisConnection.auth(password);
jedisConnection.flushDB();
HashMap map = new HashMap<>();
map.put("java", 100.0);
map.put("redis",200.0);
map.put("mysql",300.0);
jedisConnection.zadd("zkey1",map);
System.out.println(jedisConnection.zrange("zkey1", 0, -1));
System.out.println(jedisConnection.zrangeWithScores("zkey1", 0, -1));
System.out.println(jedisConnection.zrangeByScore("zkey1", 1.0, 200.0));
System.out.println(jedisConnection.zrank("zkey1", "java"));
jedisConnection.zincrby("zkey1",500,"java");
System.out.println(jedisConnection.zrank("zkey1", "java"));
jedisConnection.close();
}
6、Springboot整合Redis
1、pom依赖
2、配置configorg.springframework.boot spring-boot-starter-data-redis 2.5.4 redis.clients jedis
通过springboot-autoconfigre包里面找到RedisAutoConfigration。仿造他的来配redisTemplate
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iuSYlwkE-1639188520095)(C:UsersBanditDesktopnotenote.assetsimage-20211211092721841.png)]
@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
@Bean
@Primary
public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
RedisTemplate template = new RedisTemplate<>();
RedisSerializer 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);过期,用下方这条语句替代
om.activateDefaultTyping(LaissezFaireSubTypevalidator.instance,ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
jackson2JsonRedisSerializer.setObjectMapper(om);
template.setConnectionFactory(factory);
//key序列化方式
template.setKeySerializer(redisSerializer);
//value序列化
template.setValueSerializer(jackson2JsonRedisSerializer);
//value hashmap序列化
template.setHashValueSerializer(jackson2JsonRedisSerializer);
return template;
}
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer redisSerializer = new StringRedisSerializer();
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.activateDefaultTyping(LaissezFaireSubTypevalidator.instance,ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
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;
}
}
3、测试redisTemplate
- 需要将redisTemplate注入进来
- redisTemplate.opsFor[XXX]---->就是对redis各种数据类型操作的封装
@SpringBootTest
class MyspringbootApplicationTests {
@Autowired
RedisTemplate redisTemplate;
@Test
void contextLoads() {
Department department = new Department(1, "A部门");
redisTemplate.opsForValue().set("k1","v1");
redisTemplate.opsForList().rightPush("departKey",department);
System.out.println(redisTemplate.opsForValue().get("k1"));
System.out.println(redisTemplate.opsForList().range("departKey", 0, -1));
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m7e2Yo0C-1639188520096)(C:UsersBanditDesktopnotenote.assetsimage-20211211092959802.png)]
4、配置RedisUtil类,对redisTemplate进行封装@Component
public final class RedisUtil {
@Autowired
private RedisTemplate redisTemplate;
// =============================common============================
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;
}
}
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
@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) CollectionUtils.arrayToList(key));
}
}
}
// ============================String=============================
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return 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;
}
}
public long incr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递增因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
public long decr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递减因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, -delta);
}
// ================================Map=================================
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}
public Map 


