redis 详解系列之一(基础知识)

本文内容

  • redis概述
  • redis应用场景
  • 单线程架构简介
  • 全局命令讲解
  • 五种数据类型讲解

redis概述

        redis是一种键值对的nosql数据库,值可以支持字符串(string),哈希(hash),列表(list),集合(set),有序集合(zset)等数据结构。redis是将数据存放到内存中,因此读写速度很快,另外还能将内存中数据持久化到硬盘,保证数据不会丢失。除了redis支持的5中数据结构,还有许多额外的额功能,比如键过期功能实现缓存,发布订阅功能实现消息系统等。
        redis之所以速度快,官方数据是读写性能10万/秒,原因在于:1,redis是用C语言实现的;2,redis是单线程架构,减少了多线程竞争的开销,避免线程切换和锁机制;3,非阻塞IO,redis使用epoll多路复用技术,使得不会在网络IO上浪费太多时间。
        redis对外有五种数据结构(string,hash,list,set,zset),每种数据结构都有两种以上的内部编码实现,可以通过命令object encoding查看

127.0.0.1:6379> object encoding h
"embstr"

这样的设计有两个好处,1,改进内部编码时,对外部无影响;2,不同的编码在不同的场景下有不同的优势。

redis使用场景

        1,redis可用于缓存,大部分的大型网站都需要缓存层,为了保护存储层,使用缓存能加快访问速度,降低后端服务器数据源的压力,2,可用于排行榜系统,可根据list和zset数据结构,方便的构建排行榜系统,3,可应用于计数器应用,4,订阅发布,消息系统

redis单线程

        redis使用的是单线程架构和IO多路复用实现高性能的内存数据库,每次客户端调用命令都是经过三个过程:发送命令、执行命令、返回结果。由于单线程架构,一条命令达到服务端的时候不会立刻执行,所有的命令都会进入到一个队列中排队执行,不会有两个命令同时执行的情况。但是注意,单线程情况下,如果一个命令执行时间过长,会对其他命令造成阻塞,对整个redis服务造成影响。

redis常用命令

全局命令

        RedisDesktopManager是一种redis的可视化工具,使用非常简单。

  • 查看所有键keys *
127.0.0.1:6379> keys *
 1) "a,1"
 2) "a:2"
 3) "aa"
 4) "z"
 5) "a:1"
 6) "b"
 7) "z1"
 8) "c"
 9) "s1"
10) "s"
11) "h"
  • 键总数dbsize
    dbsize不会遍历所有的键,而是直接获取redis内置的键总数变量,时间复杂度O(1),而keys 命令会遍历所有键,时间复杂度O(n),请谨慎使用。
127.0.0.1:6379> dbsize
(integer) 11
  • 检查键是否存在exists
    1 代表存在,0代表不存在
127.0.0.1:6379> exists a
(integer) 0
127.0.0.1:6379> exists b
(integer) 1
  • 删除键 del
    若删除一个不存在的键会返回0,也可以同时删除多个键
127.0.0.1:6379> exists d
(integer) 1
127.0.0.1:6379> del d
(integer) 1
127.0.0.1:6379> exists d
(integer) 0
127.0.0.1:6379> del a b c
(integer) 3
  • 键过期 expire
    为键添加有效期,如以下 10秒后自动删除键。
127.0.0.1:6379> expire c 10
(integer) 1
127.0.0.1:6379> exists c
(integer) 0
  • 键的数据结构类型 type,如果键不存在,返回none
127.0.0.1:6379> type h
string

字符串(string)

        字符串是redis数据结构中最基础的,其他数据结构都是在其之上构建的。字符串类型的值,可以是字符串(string,json,xml等),数字(int,float),二进制等,但是请注意,最大不能超过512MB

常用命令
  • 设置值 set
    set key value [ex seconds] [px milliseconds] [nx|xx]
127.0.0.1:6379> set hello world
OK

选项:
-ex 设置秒级别的过期时间
-px 设置毫秒级别的过期时间
-nx:键必须不存在,才可以设置成功,用于添加。
-xx:与nx相反,键必须存在,才可以设置成功,用于更新。
除set命令,还有setnx,setex,作用和选项-ex,-nx作用一样。

setex key seconds value
setnx key value
127.0.0.1:6379> set a test
OK
127.0.0.1:6379> setnx a test   # 由于a键已经存在,所以设置失败
(integer) 0
127.0.0.1:6379> set a test1 XX # 由于a键已经存在,所以更新成功
OK
  • 获取值 get
127.0.0.1:6379> get a
"test1"
127.0.0.1:6379> get a1
(nil)
  • 批量设置值 mset
    mset key value [key value ...]
127.0.0.1:6379> mset k1 v1 k2 v2
OK
  • 批量获取值 mget
    mget key [key ...]
127.0.0.1:6379> mget k1 k2
1) "v1"
2) "v2"

批量操作能提高效率,执行时间较短,但是使用批量操作时,要注意数量,防止过多造成网络阻塞,或者redis阻塞。

  • 计数 incr
    incr命令用于自增操作,键不存在时,创建一个,返回1。除了自增,还有自减命令(decr)
127.0.0.1:6379> incr key1
(integer) 1
127.0.0.1:6379> incr key1
(integer) 2

字符串类型的内部编码有三种,int:8字节的长整形,embstr:小于等于39字节的字符串,raw:大于39字节的字符串。
redis根据具体场景选择不同的编码。

使用场景
  • 缓存
    通过设置键的过期时间等作为后端的缓存层,降低后端压力
  • 计数
    字符串类型有incr自增,可作为计数器使用
  • 共享数据
    如分布式web服务器共享session
  • 限制
    SET key value EX 60 NX
    通过过期时间和NX选项达到限速功能,比如每分钟最多访问多少次,可以结合应用代码实现。

哈希(hash)

        hash是形如键值对的类型,比如python中的字典。

常用命令
  • 设置值 hset
    时间复杂度O(1)
    hset key field value
127.0.0.1:6379> hset user:1 name z
(integer) 1

同样,也有hsetnx命令,作用同string的setnx

  • 获取值 hget
    时间复杂度O(1)
    hget key field
127.0.0.1:6379> hget user:1 name
"z"
  • 删除 hdel
    时间复杂度O(n)
    hdel key field [field ...]
    删除指定key下的一个或者多个field。
  • 计算filed的个数 hlen
    hlen key
  • 批量设置和获取
    时间复杂度O(k)
    hmget key field [field ...] # 可以获取指定key下的一个或者多个field。
    hmset key field value [field value ...] # 可以设置指定key下的一个或者多个field。
  • 判断field是否存在 hexists
    时间复杂度O(1)
    hexists key field
  • 获取所有field
    时间复杂度O(n)
    hkeys key
127.0.0.1:6379> hkeys user:1
1) "name"
  • 获取所有value
    时间复杂度O(n)
    hvals key
127.0.0.1:6379> hvals user:1
1) "z"
  • 获取所有field-value
    时间复杂度O(n)
    hgetall key
    注意,使用hgetall时,如果元素过多,存在阻塞redis的风险,如果一定要使用,可以使用hscan命令,渐进遍历。
127.0.0.1:6379> hgetall user:1
时间复杂度O(n)
1) "name"
2) "z"

同样,hash也有好几种内部编码,根据不同场景选择合适的。

使用场景
  • 缓存
    比如缓存用户信息时,每个用户有很多属性,比字符串更加直观,但是会消耗更多的内存。

列表(list)

        list可以用于存储多个有序的字符串,一个list最多可以存储2**32-1个元素。可以充当队列和栈。开发中有很多使用场景。

常用命令
  • 添加 插入 查找
    rpush key value [value ...] #从右边插入n个元素 时间复杂度O(n)
    lpush key value [value ...] #从左边插入n个元素 时间复杂度O(n)
    linsert key before|after p value # 向元素为p的前或者后插入value 时间复杂度O(n)
    lrange key start end # 查找元素 时间复杂度O(s+n) s是start的偏移量,n是范围值
    lindex key index # 获取指定下标元素 时间复杂度O(n)
    llen key # 获取list长度 时间复杂度O(1)
127.0.0.1:6379> rpush l a s 1
(integer) 3
127.0.0.1:6379> lpush l k n
(integer) 5
127.0.0.1:6379> linsert l before k k1
(integer) 6

127.0.0.1:6379> lrange l 0 -1
1) "n"
2) "k1"
3) "k"
4) "a"
5) "s"
6) "1"
  • 删除
    lpop key # list左侧弹出 时间复杂度O(1)
    rpop key # list右侧弹出 时间复杂度O(1)
    lrem key count value # 删除指定元素 count>0:从左到右,删除最多count个元素;
    count<0:从右到左,删除最多count的绝对值个元素;count,删除所有。 时间复杂度O(n)
    ltrim key start end # 按照范围裁剪,保留start到end的元素 时间复杂度O(n)
    lset key index newValue # 修改指定index的元素 时间复杂度O(n)
127.0.0.1:6379> lpop l
"n"
127.0.0.1:6379> rpop l
"1"
127.0.0.1:6379> lrem l 1 k
(integer) 1
127.0.0.1:6379> lrange l 0 -1
1) "k1"
2) "a"
3) "s"
127.0.0.1:6379> ltrim l 0 1
OK
127.0.0.1:6379> lrange l 0 -1
1) "k1"
2) "a"
127.0.0.1:6379> lset l 0 k2
OK
127.0.0.1:6379> lrange l 0 -1
1) "k2"
2) "a"

阻塞操作:
blpop key [key ...] timeout #list空时,timeout时间内返回,当timeout=0,无限阻塞下去. 时间复杂度O(1)
brpop key [key ...] timeout
如在一个客户端执行
127.0.0.1:6379> brpop list:test 3
另一个客户端做插入动作,观察第一个客户端情况。

使用场景
  • 消息队列
    lpush+brpop组合命令可以实现阻塞队列。
    lpush+lpop组合命令可以实现栈等

集合(set)

        set也是可以用于存储多个有序的字符串,但是里面的元素是无序的,而且没有重复的。一个set里最多有2**32-1个元素,set除了增删改查,还有交集并集的操作。

常用命令
  • 添加 删除 查找 判断
    sadd key element [element ...] #往集合添加n个元素。时间复杂度O(n)
    srem key element [element ...] #删除集合中n个元素。时间复杂度O(n)
    scard key #计算元素个数。 时间复杂度为O(1)
    sismember key element # 判断元素是否在集合中。时间复杂度O(1)
    srandmember key [count] # 随机返回count个元素 默认为1。时间复杂度O(count)
    spop key # 弹出元素。时间复杂度O(1)
    smembers key # 获取所有元素。时间复杂度O(n)
127.0.0.1:6379> sadd s a b c 1 2 3
(integer) 6
127.0.0.1:6379> srem s b
(integer) 1
127.0.0.1:6379> scard s
(integer) 5
127.0.0.1:6379> sismember s a
(integer) 1
127.0.0.1:6379> srandmember s 3
1) "c"
2) "3"
3) "a"
127.0.0.1:6379> spop s
"1"
127.0.0.1:6379> smembers s
1) "c"
2) "2"
3) "3"
4) "a"
  • 集合间操作
    sinter key [key ...] # 多个集合交集。时间复杂度O(m*k),m是键数,k是多个set中元素最少的个数
    suinon key [key ...] # 多个集合并集。时间复杂度O(n)
    sdiff key [key ...] # 多个集合差集。时间复杂度O(n)
    sinterstore destination key [key ...] # 保存交集结果到destination
    suionstore destination key [key ...] # 保存并集结果到destination
    sdiffstore destination key [key ...] # 保存差集结果到destination

127.0.0.1:6379> smembers s
1) "c"
2) "2"
3) "3"
4) "a"
127.0.0.1:6379> sadd s1 1 2 3 a b c
(integer) 4
127.0.0.1:6379> sinter s s1
1) "c"
2) "2"
3) "3"
4) "a"
127.0.0.1:6379> sunion s s1
1) "6"
2) "1"
3) "c"
4) "5"
5) "2"
6) "3"
7) "b"
8) "a"
127.0.0.1:6379> sdiff s s1
(empty list or set)
127.0.0.1:6379> sdiff s1 s
1) "6"
2) "5"
3) "1"
4) "b"
127.0.0.1:6379> sinterstore d1 s s1
(integer) 4
127.0.0.1:6379> smembers d1
1) "2"
2) "3"
3) "c"
4) "a"
使用场景
  • 用户标签(无重复特性)
  • 随机数
    spop/srandmember 命令组合生成随机数

有序集合(zset)

        有序集合保留了set的无重复特性,但是比之多一个特点是有序,给每个元素设置一个score作为排行依据,score的值可以重复。

常用命令
  • 添加
    zadd key [NX|XX] [CH] [INCR] score member [score member ...] # 添加n个成员。复杂度为O(log(n))
    nx和xx选项和string上面的等意义一样
    ch选项返回此次操作,有序集合元素和分数发生变化的个数
    incr选项对score做增加
127.0.0.1:6379> zadd zs 2 a 20 b 200 c
(integer) 3
  • 计算成员
    zcard key # 获取元素个数。复杂度为O(1)
    zscore key member # 获取元素的分数
    zrank key member # 获取元素排名
    zrevrank key member # 获取元素排名反向
    zincrby key increment member # 增加元素分数
    zrange key start end [withscores] # 返回指定排名范围的成员
    zrevrange key start end [withscores]
127.0.0.1:6379> zadd zs 2 a 20 b 200 c
(integer) 3
127.0.0.1:6379> zcard zs
(integer) 3
127.0.0.1:6379> zscore zs b
"20"
127.0.0.1:6379> zrank zs b
(integer) 1
127.0.0.1:6379> zincrby zs 30 b
"50"
127.0.0.1:6379> zrange zs 1 3
1) "b"
2) "c"
  • 删除
    zrem key member [member ...] # 删除n个元素

  • 集合操作
    交集并集差集

参考书

《redis开发与运维(付磊)》

你可能感兴趣的:(redis 详解系列之一(基础知识))