Redis是什么?
redis是目前最受欢迎的NoSQL(非关系型)数据库之一,redis是一个使用ANSI C编写的开源、包含多种数据结构、支持网络、基于内存、可选持久性的键值对存储数据库。
对比传统数据库?
- C/S通讯模型
- 单进程单线程模型
- 丰富的数据类型
- 操作具有原子性
- 持久化
- 高并发读写
- 支持lua脚本
应用场景?
- 缓存系统
- 计数器
- 消息队列
- 排行榜
- 社交网络
- 实时系统
支持的数据类型?
String、Hash、List、Set、Zset
Redis内存模型?
1. 内存统计
查看内存使用情况:info memory
2. 内存划分
数据
进程本身运行需要的内存
缓冲内存
内存碎片
3. 数据存储
涉及到内存分配器(如:jemalloc),简单动态字符串(SDS),5中对象类型以及内部编码,redisObject。
set hello world涉及到的数据类型:
- dictEntry: 里面存储了指向key和value的指针,next指向下一个dictEntry。
- Key: 就是对应的hello, 并不是直接以字符串存储,而是存储在SDS结构中。
- redisObject: value并不是以直接字符串的形式存储,也不是存储在SDS结构中,而是存储在redisObject中,
- jemalloc:无论上面哪个对象,都是需要内存分配器来分配内存进行存储,
相关名词讲解:
1. jemalloc
2. redisObject
3. SDS
5中数据类型内部编码
字符串
内部编码有3种:int embstr raw
embstr: <=39个字节的字符串(redis 3.2版本以后是44个字节)
只分配一次内存空间,redisObject和sds是连续的内存,查询效率会快很多。缺点是字符串增加的时候,需要重新分配内存,导致整个redisObject和sds都需要重新分配内存,所以embstr的实现只允许读,如果对其进行修改,那么就会变成raw编码了。
raw:大于44个字节
可以分配两次内存空间
问题:为什么在3.2版本以前是39字节?
embstr是连续的内存区域, 由redisObject和sdsshdr组成。
struct RedisObject {
int4 type; // 4bits,不同的redis对象会有不同的数据类型(string、list、hash等),type记录类
型,会用到4bits。
int4 encoding; // 4bits,存储编码形式,用4bits。
int24 lru; // 24bits,用24bits记录对象的LRU信息
int32 refcount; // 4bytes = 32bits,引用计数器,用到32bits
void *ptr; // 8bytes,64-bit system,指针指向对象的具体内容,需要64bits
}
计算完:128bits = 16bytes
struct sdshdr {
unsigned int len;//4个字节
unsigned int free;//4个字节
char buf[];//假设buf里面是39个字节
};
if (ptr) {
memcpy(sh->buf,ptr,len);
sh->buf[len] = '\0';//一个字节
计算完:48个字节
综上:embstr = 16 + 48 = 64 (jemalloc来分配)
问题:为什么分界值由39字节会变成44字节?
https://my.oschina.net/sjyz/blog/3276075
列表 list
用来存储多个有序的字符串,支持两端插入和弹出
内部编码:压缩列表(ziplist) 双端链表(linkedlist)
哈希
内部编码:压缩列表(ziplist) 哈希表(hashtable)
hashtable: 由一个dict结构、2个dictht结构、一个dictEntry指针数组和多个dictEntry结构组成。
集合 set
与列表的区别就是集合里面的元素是无序的,集合中的元素不能有重复
内部编码:整数集合(intset)哈希表(hashtable)
有序集合
内部编码:压缩列表(ziplist)或跳跃表(skiplist)
目前对于redis是什么、区别传统数据库、应用场景、数据类型、内存模型有了一定的了解。
接下来学习如何使用它。
Java客户端
查了redis官方文档,排第一的就是Redisson,那就它了,搞它!!!!
Redisson
引入依赖:
<!-- redis--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- redisson --> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.8.2</version> </dependency>
yml配置文件:
spring: redis: host: 127.0.0.1 database: 0 port: 6379 timeout: 300ms
redisson配置类
@Configuration
public class RedissonConfig {
@Autowired
private RedisProperties redisProperties;
@Bean
public RedissonClient getRedissonClient() {
Config config = new Config();
String redisUrl = String.format("redis://%s:%s", redisProperties.getHost() + "", redisProperties.getPort() + "");
config.useSingleServer().setAddress(redisUrl).setPassword(redisProperties.getPassword());
config.useSingleServer().setDatabase(3);
config.useSingleServer().setConnectionMinimumIdleSize(10);
return Redisson.create(config);
}
}
然后就可以在测试类中根据官网使用文档进行自测使用啦~~
首先布隆过滤器是什么?
判断一个元素是否存在于大数据量的集合中,(一定不存在、可能存在)因为布隆过滤器有一定的误差率
代码实现:
@Test
void test5() {
RBloomFilter<Object> phoneListFilter = redissonClient.getBloomFilter("phoneList");
//初始化布隆过滤器:预计元素为100000000L,误差率为3%
phoneListFilter.tryInit(100000000L, 0.03);
//将号码10086插入到布隆过滤器中
phoneListFilter.add("10086");
System.out.println(phoneListFilter.contains("123456"));
System.out.println(phoneListFilter.contains("10086"));
}
优点:底层是二进制组成的数组,占用内存极少,并且查询和插入速度很快
缺点:有 一定的误判率、无法删除数据(因为存在hash冲突,会同时删掉其他数据)
对于以上缺点有什么解决方案呢?
1. 首先如何解决误判率
a: 增加二进制位数组的长度,hash后数据会更加的离散化,降低冲突率
b: 增加hash次数,降低冲突概率
2. 如何删除过滤器里面的数据
a: 开发定时任务,每隔几个小时,自动创建一个新的布隆过滤器数组,替换老的,有点CopyOnWriteArrayList的味道
b: 布隆过滤器增加一个等长的数组,存储计数器,主要解决冲突问题,每次删除时对应的计数器减一,如果结果为0,更新主数组的二进制值为0