Redis,提升服务器性能的一把瑞士军刀

4,290 阅读10分钟

原创作者,公众号【程序员读书】,欢迎关注公众号,转载文章请注明出处哦。

影响服务器性能的因素有很多,比如CPU、内存、硬盘等硬件设备条件的限制,也有服务器整体架构、程序员的代码质量、使用的算法和数据结构优劣等方方面面的因素。

不过个人感觉对服务器性能影响最大,应该是数据库,服务器在面对高并发请求时,性能瓶颈往往出现在数据库方面,因为数据库扛不了那么高的并发数,而使用数据库集群往往也意味着较高的硬件和软件成本,因此,如果在代码层与数据库之间再加一层缓存,可以有效缓解数据库的压力,大大提升服务器的性能。

而提到缓存,我们最先想到的是什么,是Redis,或许这就是Redis给我们的第一印象,虽然这样理解Redis并不完全正确,因为Redis所能做,并不仅仅于此,下面一起探究一下吧。

什么是Redis

Redis是一名叫Salvatore Sanfilippo的人在2009年使用ANSI C编写的开源的内存数据结构存储系统,Redis的作者开发Redis很大一部分原因,也是因为数据库无法满足他的需求。

当然啦,我们可以把Redis简单地理解为内存数据库(NoSQL)或者Key-Value键值对服务器

Redis提供了多种数据结构和不同编程语言的客户端API,另外,也提供了持久化、主从复制、事务等多种功能和特性,就像一把瑞士军刀一般,是服务器开发中的一把利器。

Redis就像这样一把瑞士军刀

虽然Redis提供了非常强大的功能,但Redis的代码却非常简单,其最初的版本只有23000行代码,而最近的某一个版本代码也只有50000多行而已,因此,感兴趣的人可以研究一下Redis源码。

Redis的特性

下面列出一些Redis的特性,让我们对Redis有个比较全面的了解,其实,从这些特性中,也可以看出Redis的强大。

速度快

Redis的响应速度非常快,之所以这么快,最大的原因是Redis对数据的操作都是在内存中进行的,我们从下面的存储器层次结构图便可以看到,内存的读写速度比硬盘(无论是固态还是机械硬盘)都在快很多,因此Redis自然就非常快速。

存储器层次结构图 【摘自《深入理解计算机系统》】

持久化

前面我们说Redis将数据放在内存中,,但Redis也提供了持久化机制,这样可以将用户存储到内存中的数据,定时持久化到硬盘等存储设备中,保证服务器断电重启时,数据不会消失,Redis支持RDBAOF两种持久化机制,有机会,我们在以后的文章中讲讲这两种机制的区别。

多种数据类型

Redis支持多种类型的数据结构,这也是RedisMemcached的区别之一,Memcached只支持String类型,而Redis除了StringHashListSetSort Set这些基本的数据类型外,还有BitMaps(位图)HyperLogLog(超小内存唯一值计数)GEO(地理信息定位)等高级的数据类型。

支持多种编程语言客户端

Redis为大多数编程语言都提供了客户端API,这些API有些是官方提供的,有些则来自开源社区的贡献,所以不管你使用什么语言来开发服务器应用,都可以很方便连接Redis。

下面是Redis官网上列出所有编程语言API列表的地址

# redis支持的客户端列表
https://redis.io/clients

发布订阅

Redis支持订阅与发布的功能,因此可以使用Redis来实现一个简单消息队列,一端作为发布者把消息发布到Redis,而另一端作为消费者,处理来自Redis的消息。

Lua脚本

Redis在V2.6u新增了脚本功能,允许客户端使用Lua语言编写脚本传到Redis中执行,这样做的好处是:减少网络开销原子操作和脚本复用,关于Redis中如何使用Lua脚本,有空再说。

事务

Redis支持事务,当我们需要执行多条命令时,保证多条命令执行原子性,因为在事务中,多条命令的执行,要么都成功,要么都失败,当然,要执行多个命令,使用Lua脚本也许更加方便。

主从复制

Redis支持主从复制功能,即主服务器的数据,可以同步到多台从服务器上,所以主从复制实际是为Redis的高可用和分布式提供了基础。

主从复制示例图

高可用分布式

Redis也支持高可用和分布式,实际主从复制只是实际高可用的基础,但要通过主从复制实现高可用是不太可能的,Redis的高可用是通过Redis Sentinel(哨兵)来实现的,Redis Sentinel是在V2.8增加的功能,而分布式则需要Redis cluster功能的支持,Redis_cluster是在v3.0之后增加的功能。

单线程

Redis是单线程的,这里所说的单线程,指其在Redis作为服务器在接收客户端网络请求时采用是单线程,所以在同一时刻,Redis无法同时处理两个请求,只有一个请求处理完成再处理下一个请求,不过Redis在实现某个功能时,还是使用的多线程的,比如将数据持久化到硬件,就是开启另一个线程来实现的。

安装并启动Redis

Redis的安装非常简单,这里介绍两种安装方式,一种是从Redis官网上下载源码,然后编译安装,另外一种更加方便,直接从Docker Hub上拉取Redisdocker镜像。

下面介绍的是在Linux系统上安装Redis的过程。

使用源码编译安装

# 下载redis源码安装包
wget http://download.redis.io/releases/redis-5.0.5.tar.gz

# 解压缩
tar -zxvf redis-5.0.5.tar.gz

# 进入redis源码目录
cd redis-5.0.5

# 编译 & 安装
make && make install

使用Docker镜像安装并启动Redis

Redis提供了docker镜像,因此我们可以很方便拉取并运行Redis镜像,不过,前提是你的服务器上有安装docker

# 从Docker Hub上拉取官方镜像,redis:latest
docker pull redis

# 从镜像中启动一个容器
docker run --name my_redis -p 6379:6379 -d redis

这里要说明一点,就是Redis作者并没有开发Redis的Windows版本,可能Redis的作者觉得安装Windows系统的服务器不适合运行Redis,又或者什么原因,不过微软还是提供了Redis的Windows版本。

# redis的windows版本
https://github.com/microsoftarchive/redis/releases

启动Redis的几种方式

不带参数,使用默认配置启动服务器

# 使用默认配置启动
redis-server

指定命令行参数启动服务器

# 使用命令参数配置启动
redis-server --port 6380

使用配置文件启动服务器

# 使用配置文件启动
redis-server ~/redis-5.0.5/redis.conf

连接Redis

在使用前面介绍的几种方式启动Redis服务器后,我们就可以使用Redis客户端连接服务器,向服务器写入或读取数据,前面我们说过Redis支持大多数编程语言,因此大大家可以根据自己的情况选择不同的API。

使用命令行客户端连接Redis

在安装好Redis之后,就可以使用Redis安装包中自带的客户端redis-cli,这是一个命令行客户端,其使用示例如下:

# 直接使用,不带参数,会连接本地服务器
redis-cli

# 指定地址和端口号
redis-cli -h 服务器地址 -p 端口号

使用代码连接Redis服务器

在这里演示使用golang的开发的redigo/redis包连接Redis服务器。

# 获取go语言的某个redis客户端库
go get github.com/gomodule/redigo/redis

下面是使用redigo/redis库连接Redis的简单代码示例:

package main

import (
    "fmt"
    "github.com/gomodule/redigo/redis"
)

var redisCon redis.Conn

func main() {
    var err error
    redisCon ,err = redis.Dial("tcp","localhost:6379")
    
    if err != nil{
    	panic(err)
    }
    
    defer redisCon.Close()
    
    rs,err := redisCon.Do("set","test_key","test_value")
    
    if err != nil{
    	panic(err)
    }
    fmt.Println(rs)
}

上面的代码可能比较简单,有机会我们详细了解如何使用Golang更好地连接和操作Redis。

应用场景

下面列出一些Redis的使用场景,我们也可以根据自己的需要,灵活使用Redis来达到提升服务器性能的目的。

缓存系统

Redis最常被使用的场景就是用于数据缓存,由于其基于内存的数据快速读写操作,响应速度非常快,可以用于缓存一些热点数据或者不怎么变化的数据,这样,当用户请求这些数据时,直接从Redis快速读取,避免频繁的数据库读写,提升服务器的数据吞吐量及速度。

排行榜

Redissort set数据类型非常适合做排榜行功能,比如【掘金】的【推荐作者榜】其实实现的原理应该就是将计算后的作者排行权重放在Redissort set中来实现的,因此Redis在实现这类功能真的是非常方便简单。

掘金推荐作者榜

实现社区功能

Redis的功能与一些社区功能有着天然的契合性,使用Redis所支持的数据结构,可以容易存储社区功能中的如关注数,粉丝数,关注列表,粉丝列表,共同共注等数据,比如关注数与粉丝数可以用计数器,关注列表和粉丝列表可以使用seto类型来存储。

计数器

Redis内置的incr命令可以很方便地单线程下进行计数,而且这种操作是原子性,不用担心某个时刻有两个请求对同一个键值进行计数,这种计数器功能在某些场景下非常实用,比如掘金的文章,阅读数、评论数、点赞数可以使用Redis的计数功能来实现,想想,如果这些数据直接写入数据库,那么每次有人点赞、阅读、评论都需要对数据库进行操作,估计数据库会挂掉,而使用Redis计数功能,则可以快速轻松完成这种功能的实现。

消息队列

Redis支持发布订阅功能,因此可以把Redis当作一个简单的消息队列,当我们有一些业务需要使用消息队列时,可以考虑使用Redis来实现,当然,成熟的消息队列中间件系统有很多,如KafkaRabbitMQRocketMQActiveMQ,然后使用这些可能需要额外的软硬件成本及学习成本,因此可以使用Redis的消息队功能来替代。

Redis消息队列示意图

实时系统

对于一些需要实时处理的数据,也可以直接写入Redis中,再由其他系统进行处理,这其实也是利用了Redis发布订阅的功能,来达到数据实时处理的目的,比如一些垃圾邮箱处理系统等。

小结

在这篇文章中,我们简单地谈了一下Redis是什么,可以做什么,并了解其强大的特性和功能,通过演示如何安装、启动和连接Redis,也让我们知道如何使用Redis,当然啦,想要更好地了解和使用Redis,还需要更加深入地学习,下面有机会,我们继续这方面的学习。


你的关注,是我写作路上最大的鼓励!