1.Redis可以做什么

  1. 记录帖子点赞数、评论数和点击数
  2. 记录用户文章ID列表(排序),便于快速显示用户的帖子列表(zset)
  3. 记录帖子的标题、摘要、作者和封面信息,用于列表页显示(hash)
  4. 记录帖子的点赞用户ID列表,评价ID列表,用于显示和去重计数(zset)
  5. 缓存近期热贴内容(帖子)内容的空间占用比较大,减少数据库压力(hash)
  6. 记录帖子的相关文章ID,根据内容推荐相关帖子(list)
  7. 如果帖子ID是整数自增,可以使用Redis分配帖子ID(计数器)
  8. 缓存用户行为历史,过滤恶意行为(zset、hash)

macOS 下,安装redis

1.下载最新的redis安装包,解压后,粘贴到/usr/local目录下
官网地址:https://redis.io/download

2.编译和安装


cd /usr/local/redis
make
sudo make install 
在bin下可执行的程序
redis-server: Redis服务器
redis-cli: 命令行客户端
redis-benchmark: Redis的性能测试工具
redis-check-aof: AOF文件修复工具
redis-check-dump: RDB文件检测工具
redis.conf: Redis的配置文件

修改redis.config中的daemonize yes 以守护进程的方式启动

在/usr/local/redis中

vim redis.config

###########GENERAL#################

daemonize no

daemonize yes

再打开一个客户端,进入/usr/local/bin/,开启服务端redis-cli。

测试服务器和客户端都开启了。


> set hello world
OK
> get hello
"world"
> 

关闭进程

方式1:在服务器窗口中按 Ctrl+C

方式2:在客户端连接后输入 shutdown 或 直接输入 redis-cli shutdown

ps aux | grep redis #查看redis的进程信息 
或
lsof -i:6379 #查看6379端口的进程信息

kill 进程对应PID

更多安装方式:
https://www.jianshu.com/p/3bdfda703552

在centos7下安装redis:https://www.cnblogs.com/zuidongfeng/p/8032505.html

处理:/var/redis/run/redis_6379.pid exists, process is already running or crashed:https://blog.csdn.net/luozhonghua2014/article/details/54649295

Mac安装Redis可视化工具-Redis Desktop Manager:https://www.jianshu.com/p/214baa511f2e


2. 5种基础数据结构

Redis有5种基础数据类型:string(字符串),list(列表),hash(字典),set(集合)和zset(有序集合)

  • string(字符串)
    字符串string是Redis中最简单的数据结构。内部表示为一个字符数组。Redis所有数据结构都以唯一的key字符串作为名称,通过key值获取对应value数据。
    不同类型的数据结构的差异:value结构不同。
    用途:缓存用户信息。将用户信息结构体使用JSON序列化成字符串,将序列化后的字符串塞进Redis来缓存。同样,取出用户信息也经历一次反序列化。
    Redis的字符串是动态字符串,是可以修改的字符串,内部结构类似Java的ArrayList,采用预分配冗余空间的方式减少内存的频繁分配。分配实际空间capacity一般高于实际字符串长度len。
    当字符串长度小于1MB时,扩容方式:加倍现有空间。
    字符串长度超过1MB时,一次扩容最多扩1MB空间。(字符串最大长度为512MB)。
    [键值对]
    相当于字典key和value,支持简单的增删改查。“name”为字典的key,value为字符串“linkp”


> set name linkp
OK
> get name
"linkp"
> exists name
(integer) 1
> del name
(integer) 1
> get name
(nil)


【批量键值对】
可以对多个字符串进行批量读写,节省网络耗时开销。




> set name1 linkp
OK
> set name2 macro
OK
> mget name1 name2 name3
1) "linkp"
2) "macro"
3) (nil)
> mset name1 boy name2 girl name3 unknow
OK
> mget name1 name2 name3
1) "boy"
2) "girl"
3) "unknow"

【过期和set命令扩展】
可以对key设置过期时间,到时间会被自动删除,该功能常用于控制缓存的失效时间。



> set name linkp
OK
> get name
"linkp"
> expire name 5       #5秒后过期
(integer) 1
> get name
"linkp"
...                   # 等候5s
> get name
(nil)
> setex name 5 linkp   #5秒后过期,等价于 set+expire
OK
> get name
"linkp"
...                  #等候5s
> get name
(nil)
> setnx name linkp    # 如果 name 不存在就执行 set 创建
(integer) 1
> get name
"linkp"
> setnx name macro
(integer) 0          # 因为 name 已经存在,所以set创建不成功
> get name
"linkp"              # 没有改变


【计数】
如果value是一个整数,可以对它进行自增操作。自增范围:signed long 的最大值和最小值之间,超出即报错。



> set age 22
OK
> incr age
(integer) 23
> incrby age 5
(integer) 28
> incrby age -5
(integer) 23
> set codehole 9223372036854775807
OK
> incr codehole
(error) ERR increment or decrement would overflow


字符串由多个字节组成,每字节由8bit组成,可以将一个字符串看作很多bit的组合。即bitmap(位图)数据结构。

  • list(列表)
    Redis的列表相当于Java语言中LinkedList,注意这是链表,不是数组。
    即list的插入和删除操作非常快,时间复杂度O(1),但是索引定位慢,时间复杂度为O(n)。
    列表中每个元素都使用双向指针顺序,串起来可同时支持前向后向遍历。
    列表弹出最后一个元素后,该数据结构被自动删除,内存被回收。
    Redis的列表结构常用来做异步队列使用。
    将需要延后处理的任务结构体序列化成字符串,塞进Redis的列表,另一个线程从这个列表轮询数据进行处理。

【右边进左边出:队列】
队列是先进先出的数据结构,常用于消息排队和异步逻辑处理,会确保元素的访问顺序性。



> rpush books python java golang
(integer) 3
> llen books
(integer) 3
> lpop books
"python"
> lpop books
"java"
> lpop books
"golang"
> lpop books
(nil) 

【右边进右边出:栈】
栈是先进后出的数据结构,与队列相反。拿Redis的列表数据结构来做栈使用的业务场景不多见。



> rpush books python java golang
(integer) 3
> rpop books
"golang"
> rpop books
"java"
> rpop books
"python"
> rpop books
(nil)


【慢操作】


【快速列表】
Redis底层存储非简单的LinkedList,而是称为“快速链表”(quicklist)的一个结构。
当列表元素较少时,使用一块连续的内存存储,这个结构时ziplist,即压缩列表。其将所有的元素紧挨着一起存储,分配的是一块连续的内存。
数据量较多时,使用quicklist。
因为普通链表需要附加指针空间数据,结构上需要两个额外的指针prev和next。所以rRedis将链表和ziplist结合,组成quicklist,即将多个ziplist使用双向指针串起来使用。
quicklist即满足了快速的插入删除性能,又不会出现太大的空间冗余。

hash(字典)
Redis的字典相当于Java语言里面的HashMap,即无序字典,内部存储了很多键值对。
结构与Java中HashMap一样,即“数组+链表”二维结构。
第一维hash的数组位置碰撞时,就会将碰撞的元素使用链表串接起来。
区别:1.Redis字典的值只能为字符串。2.rehash方式不同。
Java中HashMap数据量大时rehash耗时,需要一次性全部rehash。
Redis中渐进式rehash策略。1.保留新旧两个hash结构。2.后续定时任务、hash操作指令中渐渐将旧hash的内容一点点的迁移到新的hash结构中。3.迁移完成,旧数据结构被自动删除,内存被回收。
hash结构存储用户信息,hash对用户结构中的每个字段单独存储,(字符串需要一次性全部序列化整个对象)。
优势:获取用户信息,可以部分获取。(整个字符串的形式去保存用户信息,需一次性全部读取,浪费网络流量)
缺点:hash结构的存储高于单个字符串。



> hset books java "think in java"
(integer) 1
> hset books golang "concurrency in go"
(integer) 1
> hset books python "pythone cookbook"
(integer) 1
> hgetall books
1) "java"
2) "think in java"
3) "golang"
4) "concurrency in go"
5) "python"
6) "pythone cookbook"
> hlen books
(integer) 3
> hget books java
"think in java"
> hset books golang "learning go programming"  # 更新操作,返回0
(integer) 0
> hget books golang
"learning go programming"
> hmset books java "effective java" python "learning python" # 批量 set
golang "modern golang programming"
OK
> hgetall books
1) "java"
2) "effective java"
3) "golang"
4) "modern golang programming"
5) "python"
6) "learning python"


同字符串一样,hash结构的单个子key也可以进行计数,对应指令为hincrby,和incr的使用方法基本一样



> hset user-linkp age 22
(integer) 1
> hincrby user-linkp age 1
(integer) 23


  • set(集合)
    Redis的集合类似Java中HashSet,内部键值对无序、唯一。内部实现相当于一个特殊的字典,字典中所有的value都是一个值NULL。
    集合中最后一个元素被移除,数据结构被自动删除,内存被回收。
    适用场景:存储活动中奖用户ID,去重功能,保证一个用户不会中奖两次。


> sadd books python
(integer) 1
> sadd books python
(integer) 0
> sadd books java golang
(integer) 2
> smembers books
1) "golang"
2) "java"
3) "python"
> sismember books java    # 查询某个 value 是否存在,相当于 contains(0)
(integer) 1
> sismember books rust
(integer) 0
> scard books     # 获取长度相当于 count()
(integer) 3
> spop books      # 弹出一个
"golang" 


  • zset(有序列表)
    zset是Redis提供的最有特色的数据结构,面试最爱问。
    类似于Java的SortedSet和HashMap结合。
    set保证内部value唯一性;给每个value赋予一个score代表这个value的排序权重。
    内部实现:“跳跃列表”
    zset最后一个value被移除,数据结构被自动删除,内存被回收。
    适用场景:
    存储粉丝列表,value值是粉丝的用户ID,score是关注时间。按照关注时间排序。
    存储学生成绩,value值是学生ID,score是其考试成绩。按照分数进行排序。


> zadd books 9.0 "think in java"
(error) WRONGTYPE Operation against a key holding the wrong kind of value
> del books                                                                                     
(integer) 1
> zadd books 9.0 "think in java"
(integer) 1
> zadd books 8.9 "java concurrency"
(integer) 1
> zadd books 8.6 "java cookbook"
(integer) 1
> zrange books 0 -1          # 按 score 排序列出,参数区间为排名范围
1) "java cookbook"
2) "java concurrency"        
3) "think in java"
> zrevrange books 0 -1       # 按 score 逆序列出,参数区间为排名范围
1) "think in java"
2) "java concurrency"
3) "java cookbook"
> zcard books               # 相当于 count()
(integer) 3
> zscore books "java concurrency"       # 获取指定 value 的 score
"8.9000000000000004"         # 内部 score 使用 double 类型进行存储,所以存在小数点精度问题
> zrank books "java concurrency"     # 排名
(integer) 1
> zrangebyscore books 0 8.91      # 根据分值区间遍历 zset
1) "java cookbook"
2) "java concurrency"
> zrangebyscore books -inf 8.91 withscores     # 根据分值区间(-∞,8.91] 遍历 zset,同时返回分值。inf 代表infinite,无穷大的意思。
1) "java cookbook"
2) "8.5999999999999996"
3) "java concurrency"
4) "8.9000000000000004"
> zrem books "java concurrency"      # 删除 value
(integer) 1
> zrange books 0 -1
1) "java cookbook"
2) "think in java"


【跳跃列表】
。。。