缓存中间件-Redis(1)
一、Redis简介
Redis是什么?
Redis是一个基于键值对的开源内存数据库。它是互联网技术领域使用最为广泛的存储中间件,它是「Remote Dictionary Service」的首字母缩写,也就是「远程字典服务」。
谁在用?
国内外很多大型互联网公司都在使用 Redis,比如 Twitter、YouPorn、暴雪娱乐、Github、StackOverflow、腾讯、阿里、京东、华为、新浪微博、百度、搜狐、优酷、美团、小米等等,很多中小型公司也都有应用。
Redis用来做什么?
- 缓存
- 计数器
- 消息队列
- 地理信息定位
- 分布式锁
Redis优点?
速度快、性能好。Redis 以其超高的性能、完美的文档、简洁易懂的源码和丰富的客户端库支持在开源中间件领域广受好评。
为什么Redis的默认端口是6379?
Redis 由意大利人 Salvatore Sanfilippo(网名 Antirez) 开发,上图是他的个人照片。
我们都知道 Redis 的默认端口是 6379,这个端口号也不是随机选的,而是由手机键盘字母「MERZ」的位置决定的。「MERZ」在 Antirez 的朋友圈语言中是「愚蠢」的代名词。
二、Redis基础知识
2.1 Redis安装
Docker安装
# 拉取 redis 镜像 > docker pull redis # 运行 redis 容器 > docker run --name myredis -d -p6379:6379 redis # 执行容器中的 redis-cli,可以直接使用命令行操作 redis > docker exec -it myredis redis-cli
Github源码编译
# 下载源码 > git clone --branch 2.8 --depth 1 git@github.com:antirez/redis.git > cd redis # 编译 > make > cd src # 运行服务器,daemonize表示在后台运行 > ./redis-server --daemonize yes # 运行命令行 > ./redis-cli
CentOS直接安装
> yum install redis # 运行客户端 > redis-cli
2.2 Redis基础数据结构
Redis 有 5 种基础数据结构,分别为:
- string (字符串)
- list (列表)
- set (集合)
- hash (哈希)
- zset (有序集合)
每种数据类型都有其底层内部编码实现,在不同的场景下,可以选择性能较好的编码实现。下面是每种数据结构对应的编码类型。
string:raw,int,embstr
hash:hashtable,ziplist
list:linkedlist,ziplist,quicklist
set:hashtable,intset
zset:skiplist,ziplist
2.2.1 string (字符串)
字符串 string 是 Redis 最简单的数据结构。Redis 所有的数据结构都是以唯一的 key 字符串作为名称,然后通过这个唯一 key 值来获取相应的 value 数据。不同类型的数据结构的差异就在于 value 的结构不一样。
字符串结构使用非常广泛,一个常见的用途就是缓存用户信息。我们将用户信息结构体使用 JSON 序列化成字符串,然后将序列化后的字符串塞进 Redis 来缓存。同样,取用户信息会经过一次反序列化的过程。
Redis 的字符串是动态字符串,是可以修改的字符串,内部结构实现上类似于 Java 的 ArrayList,采用预分配冗余空间的方式来减少内存的频繁分配,如图中所示,内部为当前字符串实际分配的空间 capacity 一般要高于实际字符串长度 len。当字符串长度小于 1M 时,扩容都是加倍现有的空间,如果超过 1M,扩容时一次只会多扩 1M 的空间。需要注意的是字符串最大长度为 512M。
操作示例
键值对
> set name codehole OK > get name "codehole" > exists name (integer) 1 > del name (integer) 1 > get name (nil)
批量键值对
可以批量对多个字符串进行读写,节省网络耗时开销。
> set name1 codehole OK > set name2 holycoder OK > mget name1 name2 name3 # 返回一个列表 1) "codehole" 2) "holycoder" 3) (nil) > mset name1 boy name2 girl name3 unknown > mget name1 name2 name3 1) "boy" 2) "girl" 3) "unknown"
过期和set命令扩展
> set name codehole > get name "codehole" > expire name 5 # 5s 后过期 ... # wait for 5s > get name (nil) > setex name 5 codehole # 5s 后过期,等价于 set+expire > get name "codehole" ... # wait for 5s > get name (nil) > setnx name codehole # 如果 name 不存在就执行 set 创建 (integer) 1 > get name "codehole" > setnx name holycoder (integer) 0 # 因为 name 已经存在,所以 set 创建不成功 > get name "codehole" # 没有改变
计数
如果 value 值是一个整数,还可以对它进行自增操作。自增是有范围的,它的范围是 signed long 的最大最小值,超过了这个值,Redis 会报错。
> set age 30 OK > incr age (integer) 31 > incrby age 5 (integer) 36 > incrby age -5 (integer) 31 > set codehole 9223372036854775807 # Long.Max OK > incr codehole (error) ERR increment or decrement would overflow
string字符串命令总结
设置
- set 设置值
- ex 秒
- px 毫秒
- nx 键必须不存在,才可以设置成功
- xx 键必须存在,才可以设置成功
- setex 指定键过期时间
- setnx 键必须不存在才能创建
获取
- get 获取值
- mset 批量设置值
- mget 批量获取值
计数
- incr 自增计数
- decr 自减计数
- incrby 指定增量
- decrby 指定减量
- incrbyfloat 自增浮点数
其他
- append 追加值
- strlen 字符串长度
- getset 设置并返回旧值
- setrange 设置指定位置字符
- getrange 获取指定位置字符
2.2.2 list (列表)
Redis 的列表相当于 Java 语言里面的 LinkedList,注意它是链表而不是数组。这意味着 list 的插入和删除操作非常快,时间复杂度为 O(1),但是索引定位很慢,时间复杂度为 O(n),这点让人非常意外。
当列表弹出了最后一个元素之后,该数据结构自动被删除,内存被回收。
Redis 的列表结构常用来做异步队列使用。将需要延后处理的任务结构体序列化成字符串塞进 Redis 的列表,另一个线程从这个列表中轮询数据进行处理。
右进左出(先进先出FIFO):队列
rpush lpop:r right l left
> rpush books python java golang (integer) 3 > llen books (integer) 3 > lpop books "python" > lpop books "java" > lpop books "golang" > lpop books (nil)
右进右出(先进后出FILO):栈
rpush rpop
> rpush books python java golang (integer) 3 > rpop books "golang" > rpop books "java" > rpop books "python" > rpop books (nil)
慢操作(索引查找)
lindex 相当于 Java 链表的get(int index)方法,它需要对链表进行遍历,性能随着参数index增大而变差。
ltrim 跟的两个参数start_index和end_index定义了一个区间,在这个区间内的值,ltrim 要保留,区间之外统统砍掉。我们可以通过ltrim来实现一个定长的链表,这一点非常有用。
index 可以为负数,index=-1表示倒数第一个元素,同样index=-2表示倒数第二个元素。
> rpush books python java golang (integer) 3 > lindex books 1 # O(n) 慎用 "java" > lrange books 0 -1 # 获取所有元素,O(n) 慎用 1) "python" 2) "java" 3) "golang" > ltrim books 1 -1 # O(n) 慎用 OK > lrange books 0 -1 1) "java" 2) "golang" > ltrim books 1 0 # 这其实是清空了整个列表,因为区间范围长度为负 OK > llen books (integer) 0
list内部编码 压缩列表(ziplist)和快速列表(quicklist)
查看14道真题和解析