本文目录一览:
4、Redis高性能的根本原理
内存的读写速度很快
Epoll 模型
常用的五大Redis的数据结构,及他们各自的底层实现结构
string hash list set sortset(zset)
string 的底层实现是 简单动态字符串(SDS -simple dynamic string)
hash 的底层实现是 hash表 或则 压缩列表(ziplist)
list 的底层实现是 双向列表(quicklist) 或者 压缩列表
set 的底层实现是 hash表(hashtable) 或者 整数数组
sortset(zset) 的底层实现是 压缩列表 或者 跳表
各个数据结构的底层实现概览
value是 string 类型的时候分为三种情况
(1)、当设置的值是整数类型的时候,redis底层会将 string 类型转化为 int 来存储
(2)、设置的值小于等于44个字节的时候,使用的编码是 embstr
(3)、设置的值大于44个字节的时候,使用的编码是 raw
redis是用C语言编写的,在C语言中 string 类型是用字符数组 char[] 来实现的。redis实现字符串的底层并没有直接使用C语言中的字符数组的形式,而是进行了改造,构造出了一种SDS的数据结构
list的底层使用 快速双向链表quicklist 或者 压缩链表ziplist 来实现的。
list的底层并没有使用传统的双向链表的结构是因为
(1)、双向链表需要有一个 前指针 和 后指针 ,每个指针占用的空间分别都是8byte, 占用内存 比较多
(2)、双向链表所通用的一个问题是会形成很多的 内存碎片
压缩链表 ziplist 结构是
快速双向链表 quicklist 结构
hash的底层实现为 hashtable 或者 ziplist 。
hashtable的底层实现
当数据量比较小或者单个元素的时候,底层使用的是ziplist存储,具体可以通过配置来制定
1、 hashtable 是无序的 ziplist 是有序的
2、在能使用 hash 的情况下优先使用 hash ,不要使用 String ,因为使用太多的 String ,则会创建出过多的 key ,当 key 大量的时候,就会容易发生 hash碰撞 ,所以就需要频繁的 rehash ,每次 rehash 就会创建2倍的内存,造成内存浪费
hash的底层实现为 整数数组intset 或者 hashtable 。
当set都为整数的时候,set的底层实现都是使用 intset 结构实现
如果set中存在字符串的值,则使用 hashtable 来实现
intset 是有序的, hashtable 是无序的
sortset 底层使用 压缩列表ziplist 或 跳表skiplist 的结构实现
当数据量小的情况下,使用 ziplist 实现,当数据量大的情况下使用 ziplist 实现,具体可以通过配置设置
默认设置下的底层结构
skiplist 的底层实现
查找对应元素的时候,先从最高的索引层找,例如找c 150,则先从L1找,L1的指针指向b,查看b120小于150,则继续往后找,b的指针指向null,则向下一层找,向下一层b的指针指向c,查看c的score为150,所以找到对应的元素c
1、
redis是使用c语言开发的么
Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库。
四个大点,搞懂 Redis 到底快在哪里?
现在我们都用高级语言来编程,比如Java、python等。也许你会觉得C语言很古老,但是它真的很有用,毕竟unix系统就是用C实现的,所以C语言是非常贴近操作系统的语言。Redis就是用C语言开发的,所以执行会比较快。
Redis将所有数据放在内存中,非数据同步正常工作中,是不需要从磁盘读取数据的,0次IO。内存响应时间大约为100纳秒,这是Redis速度快的重要基础。先看看CPU的速度:
拿我的电脑来说,主频是3.1G,也就是说每秒可以执行3.1*10^9个指令。所以说CPU看世界是非常非常慢的,内存比它慢百倍,磁盘比他慢百万倍,你说快不快?
借了一张《深入理解计算机系统》的图,展示了一个典型的存储器层次结构,在L0层,CPU可以在一个时钟周期访问到,基于SRAM的高速缓存春续期,可以在几个CPU时钟周期访问到,然后是基于DRAM的主存,可以在几十到几百个时钟周期访问到他们。
第一,单线程简化算法的实现,并发的数据结构实现不但困难且测试也麻烦。第二,单线程避免了线程切换以及加锁释放锁带来的消耗,对于服务端开发来说,锁和线程切换通常是性能杀手。当然了,单线程也会有它的缺点,也是Redis的噩梦: 阻塞。如果执行一个命令过长,那么会造成其他命令的阻塞,对于Redis是十分致命的 ,所以Redis是面向快速执行场景的数据库。
除了Redis之外,Node.js也是单线程,Nginx也是单线程,但他们都是服务器高性能的典范。
在这之前先要说一下传统的阻塞I/O是如何工作的:当使用read或者write对某一文件描述符(File Descriptor FD)进行读写的时候,如果数据没有收到,那么该线程会被挂起,直到收到数据。阻塞模型虽然易于理解,但是在需要处理多个客户端任务的时候,不会使用阻塞模型。
I/O多路复用实际上是指多个连接的**管理可以在同一进程。**多路是指网络连接,复用只是同一个线程。在网络服务中,I/O多路复用起的作用是一次性把多个连接的事件通知业务代码处理,处理的方式由业务代码来决定。在I/O多路复用模型中,最重要的函数调用就是I/O 多路复用函数,该方法能同时监控多个文件描述符(fd)的读写情况,当其中的某些fd可读/写时,该方法就会返回可读/写的fd个数。
Redis使用epoll作为I/O多路复用技术的实现,再加上Redis自身的事件处理模型将epoll的read、write、close等都转换成事件,不在网络I/O上浪费过多的时间。实现对多个FD读写的监控,提高性能。
举个形象的例子吧。比如一个tcp服务器处理20个客户端socket。A方案:顺序处理,如果第一个socket因为网卡读数据处理慢了,一阻塞后面都玩蛋去。B方案:每个socket请求都创建一个分身子进程来处理,不说每个进程消耗大量系统资源,光是进程切换就够操作系统累的了。C方案**(I/O复用模型,epoll) :将用户socket对应的fd注册进epoll(实际上服务器和操作系统之间传递的不是socket的fd而是fd_set的数据结构),然后epoll只告诉哪些需要读/写的socket,只需要处理那些活跃的、有变化的socket fd的就好了。这样,整个过程只在调用epoll的时候才会阻塞,收发客户消息是不会阻塞的。
:-D 搜索微信号(ID: 芋道源码 ),可以获得各种 Java 源码解析、原理讲解、面试题、学习指南。
:-D 并且,回复【 书籍 】后,可以领取笔者推荐的各种 Java 从入门到架构的 100 本书籍。
:-D 并且,回复【 技术群 】后,可以加入专门讨论 Java、后端、架构的技术群。