Redis安装

在Linux或者Mac环境可以直接安装使用

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

直接安装方式

# mac
> brew install redis
# ubuntu
> apt-get install redis
# redhat
> yum install redis
# 运行客户端
> redis-cli

Redis基础数据结构

Redis有5种基础数据结构,分别为:string(字符串)、list(列表)、set(集合)、hash(哈希)和zset(有序集合)

string(字符串)

Redis所有的数据结构都是以唯一的key字符串作为名称,然后通过这个唯一的key值来获取响应的value数据。不同类型的数据结构差异在于value的结构不同。

在这里插入图片描述
Redis的字符串是可以修改的动态字符串,采用预分配冗余空间的方式来减少内存的频繁分配(如图:内部为当前字符串分配的空间capacity一般要高于实际字符串长度len),当字符串小于1M时,扩容都是加倍现有的空间,如果超过1M,扩容时一次只会多扩1M的空间,字符串最大长度为512M

键值对
127.0.0.1:6379> set name xiaoyige
OK
127.0.0.1:6379> get name
"xiaoyige"
127.0.0.1:6379> exists name
(integer) 1
127.0.0.1:6379> del name
(integer) 1
127.0.0.1:6379> get name
(nil)
批量键值对
127.0.0.1:6379> mset name1 mingzi1 name2 mingzi2
OK
127.0.0.1:6379> mget name1 name2 name3
1) "mingzi1"
2) "mingzi2"
3) (nil)
过期和set命令扩展

可以对key设置过期时间,到点自动删除,这个功能常用来控制缓存失效时间。

127.0.0.1:6379> set name mingzi
OK
127.0.0.1:6379> get name
"mingzi"
127.0.0.1:6379> expire name 5	# 5s后过期
(integer) 1
127.0.0.1:6379> get name
"mingzi"
# 5s内能获取到
127.0.0.1:6379> get name
(nil)
# 5s后缓存失效

127.0.0.1:6379> setex name 5 mingzi	5s后过期,等价于set+expire
OK
127.0.0.1:6379> get name
"mingzi"
127.0.0.1:6379> get name
(nil)

127.0.0.1:6379> setnx name mingzi	# 当key不存在时执行set创建
(integer) 1
127.0.0.1:6379> get name
"mingzi"
127.0.0.1:6379> setnx name mingzi2	# 因为key已经存在所有创建不成功
(integer) 0
127.0.0.1:6379> get name
"mingzi"

注意setnx(SET if Not eXists),命令在指定的key不存在时,为key设置指定的值。

计数

如果value是个证书,还可以对它进行自增操作。但是自增是有范围的,是**signed long的最大最小值 2 63 2^{63} 263),超过了redis会报错

127.0.0.1:6379> set num 100
OK
127.0.0.1:6379> incr num	# 默认+1
(integer) 101
127.0.0.1:6379> incrby num 4
(integer) 105
127.0.0.1:6379> incrby num -4
(integer) 101

127.0.0.1:6379> set num 9223372036854775807	# Long.Max
OK
127.0.0.1:6379> incr num
(error) ERR increment or decrement would overflow

字符串是由很多个字节组成,每个字节又是由8个bit组成,如此便可以将一个字符串看成很多bit的组合,这便是bitmap「位图」数据结构

list(列表)

Redis的列表是链表,这意味着list的插入和删除操作非常快,时间复杂度为O(1),但是索引定位很慢,时间复杂度为O(n)。

当列表弹出了最后一个元素后,该数据结构自动被删除,内存被回收

Redis的列表结构常用来做异步队列使用。将需要延后处理的任务结构体序列化成字符串塞进Redis的列表,另一个线程从这个列表中轮询数据进行处理。

127.0.0.1:6379> rpush books golang java python	# golang <-> java <-> python
(integer) 3
127.0.0.1:6379> llen books
(integer) 5
127.0.0.1:6379> lpush books C++ C#
(integer) 5
127.0.0.1:6379> lpop books   # C# <-> C++ <-> golang <-> java <-> python
"C#"
127.0.0.1:6379> rpop books	# C++ <-> golang <-> java <-> python
"python"
127.0.0.1:6379> lpop books	# C++ <-> golang <-> java
"C++"
127.0.0.1:6379> rpop books	# golang <-> java
"java"
127.0.0.1:6379> lpop books	# golang
"golang"
127.0.0.1:6379> rpop books
(nil)
慢操作lindex
127.0.0.1:6379> rpush books golang java python
(integer) 3
127.0.0.1:6379> lindex books 1	# 定位找到下标为1的元素
"java"

127.0.0.1:6379> lrange books 0 -1	# 遍历books
1) "golang"
2) "java"
3) "python"

127.0.0.1:6379> ltrim books 1 -1	# O(n) 保留从下标1开始到最后的元素
OK
127.0.0.1:6379> lrange books 0 -1
1) "java"
2) "python"

127.0.0.1:6379> ltrim books 0 0	# 保留下标为0的元素
OK
127.0.0.1:6379> lrange books 0 -1
1) "java"

127.0.0.1:6379> ltrim books 1 0	# 清空了整个列表,因为区间范围长度为负
OK
127.0.0.1:6379> llen books
(integer) 0

127.0.0.1:6379> rpush books golang java python
(integer) 3
127.0.0.1:6379> rpush books golang java python
(integer) 6
127.0.0.1:6379> rpush books golang java python
(integer) 9
127.0.0.1:6379> lrange books 0 -1
1) "golang"
2) "java"
3) "python"
4) "golang"
5) "java"
6) "python"
7) "golang"
8) "java"
9) "python"
127.0.0.1:6379> lrem books 2 golang	# 从表头遍历删除值为golang的元素并删除,删除两个结束,如果是负数,则从表尾向表头遍历
(integer) 2
127.0.0.1:6379> lrange books 0 -1
1) "java"
2) "python"
3) "java"
4) "python"
5) "golang"
6) "java"
7) "python"

lindex相当于链表中get(int index)方法,它需要对链表进行遍历,性能随着参数index增大而变差

快速列表

Redis底层存储的不是一个简单的linkedlist,而是称之为快速链表quicklist的一个结构。
在列表元素较少的情况下会使用一块连续的内存存储,这个结构是压缩列表(ziplist)。当数据量比较多的时候才会改成quicklist。原因是普通的链表需要的附加指针空间太大,会比较浪费空间,而且会加重内存的碎片化。所以Redis将链表和ziplist结合起来组成了quicklist。将多个ziplist使用双向指针串起来使用。这样的优点是满足了快速的插入删除功能,而且不回出现太大的空间冗余。

hash(字典)

Redis中的字典是无序字典。使用的数组+链表二维结构。第一维hash的组位置碰撞时,就会将碰撞的元素使用链表串起来。在这里插入图片描述
Redis的字典的值只能是字符串。
Rehash:h[0]为原hash表,当h[0]在装载数据到一定成都的时候触发扩容和rehash过程
不同于Java的rehash,redis为了高性能,不能堵塞服务器,所以采用了渐进式rehash策略。
渐进式rehash会在rehash的同时,保留新旧两个hash结构,循序渐进地将旧hash的内容一点点前一到新的hash结构中。当迁移完成后,就会使用新的hash结构取而代之。当hash移除了最后一个元素后,该数据结构自动被删除,内存被回收。
当hash结构的存储消耗要高于单个字符串,到底该使用hash还是字符串,需要根据实际情况权衡。

127.0.0.1:6379> hset guowei height 177
(integer) 1
127.0.0.1:6379> hset guowei weight 127
(integer) 1
127.0.0.1:6379> hset guowei friends gaopeng
(integer) 1
127.0.0.1:6379> hgetall guowei
1) "height"
2) "177"
3) "weight"
4) "127"
5) "friends"
6) "gaopeng"
127.0.0.1:6379> hlen guowei
(integer) 3
127.0.0.1:6379> hget guowei height
"177"
127.0.0.1:6379> hmset guowei sister guohuan mom yangfengrong dad guoshujun
OK
127.0.0.1:6379> hget guowei sister
"guohuan"

127.0.0.1:6379> hmget guowei sister mom dad
1) "guohuan"
2) "yangfengrong"
3) "guoshujun"

127.0.0.1:6379> hset guohuan legs 2
(integer) 1
127.0.0.1:6379> hset guohuan arms 2
(integer) 1
127.0.0.1:6379> hdel guohuan arms	# 删除key为arms的元素
(integer) 1
127.0.0.1:6379> hgetall guohuan
1) "legs"
2) "2"

hash结构中单个key也可以进行计数,对应的指令是hincrby、hincr

127.0.0.1:6379> hset guowei age 26
(integer) 1
127.0.0.1:6379> hincrby guowei age 1
(integer) 27

set(集合)

Redis set的键值是无序的唯一的。它的内部实现相当于一个特殊的字典,字典中所有的value都是一个值NULL
当集合中最后一个元素移除之后,数据结构自动删除,内存被回收。

127.0.0.1:6379> sadd guowei leftleg	# 可以添加一个或者多个值
(integer) 1
127.0.0.1:6379> sadd guowei rightleg
(integer) 1
127.0.0.1:6379> sadd guowei leftleg
(integer) 0		# 元素重复,添加失败

127.0.0.1:6379> sadd guowei leftarm
(integer) 1
127.0.0.1:6379> sadd guowei rightarm
(integer) 1


127.0.0.1:6379> smembers guowei
1) "rightleg"
2) "leftleg"
3) "leftarm"
4) "rightarm"

127.0.0.1:6379> scard guowei	# 获取集合元素个数
(integer) 4

127.0.0.1:6379> spop guowei	# 瞎级把弹
"leftarm"


zset(有序集合)

一方面zset是一个set,保证了内部value的唯一性,另一方main可以给每个value赋予一个score,代表这个value的排序权重。它的内部实现方式是「跳跃列表」的数据结构。
zset中最后一个value被移除后,数据结构自动删除,内存被回收。

127.0.0.1:6379> zadd guowei 9.0 "I hava a head"
(integer) 1
127.0.0.1:6379> zadd guowei 8.0 "I hava arms"
(integer) 1
127.0.0.1:6379> zadd guowei 7.0 "I hava legs"
(integer) 1
127.0.0.1:6379> zrange guowei 0 -1	# 按照score从小到大输出
1) "I hava legs"
2) "I hava arms"
3) "I hava a head"

127.0.0.1:6379> zrevrange guowei 0 -1	# 按照score从大到小输出
1) "I hava a head"
2) "I hava arms"
3) "I hava legs"

127.0.0.1:6379> zcard guowei
(integer) 3

127.0.0.1:6379> zscore guowei "I hava a head"	# 指定value获取score
"9"

127.0.0.1:6379> zrangebyscore guowei 0 8.7	# 指定范围
1) "I hava legs"
2) "I hava arms"

127.0.0.1:6379> zrangebyscore guowei 8.7 inf	# inf为ininfinite,无穷大的意思,-inf为负无穷大
1) "I hava a head"

127.0.0.1:6379> zrangebyscore guowei 8.0 inf withscores		# inf为ininfinite,无穷大的意思,-inf为负无穷大,所以该命令是根据分值区间(-∞,8.0)遍历zset,同时返回分值
1) "I hava arms"
2) "8"
3) "I hava a head"
4) "9"

127.0.0.1:6379> zrem guowei "I hava a head"	# 删除元素
(integer) 1
127.0.0.1:6379> zrange guowei 0 -1
1) "I hava legs"
2) "I hava arms"
跳跃列表

因为zset需要支持随机的插入和删除,所以不好使用数组来表示
在这里插入图片描述
链表可以支持随机的插入和删除,但是需要按照score值进行排序。当有新元素要插入时,需要定位到特定位置的插入点,这样才可以保证链表是有序的,但是如果线性比较的话耗时太久,所以通常我们使用二分法来查找插入点,这就产生了跳表。
在这里插入图片描述
跳表类似于层级制,最下面一层所有元素都会串起来。然后每隔几个挑选出一个代表,再将这几个代表使用另外一级指针串起来。然后在这些代表里再选出二级代表,再串起来。最终形成了金字塔结构。
在定位插入点时,先在顶层进行定位,然后下潜到下一级定位。一直下潜到最底层找到合适到位置,将新元素插进去。

容器型数据结构的通用规则

list/set/hash/zset 这四种数据结构是容器型数据结构,他们共享两条通用规则:

  1. create if not exists:如果容器不存在,那就创建一个,再进行操作
  2. drop if not elements:如果容器里元素没有了,那么立即删除容器释放内存。

过期时间

Redis所有的数据结构都可以设置过期时间,时间到了,Redis会自动该删除相应的对象。如果一个字符串已经设置了过期时间,然后你又调用了set方法修改了它,则它的过期时间会消失

127.0.0.1:6379> set guowei ll
OK
127.0.0.1:6379> expire guowei 1000
(integer) 1
127.0.0.1:6379> get guowei
"ll"
127.0.0.1:6379> ttl guowei
(integer) 990
127.0.0.1:6379> set guowei ff
OK
127.0.0.1:6379> ttl guowei
(integer) -1
Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐