Redis的二进制安全
概念
只要一个二进制数据赋予了它本身数值以外的含义,就是二进制不安全的,比如:/0 对应的数字是0,它本来是0,但是C语言把这个0作为结束符进行使用,有了别的含义,因此C为数据不安全。
Redis二进制安全
C字符串中的字符必须符合某种编码(比如ASCII),并且除了字符串的末尾之外,字符串里面不能包含空字符,否则最先被程序读入的空字符将被误认为是字符串结尾,这些限制使得C字符串只能保存文本数据,而不能保存像图片、音频、视频、压缩文件这样的二进制数据。
举个例子,如果有一种使用空字符来分割多个单词的特殊数据格式,如图所示,那么这种格式就不能使用C字符串来保存,因为C字符串所用的函数只会识别出其中的"Redis",而忽略之后的"Cluster"。
虽然数据库一般用于保存文本数据,但使用数据库来保存二进制数据的场景也不少见,因此,为了确保Redis可以适用于各种不同的使用场景,SDS(simple dynamid string )的 API都是二进制安全的(binary-safe),所有SDS API都会以处理二进制的方式来处理SDS存放在buf数组里的数据,程序不会对其中的数据做任何限制、过滤、或者假设,数据在写入时是什么样的,它被读取时就是什么样。
这也是我们将SDS的buf属性称为字节数组的原因——Redis不是用这个数组来保存字符,而是用它来保存一系列二进制数据。
例如,使用SDS来保存之前提到的特殊数据格式就没有任何问题,因为SDS使用len属性的值而不是空字符来判断字符串是否结束,如图所示。
通过使用二进制安全的SDS,而不是C字符串,使得Redis不仅可以保存文本数据,还可以保存任意格式的二进制数据。
Redis的单线程模型
概念
当Redis接受到多个并发请求的时候,redis会把这多个并发请求存到一个队列当中去,然后按照队列的先后顺序,一个一个的执行命令,也就是redis同一时刻只执行一个命令,只有这一个命令执行完了才会继续执行下一个任务。所以redis中的incr和decr是绝对的原子操作,绝对的线程安全,因此尽管redis是单线程模型的,但是性能还是很高。
参照上图,简单来说,就是。我们的redis-client在操作的时候,会产生具有不同事件类型的socket。在服务端,有一段I/0多路复用程序,将其置入队列之中。然后,文件事件分派器,依次去队列中取,转发到不同的事件处理器中。
需要说明的是,这个I/O多路复用机制,redis还提供了select、epoll、evport、kqueue等多路复用函数库,大家可以自行去了解。
Redis作为单线程模型为什么性能还是那么高?
- 纯内存访问:数据存在内存当中,内存的响应时间是及快的。
- 非阻塞式I/O操作:redis采用epoll作为I/O多路复用技术的实现解放CPU,达到0拷贝。
- 采用单线程自然也就避免了线程上下文切换(就是说A线程执行到一半,线程B抢夺到CPU了,此时A线程则会将该时刻记录下来,从而去执行B线程,当B线程执行完了之后,还要切换到A线程继续执行,此时这个线程切换会影响性能的。)和锁带来的开销。