驻马店建设网站网站建设软硬件平台有哪些

当前位置: 首页 > news >正文

驻马店建设网站,网站建设软硬件平台有哪些,hge网站做微端,网站做微信支付对接目录 Redis 常⻅数据类型预备知识基本全局命令小结 数据结构和内部编码单线程架构引出单线程模型为什么单线程还能这么快 Redis 常⻅数据类型 Redis 提供了 5 种数据结构#xff0c;理解每种数据结构的特点对于 Redis 开发运维⾮常重要#xff0c;同时掌握每种数据结构的常⻅… 目录 Redis 常⻅数据类型预备知识基本全局命令小结 数据结构和内部编码单线程架构引出单线程模型为什么单线程还能这么快 Redis 常⻅数据类型 Redis 提供了 5 种数据结构理解每种数据结构的特点对于 Redis 开发运维⾮常重要同时掌握每种数据结构的常⻅命令会在使⽤ Redis 的时候做到游刃有余。本章内容如下 预备知识⼏个全局generic命令数据结构和内部编码单线程模式机制分析。5 种数据结构的特点、命令使⽤、应⽤场景⽰例。键遍历、数据库管理。 预备知识 在正式介绍 5 种数据结构之前了解⼀下 Redis 的⼀些全局命令、数据结构和内部编码、单线程命令处理机制是⼗分必要的它们能为后⾯内容的学习打下⼀个良好的基础.主要体现在两个⽅⾯ Redis 的命令有上百个如果纯靠死记硬背⽐较困难但是如果理解 Redis 的⼀些机制会发现这些命令有很强的通⽤性。Redis 不是万⾦油有些数据结构和命令必须在特定场景下使⽤⼀旦使⽤不当可能对 Redis 本⾝或者应⽤本⾝造成致命伤害。 基本全局命令 Redis 有 5 种数据结构但它们都是键值对种的值对于键来说有⼀些通⽤的命令。 必须要进入 redis-cli 客户端程序才能输入 redis 命令 全局命令就是能够搭配任意一个数据结构使用的命令 KEYS keys 查询当前服务器上匹配的 key返回所有满⾜样式pattern的 key。⽀持如下统配样式。 通过一些特殊符号通配符来描述的模样匹配上述模样的 key 就能被查询出来 pattern 包含特殊符号的字符串是去描述另外的字符串是什么样的 h?llo 匹配 hello , hallo 和 hxlloh*llo 匹配 hllo 和 heeeelloh[ae]llo 匹配 hello 和 hallo 但不匹配 hilloh[^e]llo 匹配 hallo , hbllo , … 但不匹配 helloh[a-b]llo 匹配 hallo 和 hbllo 匹配任意一个字符 匹配任意零个或者多个字符 abcde只能匹配 abcde 的任意一个相当于给出固定选项 ^e排除 e只有 e 匹配不了其他都能匹配 a-e匹配 a 到 e 之间的字符包括 a 和 e 语法 KEYS pattern命令有效版本1.0.0 之后 时间复杂度O(N) 所以在生产环境上一般禁止使用 keys 命令尤其是像 keys _ 这种这是查询 Redis 存储所有的 key。Redis 经常被用于做缓存挡在 MySQL 前面如果 Redis 被 keys _ 这样一个命令阻塞住了其他查询 Redis 操作间超时了此时这些请求就会直接查数据库如果请求过多 MySQL 就容易挂了。 办公环境办公的电脑开发环境有时就是和办公环境一样的前端/客户端有时开发环境就是一个单独的服务器后端因为有些程序比较复杂 编译一次时间比较久比如 C就需要一台高性能的服务器有些程序一启动就消耗很多 CPU 和内存资源有的程序比较依赖 Linux在 Windows 环境搭不起来 测试环境一般是比较好的服务器测试工程师用的生产环境线上环境与前面三个环境相对的前面三个是线下环境线上环境是外界用户访问的线下环境是外界用户无法访问的这是影响公司营收的 返回值匹配 pattern 的所有 key。 ⽰例 redis MSET firstname Jack lastname Stuntman age 35 OK redis KEYS name

  1. firstname
  2. lastname redis KEYS a??
  3. age redis KEYS *
  4. age
  5. firstname
  6. lastnameEXISTS exists 判断某个 key 是否存在。 语法 EXISTS key [key …]命令有效版本1.0.0 之后 时间复杂度O(1)严谨点应该是多少个 key 就多少个 O(1) Redis 组织这些 key 就是按照哈希表的方式来组织的。 Redis 支持很多数据结构指的是一个 value 可以是一些复杂的数据结构。Redis 自身的这些键值对是通过哈希表组织的具体的某个值又可以是一些数据结构。 返回值key 存在的个数。 ⽰例 redis SET key1 HelloOKredis EXISTS key1(integer) 1redis EXISTS nosuchkey(integer) 0redis SET key2 WorldOKredis EXISTS key1 key2 nosuchkey(integer) 2DEL del 删除指定的 key。 语法 DEL key [key …]命令有效版本1.0.0 之后 时间复杂度O(1) 返回值删除掉的 key 的个数。 ⽰例 redis SET key1 HelloOKredis SET key2 WorldOKredis DEL key1 key2 key3(integer) 2在 MySQL 像删除一类的操作都是比较危险的删除后数据就没有了。但在 Redis 主要的应用场主要是作为缓存此时存的是一个热点数据全量数据在 MySQL 之类的数据库中此时直接把 Redis 的 key 删除了几个一般来说问题不大除非是删除了很多这样就会导致请求直接发送到 MySQL就容易把 MySQL 搞挂。 如果把 Redis 作为数据库存储这样误删数据就问题大了。 如果把 Redis 作为消息队列影响大不大就具体情况具体分析了。 EXPIRE expire 为指定的 key 添加秒级的过期时间Time To Live TTL单位是秒超过时间 key 就被自动删除了比如手机验证码基于 Redis 实现分布式锁为避免不能正确解锁的情况通常都会在加锁的时候设置过期时间Redis 实现分布式锁就是给 Redis 里写一个特殊的 key value。 pexpire 单位是毫秒其他和 expire 是一样的 语法 EXPIRE key secondskey 必须存在 命令有效版本1.0.0 之后 时间复杂度O(1) 返回值1 表⽰设置成功。0 表⽰设置失败。 ⽰例 TTL ttl time to live 获取指定 key 的过期时间秒级。 pttl 单位是毫秒其他和 ttl 是相似的 语法 TTL key命令有效版本1.0.0 之后 时间复杂度O(1) 返回值剩余过期时间。-1 表⽰没有关联过期时间-2 表⽰ key 不存在。 ⽰例 redis SET mykey HelloOKredis EXPIRE mykey 10(integer) 1redis TTL mykey(integer) 10EXPIRE 和 TTL 命令都有对应的⽀持毫秒为单位的版本PEXPIRE 和 PTTL详细⽤法就不再介绍了 一个 Redis 中可能同时存在很多 key很大一部分可能都有过期时间Redis 服务器是怎么知道哪些 key 已经过期要被删除哪些 key 还没过期 如果直接遍历所有的 key肯定是效率很低的。 Redis 整体的策略 定期删除这个定期删除也采用了一个抽取样本的方式每次只抽取一部分保证这个抽取检查的过程足够快不要浪费太多时间惰性删除过期了不会立即删除但一旦访问到了 Redis 就立刻出发删除 key 的操作同时返回一个 nil 以上策略的原因因为 Redis 是单线程的程序主要的任务是处理每个命令如果扫描过期 key 太耗费时间就导致正常处理请求命令被阻塞了 以上策略的效果是一般的仍然可能有很多过期的 key 被残留。对以上 Redis 也是有补充的比如一系列的内存淘汰策略 注意 Redis 中没有采取定时器的方式来实现过期 key 的删除如果有多个 key 过期也可以通过一个定时器基于优先级队列或者时间轮来高效/节省 CPU 的前提下来处理多个 key 作者没有采用定时器的原因可能是因为如果基于定时器实现就要引入多线程了而 Redis 早期版本就是奠定了单线程的基调多线程就不符合作者初衷 定时器的实现方式Redis 并没有这个做 优先级队列 假定很多 key 都设置了过期时间放入优先级队列后指定优先级规则是过期时间早的先出队列此时扫描线程只需要盯着队首元素即可但这个是扫描线程也不需要检查得太频繁可以根据当前时刻和队首元素的过期时间设置一个等待此时如果有新任务添加进来就唤醒一下刚才的线程重新检查一下队首元素再根据时间差距重新调整阻塞时间即可 时间轮 把时间划分成很多的格子划分的粒度看实际需求每个格子都挂着一个链表每个链表都代表一个要执行的任务此时设置一个指针遍历这个轮每隔固定的时间走一个格子此时添加一个 key 时把其过期时间除以指针移动的固定时间放到对应的格子对于时间轮多少个格子以及移动的固定时间都是根据需求设置的 Redis 源码中一个比较核心的机制是事件循环 关于键过期机制可以参考图 2-1 所⽰。 图 2-1 键的过期机制 TYPE 返回 key 对应的数据类型。 Redis 所有的 key 都是 string但 key 对应的 value 可能有多种类型 语法 TYPE key命令有效版本1.0.0 之后 时间复杂度O(1) 返回值 none , string , list , set , zset , hashstream这个是 Redis 作为消息队列使用的 value。 ⽰例 redis SET key1 valueOKredis LPUSH key2 value(integer) 1redis SADD key3 value(integer) 1redis TYPE key1stringredis TYPE key2listredis TYPE key3set小结 keys用来查看匹配规则的 keyexists用来判定指定 key 是否存在del删除指定的 keyexpire给 key 设置过期时间ttl查询 key 的过期时间type查询 key 对应的 value 的类型 以上只是给出⼏个通⽤的命令为 5 种数据结构的使⽤实际上 Redis 支持10个数据类型做⼀个热⾝后续将对键管理做⼀个更为详细的介绍。 数据结构和内部编码 type 命令实际返回的就是当前键的数据结构类型它们分别是string字符串、list列表、hash哈希、set集合、zset有序集合但这些只是 Redis 对外的数据结构如图 2-2 所⽰。 图 2-2 Redis 的 5 种数据类型 这5种数据结构相当于 Java 中的 StringHashMapListSet这个找不太到除了存储 member 外还需要存储一个 score权重 实际上 Redis 针对每种数据结构都有⾃⼰的底层内部编码实现⽽且是多种实现这样 Redis 会在合适的场景选择合适的内部编码如表 2-1 所⽰。 表 2-1 Redis 数据结构和内部编码 数据类型内部编码说明stringraw最基本的字符串stringint当 value 是一个整数时Redis 很可能直接用 int 保存stringembstr针对短字符串进行的特殊优化hashhashtable最基本的哈希表但这并不是 Java 标准库的那个 HashTablehashziplist压缩列表针对哈希表里面元素较少的情况能够节省空间listlinkedlist链表listziplist压缩列表sethashtablesetintset针对集合中存的都是整数zsetskiplist跳表也是链表但每个节点上有多个指针域zsetziplist 可以看到每种数据结构都有⾄少两种以上的内部编码实现Redis 会自动根据当前的实际情况选择内部的编码方式自动适应的例如 list 数据结构包含了 linkedlist 和 ziplist 两种内部编码。同时有些内部编码例如 ziplist可以作为多种数据结构的内部实现可以通过 object encoding 命令查询内部编码 127.0.0.1:6379 set hello world OK 127.0.0.1:6379 lpush mylist a b c (integer) 3 127.0.0.1:6379 object encoding hello embstr 127.0.0.1:6379 object encoding mylist quicklist可以看到 hello 对应值的内部编码是 embstr键 mylist 对应值的内部编码是 ziplist。 从 Redis 3.2 开始对于 list 引入了新的实现方式 quicklist就直接代替了 linkedlist 和 ziplist同时兼顾了两个的优点quicklist 就是一个链表每个元素又是一个 ziplist把空间和效率都折中兼顾到。 注意只是对于 list 这个数据类型是这样别的还是按照上述表格 Redis 这样设计有两个好处 可以改进内部编码⽽对外的数据结构和命令没有任何影响这样⼀旦开发出更优秀的内部编码⽆需改动外部数据结构和命令例如 Redis 3.2 提供了 quicklist结合了 ziplist 和 linkedlist 两者的优势为列表类型提供了⼀种更为优秀的内部编码实现⽽对⽤⼾来说基本⽆感知。多种内部编码实现可以在不同场景下发挥各⾃的优势例如 ziplist ⽐较节省内存但是在列表元素⽐较多的情况下性能会下降这时候 Redis 会根据配置选项将列表类型的内部实现转换为linkedlist整个过程⽤⼾同样⽆感知。 单线程架构 Redis 使⽤了单线程架构来实现⾼性能的内存数据库服务本节⾸先通过多个客⼾端命令调⽤的例⼦说明 Redis 单线程命令处理机制接着分析 Redis 单线程模型为什么性能如此之⾼最终给出为什么理解单线程模型是使⽤和运维 Redis 的关键。 Redis 只使用一个线程处理所有的命令请求而不是 Redis 服务器进程内部真的只有一个线程有多个线程多个线程是在处理网络IO 引出单线程模型 现在开启了三个 redis-cli 客⼾端同时执⾏命令。 客⼾端 1 设置⼀个字符串键值对 客⼾端 2 对 counter 做⾃增操作 客⼾端 3 对 counter 做⾃增操作 我们已经知道从客⼾端发送的命令经历了发送命令、执行命令、返回结果三个阶段其中我们重点关注第 2 步。我们所谓的 Redis 是采⽤单线程模型执⾏命令的是指虽然三个客户端看起来是同时要求 Redis 去执行命令的但微观⻆度这些命令还是采⽤线性⽅式去执⾏的只是原则上命令的执⾏顺序是不确定的但⼀定不会有两条命令被同步执⾏如图 2-3、2-4、2-5 所⽰可以想象 Redis 内部只有⼀个服务窗⼝多个客⼾端按照它们达到的先后顺序被排队在窗⼝前依次接受 Redis 的服务所以两条 incr 命令⽆论执⾏顺序结果⼀定是 2不会发⽣并发问题这个就是 Redis 的单线程执⾏模型。 图 2-3 宏观上同时要求服务的客⼾端 图 2-4 微观上客⼾端发送命令的时间有先后次序的 图 2-5 Redis 的单线程模型 为什么单线程还能这么快 这是个重要的面试题 通常来讲单线程处理能⼒要⽐多线程差例如有 10000 公⽄货物每辆⻋的运载能⼒是每次 200 公⽄那么要 50 次才能完成但是如果有 50 辆⻋只要安排合理只需要依次就可以完成任务。那么为什么 Redis 使⽤单线程模型会达到每秒万级别的处理能⼒呢可以将其归结为三点 纯内存访问。Redis 将所有数据放在内存中内存的响应时⻓⼤约为 100 纳秒这是 Redis 达到每秒万级别访问的重要基础。核心功能更简单。Redis 核心功能比数据库的核心功能更简单数据库对于数据的插入删除查询都有更复杂的功能支持这样的功能势必要花费更多的开销。非阻塞 IO。Redis 使⽤ epoll 作为 I/O 多路复⽤技术的实现再加上 Redis ⾃⾝的事件处理模型将 epoll 中的连接、读写、关闭都转换为事件不在⽹络 I/O 上浪费过多的时间如图 2-6 所⽰。 一个线程就可以管理多个 socket对于 TCP 来说服务器每次要服务一个客户端就要安排一个 socket一个服务器服务多个客户端就需要多个 socket但很多情况下每个客户端和服务器之间的通信也没那么频繁此时很多 socket 大部分时间都是静默的是没有数据需要传输的同一时刻只有少数 socket 是活跃的因此就用个 IO多路复用一个线程来处理多个 socket。 这是操作系统给程序员提供的机制提供了一套 API内部的功能都是操作系统实现的。 Linux 提供的 IO多路复用 主要是三套 API selectpollepoll epoll 是个事件通知/回调机制一个线程可以同时做多件事情能高效完成多件事但前提是这多件事交互是互不影响的大部分时间都在等。 C 可以直接使用 Linux 原生的 epoll APIJava 可以使用 NIO这是标准库提供的一组类底层就是封装了 epoll。 单线程避免了线程切换和竞态产生的消耗。单线程可以简化数据结构和算法的实现让程序模型更简单其次多线程避免了在线程竞争同⼀份共享数据时带来的切换和等待消耗。 图 2-6 Redis 使⽤ I/O 多路复⽤模型 虽然单线程给 Redis 带来很多好处但还是有⼀个致命的问题对于单个命令的执⾏时间都是有要求的。如果某个命令执⾏过⻓会导致其他命令全部处于等待队列中迟迟等不到响应造成客⼾端的阻塞对于 Redis 这种⾼性能的服务来说是⾮常严重的所以 Redis 是面向快速执行场景的数据库。