0.前置知识

0.1 线程上下文切换

由于现在大多计算机都是多核CPU,多线程往往会比单线程更快,因为多线程能充分利用CPU资源,从而更能够提高并发,但提高并发并不意味着启动更多的线程来执行,更多的线程意味着线程创建销毁开销加大、上下文非常频繁。

多任务系统往往需要同时执行多个任务,大多数情况执行任务数大于机器的CPU数,然而一颗CPU同时只能执行一项任务,为了模拟任务同时执行的效果,操作系统的设计者巧妙地利用了时间片轮转的方式。时间片是指每个线程都会被分配一个执行时间段,线程将在执行时间段中被调度执行。

线程上下文是指某一时间点 CPU 寄存器和程序计数器的内容,CPU通过时间片分配算法来循环执行任务(线程),因为时间片非常短,所以CPU通过不停地切换线程执行。CPU进行线程切换时会将当前任务的状态保存下来,以便下次切换回这个任务时可以再次加载这个任务的状态(每个线程都有一个程序计数器,用于表明指令序列中 CPU 正在执行的位置,以便于CPU切换到其他线程时无需从头执行),然后加载下一任务的状态并执行。任务的状态保存及再加载, 这段过程就叫做上下文切换。

上下文切换会导致额外的开销,常常表现为高并发执行时速度会慢串行,因此减少上下文切换次数便可以提高多线程程序的运行效率,上下文的切换消耗分为以下两种:

  • 直接消耗:指的是CPU寄存器需要保存和加载,系统调度器的代码需要执行,TLB实例需要重新加载,CPU 的pipeline需要刷掉。
  • 间接消耗:指的是多核的cache之间的共享数据, 间接消耗对于程序的影响要看线程工作区操作数据的大小。

    0.2 5种IO网络模型

1.Redis基础介绍?

Redis是一个基于C语言实现的内存存储的数据结构服务器,常用于非关系型数据库、高速缓存、消息队列代理。它支持字符串哈希表列表集合有序集合位图hyperloglogs等数据结构。内置复制、Lua脚本、LRU收回、事务以及不同级别磁盘持久化功能,同时通过Redis Sentinel提供高可用,通过Redis Cluster提供自动分区

1.1 Redis特性

  • 基于内存操作,性能极高。官方提供的基准测试单机Redis读的速度是110000次/s,写的速度是81000次/s。Redis虽然基于内存操作,也支持AOF、RDB、AOF+RDB混合三种持久化方案。
  • 支持丰富的数据结构。Redis不仅支持简单的Key-Value类型的数据,同时也支持Lists(列表)、Hashes(哈希)、Sets(集合)、Sorted sets(有序集合)、Bitmaps(位图)、Hyperloglogs(超日志,用于基数统计)、Geospatial indexes(地理空间索引,简称GEO,用于地理位置经纬度存储与计算)、Streams(流)数据。
  • Redis支持事务、发布订阅(publish/subscribe)、Pipe(管道)等特性。
  • Redis支持数据的备份(master-salve模式)与集群(数据分片存储)、Redis Sentinel(哨兵监控机制,用于监控Redis集群节点状态)。
  • Redis通过Redis Cluster支持高可用

1.2 Redis的优点

  • Redis基于内存操作读写性能极高。官方提供的基准测试单机Redis读的速度是110000次/s,写的速度是81000次/s。
  • 支持丰富的数据结构。Redis不仅支持Strings、Lists(列表)、Hashes(哈希)、Sets(集合)、Sorted sets(有序集合)5种基本数据类型,也支持Bitmaps(位图)、Hyperloglogs(超日志,用于基数统计)、Geospatial indexes(地理空间索引,简称GEO,用于地理位置经纬度存储与计算)、Streams(流)复杂数据类型。
  • 基于原子操作。Redis所有的命令都是原子性的,同时Redis事务还支持对多个操作合并后的原子性执行。
  • 丰富的特性。Redis还支持事务、发布订阅(publish/subscribe)、Pipe(管道)、key过期、Redis Cluster、Redis Sentinel、数据持久化等特性。

    1.3 Redis性能好的几大原因

  • 基于内存操作,所以读写效率非常高。

  • Redis采用非阻塞IO、IO多路复用网络模型,减少了线程切换时上下文的切换和竞争所带来的开销。
  • Redis采用了单线程模型,保证了每个操作的原子性,也减少了线程切换时上下文的切换和竞争所带来的开销。Redis之所以采用单线程而非多线程,原因有如下三点:
    • 单线程编写的代码比较清晰,处理逻辑比较简单。
    • 无需考虑各种锁问题保证线程安全,使用多线程可能会带来线程安全问题,一般的通过加锁来解决线程安全问题,但加锁和释放锁的动作非常影响程序执行效率,而且还可能出现死锁。
    • 不存在多进程和多线程导致的切换而消耗CPU。Redis采用单线程模型,也意味着Redis无法充分利用CPU,但可以增加Redis Cluster节点提升性能。
  • Redis存储结构多样化,不同的数据结构对数据存储进行了优化,从而有利于数据的存储与读取。
  • Redis采用自己实现的事件分离器,执行效率比较高,其内部采用了非阻塞的执行方式,吞吐能力比较大。

1.4 Redis、Memcached、Ehcache常见缓存框架对比

  • Memcached:Memcached 是一个基于内存的高性能、分布式Key-Value内存对象缓存系统,用于存储来自数据库调用、API 调用或页面呈现结果的任意数据(字符串、对象)的小块。
  • Ehcache:Ehcache 是一个基于标准的开源缓存、可提高性能、卸载数据库并简化可扩展性。它是使用最广泛的基于Java的缓存,因为它健壮,经过验证,功能齐全,并与其他流行的库和框架集成。Ehcache 从进程内缓存一直扩展到具有 TB 级缓存的进程内/进程外混合部署。

Redis、Memcached、Ehcache是市面上主流的开源缓存框架,它们的区别如下表:


Ehcache Memcached Redis
实现语言 Java C C
数据类型 不支持 不支持 支持多种数据类型,如String、List、Set、Hash、Sorted Set等多种数据类型
支持客户端语言 仅支持Java客户端 支持C/C++、PHP、 Java、Python、 Ruby、 Perl、Erlang、Lua等 支持ActionScript ActiveX/COM+ Bash Boomi C C# C++ Clojure Common Lisp Crystal D Dart Delphi Elixir emacs lisp Erlang Fancy gawk GNU Prolog Go Haskell Haxe Io Java Julia Kotlin Lasso Lua Matlab mruby Nim Node.js Objective-C OCaml Pascal Perl PHP PL/SQL Prolog Pure Data Python R Racket Rebol Ruby Rust Scala Scheme Smalltalk等30多种

2.Redis版本历史

2.1 Redis6.0

  • 多线程IO:Redis 6引入多线程IO,但多线程部分只是用来处理网络数据的读写和协议解析,执行命令仍然是单线程。之所以这么设计是不想因为多线程而变得复杂,需要去控制 key、lua、事务,LPUSH/LPOP 等等的并发问题。
  • 重新设计了客户端缓存功能:实现了Client-side-caching(客户端缓存)功能。放弃了caching slot,而只使用key names。
  • RESP3协议:RESP(Redis Serialization Protocol)是 Redis 服务端与客户端之间通信的协议。Redis 5 使用的是 RESP2,而 Redis 6 开始在兼容 RESP2 的基础上,开始支持 RESP3。推出RESP3的目的:一是因为希望能为客户端提供更多的语义化响应,以开发使用旧协议难以实现的功能;另一个原因是实现 Client-side-caching(客户端缓存)功能。
  • 支持SSL:连接支持SSL,更加安全。
  • ACL权限控制:1. 支持对客户端的权限控制,实现对不同的key授予不同的操作权限。2. 有一个新的ACL日志命令,允许查看所有违反ACL的客户机、访问不应该访问的命令、访问不应该访问的密钥,或者验证尝试失败。这对于调试ACL问题非常有用。
  • 提升了RDB日志加载速度:根据文件的实际组成(较大或较小的值),可以预期20/30%的改进。当有很多客户机连接时,信息也更快了,这是一个老问题,现在终于解决了。
  • 发布官方的Redis集群代理模块 Redis Cluster proxy:在 Redis 集群中,客户端会非常分散,现在为此引入了一个集群代理,可以为客户端抽象 Redis 群集,使其像正在与单个实例进行对话一样。同时在简单且客户端仅使用简单命令和功能时执行多路复用。
  • 提供了众多的新模块(modules)API

3.安装Redis

3.1 centos7安装Redis

(1).下载Redis6..0.10安装包并上传到虚拟机(或服务器)
(2).解压安装包并编译

  1. #解压
  2. tar -xzvf redis-6.0.10.tar.gz
  3. #进入解压后的目录
  4. cd redis-6.0.10
  5. #编译并安装,make后redis-6.0.10的src目录下会出现编译后的redis服务程序redis-server,还有用于测试的客户端程序redis-cli
  6. make && make install

(3).启动redis-server

#切换到src目录下
cd src

#方式1:不指定redis配置则使用默认配置
./redis-server

#方式2:指定redis配置,redis.conf是一个默认的配置文件。我们可以根据需要使用自己的配置文件
./redis-server redis.conf

(5).解决远程连接Redis失败。基本上远程连接失败由redis.conf这三个配置造成

(1).注释bind配置。redis.conf中有一个bind配置项,此配置项表示允许连接的主机host,默认是127.0.0.1(也就是本机访问),如果要远程访问则需要将bind配置注释掉。

(2).将 protected-mode yes 配置修改为 protected-mode no,protected-mode表示是否启用保护模式,默认情况下启用保护模式,只有在以下情况下才应禁用它:第一您确定要让其他主机的客户端连接到Redis,即使未配置身份验证,也没有特定的接口集,第二使用"bind"指令显式列出。

(3).将 daemonize no 配置修改为 daemonize yes。daemonize表示是否以守护程序允许,如果daemonize为no 启动redis-server将会占用一个窗口

(6).启动redis-cli连接redis-server

#启动redis-cli,启动redis-cli后输入ping测试是否启动成功,出现PONG说明启动成功了
src/redis-cli

#redis-cli也可以连接到指定服务器,只需通过-h参数指定redis-server服务器地址,-p参数指定redis-server的端口(默认6379),-a参数指定redis-serve的密码 
src/redis-cli -h host -p port -a password

#连接本机的例子: src/redis-cli -h 127.0.0.1 -p 6379

3.2 Redis目录结构说明

3.3 Redis配置文件常用配置说明

3.4 Redis开机自启动配置

4.Redis数据类型介绍

Redis支持多种数据类型结构,基本类型有字符串(String)、散列(Hashes)、列表(Lists)、集合(Sets)、有序集合(Sorted sets,简称Zset),不常用类型有位数组(或者简单的位图,Bit arrays (or simply bitmaps))、HyperLogLogs(超日志)、Streams。

  • String(字符串):二进制安全字符串。
  • Lists(列表):根据插入顺序排序的一组元素的集合,它们基本上是链表。
  • Sets(集合):元素唯一且未排序的集合。
  • Sorted sets(有序集合):元素唯一且有序的集合,集合中的每个元素都与一个称为score的浮点数字值相关联,元素总是按它们的score排序,因此与Sets不同,可以检索一系列元素(例如,获取排行榜的前10名或后10名)。
  • Hashes(哈希):是由与值关联的字段组成的映射。
  • Bit arrays(位图):可以使用特殊命令像位数组一样处理字符串值:您可以设置和清除单个位,计数所有设置为1的位,找到第一个设置或未设置的位,等等。
  • HyperLogLogs(超日志):这是一个概率数据结构,用于估计集合的基数。
  • Streams(流):提供抽象日志数据类型的类地图项的仅追加集合。

    5.Redis客户端