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开发与运维(付磊)》

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容

  • 转载:Redis 宝典 | 基础、高级特性与性能调优 本文由 DevOpsDays 本文由简书作者kelgon供稿...
    meng_philip123阅读 3,112评论 1 34
  • 本文为笔者对在学习Redis过程中所收集资料的一个总结,目的是为了以后方便回顾相关的知识,大部分为非原创内容。特此...
    EakonZhao阅读 14,407评论 0 9
  • Redis是啥 Redis是一个开源的key-value存储系统,由于拥有丰富的数据结构,又被其作者戏称为数据结构...
    一凡呀阅读 1,171评论 0 5
  • Redis的内存优化 声明:本文内容来自《Redis开发与运维》一书第八章,如转载请声明。 Redis所有的数据都...
    meng_philip123阅读 18,873评论 2 29
  • 1.数据结构 1.1字符串 字符串类型的值实际可以是字符串、数字(整数,浮点数),甚至是二进制(图片、视频)...
    Sponge1128阅读 1,225评论 0 0