Redis开发与运维. 2.1 预备

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介:

2.1 预备

在正式介绍5种数据结构之前,了解一下Redis的一些全局命令、数据结构和内部编码、单线程命令处理机制是十分有必要的,它们能为后面内容的学习打下一个好的基础,主要体现在两个方面:第一、Redis的命令有上百个,如果纯靠死记硬背比较困难,但是如果理解Redis的一些机制,会发现这些命令有很强的通用性。第二、Redis不是万金油,有些数据结构和命令必须在特定场景下使用,一旦使用不当可能对Redis本身或者应用本身造成致命伤害。

2.1.1 全局命令

Redis有5种数据结构,它们是键值对中的值,对于键来说有一些通用的命令。

1.?查看所有键

keys *

下面插入了3对字符串类型的键值对:

127.0.0.1:6379> set hello world

OK

127.0.0.1:6379> set java jedis

OK

127.0.0.1:6379> set python redis-py

OK

keys *命令会将所有的键输出:

127.0.0.1:6379> keys *

1) "python"

2) "java"

3) "hello"

2.?键总数

dbsize

下面插入一个列表类型的键值对(值是多个元素组成):

127.0.0.1:6379> rpush mylist a b c d e f g

(integer) 7

dbsize命令会返回当前数据库中键的总数。例如当前数据库有4个键,分别是hello、java、python、mylist,所以dbsize的结果是4:

127.0.0.1:6379> dbsize

(integer) 4

dbsize命令在计算键总数时不会遍历所有键,而是直接获取Redis内置的键总数变量,所以dbsize命令的时间复杂度是O(1)。而keys命令会遍历所有键,所以它的时间复杂度是O(n),当Redis保存了大量键时,线上环境禁止使用。

3.?检查键是否存在

exists key

如果键存在则返回1,不存在则返回0:

127.0.0.1:6379> exists java

(integer) 1

127.0.0.1:6379> exists not_exist_key

(integer) 0

4.?删除键

del key [key ...]

del是一个通用命令,无论值是什么数据结构类型,del命令都可以将其删除,例如下面将字符串类型的键java和列表类型的键mylist分别删除:

127.0.0.1:6379> del java

(integer) 1

127.0.0.1:6379> exists java

(integer) 0

127.0.0.1:6379> del mylist

(integer) 1

127.0.0.1:6379> exists mylist

(integer) 0

返回结果为成功删除键的个数,假设删除一个不存在的键,就会返回0:

127.0.0.1:6379> del not_exist_key

(integer) 0

同时del命令可以支持删除多个键:

127.0.0.1:6379> set a 1

OK

127.0.0.1:6379> set b 2

OK

127.0.0.1:6379> set c 3

OK

127.0.0.1:6379> del a b c

(integer) 3

5.?键过期

expire key seconds

Redis支持对键添加过期时间,当超过过期时间后,会自动删除键,例如为键hello设置了10秒过期时间:

127.0.0.1:6379> set hello world

OK

127.0.0.1:6379> expire hello 10

(integer) 1

ttl命令会返回键的剩余过期时间,它有3种返回值:

大于等于0的整数:键剩余的过期时间。

-1:键没设置过期时间。

-2:键不存在

可以通过ttl命令观察键hello的剩余过期时间:

#还剩7秒

127.0.0.1:6379> ttl hello

(integer) 7

...

#还剩1秒

127.0.0.1:6379> ttl hello

(integer) 1

#返回结果为-2,说明键hello已经被删除

127.0.0.1:6379> ttl hello

(integer) -2

127.0.0.1:6379> get hello

(nil)

有关键过期更为详细的使用以及原理会在2.7节介绍。

6.?键的数据结构类型

type key

例如键hello是字符串类型,返回结果为string。键mylist是列表类型,返回结果为list:

127.0.0.1:6379> set a b

OK

127.0.0.1:6379> type a

string

127.0.0.1:6379> rpush mylist a b c d e f g

(integer) 7

127.0.0.1:6379> type mylist

list

如果键不存在,则返回none:

127.0.0.1:6379> type not_exsit_key

none

本小节只是抛砖引玉,给出几个通用的命令,为5种数据结构的使用做一个热身,2.7节将对键管理做一个更为详细的介绍。

2.1.2 数据结构和内部编码

type命令实际返回的就是当前键的数据结构类型,它们分别是:string(字符串)、hash(哈希)、list(列表)、set(集合)、zset(有序集合),但这些只是Redis对外的数据结构,如图2-1所示。

实际上每种数据结构都有自己底层的内部编码实现,而且是多种实现,这样Redis会在合适的场景选择合适的内部编码,如图2-2所示。

可以看到每种数据结构都有两种以上的内部编码实现,例如list数据结构包含了linkedlist和ziplist两种内部编码。同时有些内部编码,例如ziplist,可以作为多种外部数据结构的内部实现,可以通过object encoding命令查询内部编码:

127.0.0.1:6379> object encoding hello

"embstr"

127.0.0.1:6379> object encoding mylist

"ziplist"

        

               图2-1 Redis的5种数据结构     图2-2 Redis数据结构和内部编码

可以看到键hello对应值的内部编码是embstr,键mylist对应值的内部编码是ziplist。

Redis这样设计有两个好处:第一,可以改进内部编码,而对外的数据结构和命令没有影响,这样一旦开发出更优秀的内部编码,无需改动外部数据结构和命令,例如Redis 3.2提供了quicklist,结合了ziplist和linkedlist两者的优势,为列表类型提供了一种更为优秀的内部编码实现,而对外部用户来说基本感知不到。第二,多种内部编码实现可以在不同场景下发挥各自的优势,例如ziplist比较节省内存,但是在列表元素比较多的情况下,性能会有所下降,这时候Redis会根据配置选项将列表类型的内部实现转换为linkedlist。

2.1.3 单线程架构

Redis使用了单线程架构和I/O多路复用模型来实现高性能的内存数据库服务,本节首先通过多个客户端命令调用的例子说明Redis单线程命令处理机制,接着分析Redis单线程模型为什么性能如此之高,最终给出为什么理解单线程模型是使用和运维Redis的关键。

1.?引出单线程模型

现在开启了三个redis-cli客户端同时执行命令。

客户端1设置一个字符串键值对:

127.0.0.1:6379> set hello world

客户端2对counter做自增操作:

127.0.0.1:6379> incr counter

客户端3对counter做自增操作:

127.0.0.1:6379> incr counter

Redis客户端与服务端的模型可以简化成图2-3,每次客户端调用都经历了发送命令、执行命令、返回结果三个过程。

其中第2步是重点要讨论的,因为Redis是单线程来处理命令的,所以一条命令从客户端达到服务端不会立刻被执行,所有命令都会进入一个队列中,然后逐个被执行。所以上面3个客户端命令的执行顺序是不确定的(如图2-4所示),但是可以确定不会有两条命令被同时执行(如图2-5所示),所以两条incr命令无论怎么执行最终结果都是2,不会产生并发问题,这就是Redis单线程的基本模型。但是像发送命令、返回结果、命令排队肯定不像描述的这么简单,Redis使用了I/O多路复用技术来解决I/O

的问题,下一节将进行介绍。

2.?为什么单线程还能这么快

通常来讲,单线程处理能力要比多线程差,例如有10?000斤货物,每辆车的运载能力是每次200斤,那么要50次才能完成,但是如果有50辆车,只要安排合理,只需要一次就可以完成任务。那么为什么Redis使用单线程模型会达到每秒万级别的处理能力呢?可以将其归结为三点:

第一,纯内存访问,Redis将所有数据放在内存中,内存的响应时长大约为100纳秒,这是Redis达到每秒万级别访问的重要基础。

第二,非阻塞I/O,Redis使用epoll作为I/O多路复用技术的实现,再加上Redis自身的事件处理模型将epoll中的连接、读写、关闭都转换为事件,不在网络I/O上浪费过多的时间,如图2-6所示。

第三,单线程避免了线程切换和竞态产生的消耗。

既然采用单线程就能达到如此高的性能,那么也不失为一种不错的选择,因为单线程能带来几个好处:第一,单线程可以简化数据结构和算法的实现。如果对高级编程语言熟悉的读者应该了解并发数据结构实现不但困难而且开发测试比较麻烦。第二,单线程避免了线程切换和竞态产生的消耗,对于服务端开发来说,锁和线程切换通常是性能杀手。

但是单线程会有一个问题:对于每个命令的执行时间是有要求的。如果某个命令执行过长,会造成其他命令的阻塞,对于Redis这种高性能的服务来说是致命的,所以Redis是面向快速执行场景的数据库。

单线程机制很容易被初学者忽视,但笔者认为Redis单线程机制是开发和运维人员使用和理解Redis的核心之一,随着后面的学习,相信读者会逐步理解。

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
10天前
|
存储 运维 安全
Spring运维之boot项目多环境(yaml 多文件 proerties)及分组管理与开发控制
通过以上措施,可以保证Spring Boot项目的配置管理在专业水准上,并且易于维护和管理,符合搜索引擎收录标准。
21 2
|
1月前
|
运维 Java Linux
【运维基础知识】掌握VI编辑器:提升你的Java开发效率
本文详细介绍了VI编辑器的常用命令,包括模式切换、文本编辑、搜索替换及退出操作,帮助Java开发者提高在Linux环境下的编码效率。掌握这些命令,将使你在开发过程中更加得心应手。
32 2
|
1月前
|
NoSQL Java Redis
shiro学习四:使用springboot整合shiro,正常的企业级后端开发shiro认证鉴权流程。使用redis做token的过滤。md5做密码的加密。
这篇文章介绍了如何使用Spring Boot整合Apache Shiro框架进行后端开发,包括认证和授权流程,并使用Redis存储Token以及MD5加密用户密码。
30 0
shiro学习四:使用springboot整合shiro,正常的企业级后端开发shiro认证鉴权流程。使用redis做token的过滤。md5做密码的加密。
|
1月前
|
存储 运维 监控
实时计算Flink版在稳定性、性能、开发运维、安全能力等等跟其他引擎及自建Flink集群比较。
实时计算Flink版在稳定性、性能、开发运维和安全能力等方面表现出色。其自研的高性能状态存储引擎GeminiStateBackend显著提升了作业稳定性,状态管理优化使性能提升40%以上。核心性能较开源Flink提升2-3倍,资源利用率提高100%。提供一站式开发管理、自动化运维和丰富的监控告警功能,支持多语言开发和智能调优。安全方面,具备访问控制、高可用保障和全链路容错能力,确保企业级应用的安全与稳定。
42 0
|
1月前
|
NoSQL API Redis
如何使用 C++ 开发 Redis 模块
如何使用 C++ 开发 Redis 模块
|
3月前
|
运维 Devops 持续交付
自动化运维之路:从脚本到DevOps探索后端开发:从基础到高级实践
【8月更文挑战第28天】在数字化时代的浪潮中,企业对于IT运维的要求越来越高。从最初的手动执行脚本,到如今的自动化运维和DevOps实践,本文将带你领略运维的演变之旅。我们将探索如何通过编写简单的自动化脚本来提升效率,进而介绍DevOps文化的兴起及其对现代运维的影响。文章将为你揭示,通过持续集成、持续部署和微服务架构的实践,如何构建一个高效、可靠的运维体系。准备好让你的运维工作变得更加智能化和自动化了吗?让我们一起踏上这段旅程。 【8月更文挑战第28天】 本文旨在为初学者和有一定经验的开发者提供一个深入浅出的后端开发之旅。我们将一起探索后端开发的多个方面,包括语言选择、框架应用、数据库设计
|
3月前
|
运维 Devops 数据库
太卷了!DevOps,就是开发要把运维卷跑了?
太卷了!DevOps,就是开发要把运维卷跑了?
101 0
|
3月前
|
运维 监控 Kubernetes
揭秘运维开发:如何让你的系统更高效、更可靠?
揭秘运维开发:如何让你的系统更高效、更可靠?
|
1月前
|
运维 Linux Apache
,自动化运维成为现代IT基础设施的关键部分。Puppet是一款强大的自动化运维工具
【10月更文挑战第7天】随着云计算和容器化技术的发展,自动化运维成为现代IT基础设施的关键部分。Puppet是一款强大的自动化运维工具,通过定义资源状态和关系,确保系统始终处于期望配置状态。本文介绍Puppet的基本概念、安装配置及使用示例,帮助读者快速掌握Puppet,实现高效自动化运维。
54 4
|
9天前
|
机器学习/深度学习 数据采集 人工智能
智能运维:从自动化到AIOps的演进与实践####
本文探讨了智能运维(AIOps)的兴起背景、核心组件及其在现代IT运维中的应用。通过对比传统运维模式,阐述了AIOps如何利用机器学习、大数据分析等技术,实现故障预测、根因分析、自动化修复等功能,从而提升系统稳定性和运维效率。文章还深入分析了实施AIOps面临的挑战与解决方案,并展望了其未来发展趋势。 ####