Redis 基本特性1. 非关系型的键值对数据库 , 可以根据键以O(1) 的时间复杂度取出或插入关联值2. Redis 的数据是存在内存中的3. 键值对中键的类型可以是字符串 , 整型 , 浮点型等 , 且键是唯一的4. 键值对中的值类型可以是string , hash , list , set , sorted set 等5. Redis 内置了复制 , 磁盘持久化 , LUA脚本 , 事务 , SSL,ACLs , 客户端缓存 , 客户端代理等功能6. 通过Redis哨兵和Redis Cluster 模式提供高可用性
Redis高性能的原因1.图示(换算时间:1s =1000 ms , 1ms=1000 us , 1us =1000 ns):
文章插图
2.对于内存数据库来说 , 本身数据就存在于内存里 , 避免了磁盘 I/O 的限制 , 无疑访问速度会远大于磁盘数据库 。
3.其次Redis , 默认是采用一个线程执行指令任务的 , 既减少了线程上下文切换带来的开销 , 也避免并发问题 。
4.而且Redis中有多种数据类型 , 每种数据类型的底层都由一种或多种数据结构来支持 。正是因为有了这些数据结构 , Redis 在存储与读取上的速度才不受阻碍 。
深入底层C源码分析Redis1.Redis是基于键值对存储数据的 , 像我们平时会使用的时候很容易觉得Redis的键值是多种数据类型的 , 其实不然 , Redis的键值是String类型的 , 数据变成字节流(byte)基于网络传输的过程 , 传到Redis服务转成SDS(Simple Dynamic String【简单动态字符串】) String(Redis自定义的数据类型) 。既然Redis是基于C语言写的 , 那么为什么不用原生的?
//如果我们想存储字符串:mynameC: char data[]="myname\0"; //而C语言中对于字符串是默认采用\0作为结尾的而对于Redis , 它是面向多种语言的 , 对于传过来的数据是不可控的:如果传输的视频流或者音频的流文件 , 大概率会出现"name\0orxxx"这种那么C语言只能读到“name”这部分遇到“\0” , 则会视为结束了 。(这明显是不合适 , 容易导致数据丢失)故 , Redis采用sds结构:struct sdshdr {int len;//存储的长度int free;//剩余的空闲空间char buf[]; //数据存储的地方};这种数据结构的好处是:1.对于存储数据的准确性更高了 , 依靠len字段来标明准确数据的位置 。【二进制安全的数据结构】2.采用以空间换时间的方式 , 每次扩容的时候可以适当分配大一点的空间 , 记录剩余时间是否够下一次的修改或者追加 。(减少对象的销毁与创建的步骤)【提供了内存预分配机制 , 避免了频繁的内存分配】3.会在数据末尾依旧采用\0作为结尾【兼容C语言的函数库】说明:
Redis自定义sdshdr数据结构具备三大特性:
【1】二进制安全的数据结构
【2】提供了内存预分配机制 , 避免了频繁的内存分配
【3】兼容C语言的函数库
2.String类型的数据结构1)代码展示
//redis 3.2 以前struct sdshdr {int len;int free;char buf[];};//redis 3.2 后//redis\deps\hiredis\sds.h文件typedef char *sds;//存在注释:sdshdr5 is never used, we just access the flags byte directly.However is here to document the layout of type 5 SDS strings.//意思大概是:sdshdr5从未使用过 , 我们只是直接访问标志字节 。然而 , 这里是为了记录类型5 SDS字符串的布局struct __attribute__ ((__packed__)) sdshdr5 {// 对应的字符串长度小于 1<<5unsigned char flags;char buf[];};//__attribute__ ((packed)) 的作用就是告诉编译器取消结构体在编译过程的优化对齐 , 按照实际占用字节数进行对齐struct __attribute__ ((__packed__)) sdshdr8 { // 对应的字符串长度小于 1<<8uint8_t len;//目前字符串的长度uint8_t alloc;//分配的内存总长度unsigned char flags;//flag用3bit来标明类型 , 类型后续解释 , 其余5bit目前没有使用char buf[];//柔性数组 , 以'\0'结尾};struct __attribute__ ((__packed__)) sdshdr16 { // 对应的字符串长度小于 1<<16uint16_t len;uint16_t alloc;unsigned char flags;char buf[];};struct __attribute__ ((__packed__)) sdshdr32 { // 对应的字符串长度小于 1<<32uint32_t len;uint32_t alloc;unsigned char flags;char buf[];};struct __attribute__ ((__packed__)) sdshdr64 { // 对应的字符串长度小于 1<<64uint64_t len;uint64_t alloc;unsigned char flags;char buf[];};#define SDS_TYPE_50#define SDS_TYPE_81#define SDS_TYPE_16 2#define SDS_TYPE_32 3#define SDS_TYPE_64 4static inline char sdsReqType(size_t string_size) {if (string_size < 1<<5)return SDS_TYPE_5;if (string_size < 1<<8)return SDS_TYPE_8;if (string_size < 1<<16)return SDS_TYPE_16;#if (LONG_MAX == LLONG_MAX)if (string_size < 1ll<<32)return SDS_TYPE_32;return SDS_TYPE_64;#elsereturn SDS_TYPE_32;#endif}
经验总结扩展阅读
- 七 Netty 学习:NioEventLoop 对应线程的创建和启动源码说明
- Spring mvc源码分析系列--Servlet的前世今生
- spring cron表达式源码分析
- 集合框架——LinkedList集合源码分析
- 含源码 手把手教你使用LabVIEW OpenCV DNN实现手写数字识别
- 补充部分---ScheduledThreadPoolExecutor类分析 线程池底层原理详解与源码分析
- 五 Netty 学习:服务端启动核心流程源码说明
- HashMap底层原理及jdk1.8源码解读
- 含源码 手把手教你使用LabVIEW人工智能视觉工具包快速实现传统Opencv算子的调用
- 含源码 手把手教你使用LabVIEW人工智能视觉工具包快速实现图像读取与采集