jedis mysql 数据结构_Redis的数据结构和内部编码

Redis的数据结构概述

0b0a53cc9cbdd8441ede69b3ba5ec7ec.png

9df91db2b4e3369d67073b904921a7cd.png

redis是单线程,一次只执行一条命令,那为什么可以这么快:

纯内存

非阻塞IO

避免线程切换和竞态消耗

在使用过程中要注意:

一次只运行一条命令

避免长(慢)命令,例如keys、flushall、flushdb、slow sua script、multi/exec、operate big value(collection)

redis在 fsync file descriptior、close file descriptor时会有独立的线程来执行

Redis的5种数据结构

1、string 字符串

字符串的键值结构

string类型的value值可以是字符串、int、二进制、或者一些json、xml、object序列化的字符串。

字符串的类型不能大于512MB。

86c26ac4dbc523f8336b9865e65155e0.png

字符串的常用命令

get key

获取key的value值,时间复杂度为O(1)

set key value

设置key的值为value, 时间复杂度为O(1)

del key

删除key,时间复杂度为O(1)

127.0.0.1:6379> set hello world

OK

127.0.0.1:6379> get hell

(nil)

127.0.0.1:6379> get hello

"world"

127.0.0.1:6379> del hello

(integer) 1

127.0.0.1:6379> get hello

(nil)

set命令还有一些参数,例如

setnx key value,是key不存在时才能设置

set key value xx,是key存在时才能设置

setex key seconds value, 设置key的value值同时设置过期时间

127.0.0.1:6379> exists php

(integer) 0

127.0.0.1:6379> set php good

OK

127.0.0.1:6379> setnx php bad

(integer) 0

# php已经存在,不能修改

127.0.0.1:6379> get php

"good"

127.0.0.1:6379> set php bad xx

OK

127.0.0.1:6379> get php

"bad"

127.0.0.1:6379> setex php 20 good

OK

127.0.0.1:6379> ttl php

(integer) 15

127.0.0.1:6379> get php

(nil)

get、set命令还有一些批量操作mget、mset,

mget key1 key2 key3..., 批量获取key,原子操作,时间复杂度为O(n)

mset key1 value1 key2 value2 key3 value3...,批量设置key,原子操作,时间复杂度为O(n)

127.0.0.1:6379> mset test1 value1 test2 value2 test3 value3

OK

127.0.0.1:6379> mget test1 test2 test3

1) "value1"

2) "value2"

3) "value3"

1ff6a28e2bec9e65f1711cc645ce5370.png

如果是n次get,那么网络时间是很大的开销,使用mget可以批量地将命令传输给redis,减少网络时间。

956fe7c7825a3defed8f64e96855fe69.png

当网络时间越长,或者命令越多,效果月明显,当然也不能无限制的使用,命令如果太多,可以拆分几次执行。

getset key newvalue

set key newvalue并返回旧的value。时间复杂度为O(1)。

append key value

将value追加到旧的value中。时间复杂度为O(1)。

strlen key

返回value的长度,注意中文,一个中文占用3个字节。时间复杂度为O(1)。

127.0.0.1:6379> set hello world

OK

127.0.0.1:6379> getset hello php

"world"

127.0.0.1:6379> get hello

"php"

127.0.0.1:6379> append hello ",java"

(integer) 8

127.0.0.1:6379> get hello

"php,java"

127.0.0.1:6379> strlen hello

(integer) 8

127.0.0.1:6379> set hello "你好"

OK

127.0.0.1:6379> strlen hello

(integer) 6

incr key

key自增1,如果key不存在,则自增后get(key)=1。时间复杂度为O(1)。

decr key

key自减1,如果key不存在,则自减后get(key)=-1。时间复杂度为O(1)。

incrby key k

key自增k,如果key不存在,则自增后get(key)=k。时间复杂度为O(1)。

decrby key k

key自减k,如果key不存在,则自减后get(key)=-k。时间复杂度为O(1)。

127.0.0.1:6379> get counter

(nil)

127.0.0.1:6379> incr counter

(integer) 1

127.0.0.1:6379> get counter

"1"

127.0.0.1:6379> incrby counter 99

(integer) 100

127.0.0.1:6379> get counter

"100"

127.0.0.1:6379> decr counter

(integer) 99

127.0.0.1:6379> get counter

"99"

127.0.0.1:6379> decrby counter 10

(integer) 89

如果数据不是int类型,会提示“(error) ERR value is not an integer or out of range”

incrbyfloat key k

key增加float类型k值,如果key不存在,则自增后get(key)=1。时间复杂度为O(1)。

getrange key start end

获取key指定start到end下标的所有值

setrange key index value

设置key的下标index所对应的值

127.0.0.1:6379> incr counter

(integer) 1

127.0.0.1:6379> incrbyfloat counter 3.8

"4.8"

127.0.0.1:6379> get counter

"4.8"

127.0.0.1:6379> set test1 testvalue

OK

127.0.0.1:6379> getrange test1 0 4

"testv"

127.0.0.1:6379> setrange test1 2 ave

(integer) 9

127.0.0.1:6379> get test1

"teavealue"

字符串的使用场景

字符串数据结构常用来缓存、计数器和分布式锁等应用场景中。

在一些实战中的应用场景:

1.记录网站每个用户个人主页的访问量

incr userid:pageview

# 例如用户的userid为123456

incr 1234:pageview

2.缓存视频基础信息(数据源在mysql中)

5b0cf9a839a3b791080686b252d47976.png

# 伪代码如下

public VideoInfo get(long id){

String redisKey = redisPrefix + id;

VideoInfo videoInfo = redis.get(redisKey);

if(videoInfo == null){

videoInfo = mysql.get(id);

if(videoInfo != null){

//序列化

redis.set(redisKey, serialize(videoInfo))

}

}

return videoInfo;

}

3.分布式id生成器

多个应用并发获取ID,ID不会重复完全自增。可能在实际中会更为复杂,但这是基础思路

53e213088653ed257e5bb199291fadc1.png

incr id(原子操作)

2、hash 哈希

hash的键值结构

feea93ebe74f1dda97068f084f955cc8.png

可以单独添加、更新、删除field

1369f2d399570be16e2a28901f22e1cc.png

注意field是不能相同的,但value可以相同

hash的常用命令

hget key field

获取hash key对应的field的value。时间复杂度为O(1)。

hset key field value

设置hash key对应的field的value值。时间复杂度为O(1)。

hdel key field

删除hash key对应的field。时间复杂度为O(1)。

127.0.0.1:6379> hset user:1:info name nancy

(integer) 1

127.0.0.1:6379> hset user:1:info age 18

(integer) 1

127.0.0.1:6379> hgetall user:1:info

1) "name"

2) "nancy"

3) "age"

4) "18"

127.0.0.1:6379> hdel user:1:info age

(integer) 1

127.0.0.1:6379> hgetall user:1:info

1) "name"

2) "nancy"

hexists key field

判断hash key是否有field

hlen key

获取hash key的数量

127.0.0.1:6379> hgetall user:1:info

1) "name"

2) "nancy"

127.0.0.1:6379> hexists user:1:info name

(integer) 1

127.0.0.1:6379> hlen user:1:info

(integer) 1

hmget key field1 field2 ... fieldN

批量获取hash key的一批field值。时间复杂度为O(n)。

hmset key field1 value1 field2 value2 ... fieldN valueN

批量设置hash key的一批field值。时间复杂度为O(n)。

127.0.0.1:6379> hmset user:1:info name nancy age 18 birth 2002

OK

127.0.0.1:6379> hlen user:1:info

(integer) 3

127.0.0.1:6379> hmget user:1:info name age

1) "nancy"

2) "18"

hgetall key

返回hash key的所有field和value。时间复杂度为O(n)。要小心使用hgetall,如果hash key存储很多的属性

hvals key

返回hash key对应所有field的value值。时间复杂度为O(n)。

hkeys key

返回hash key对应的所有field。时间复杂度为O(n)。

127.0.0.1:6379> hgetall user:1:info

1) "name"

2) "nancy"

3) "age"

4) "18"

5) "birth"

6) "2002"

127.0.0.1:6379> hvals user:1:info

1) "nancy"

2) "18"

3) "2002"

127.0.0.1:6379> hkeys user:1:info

1) "name"

2) "age"

3) "birth"

hsetnx key field value

设置hash key对应的field的value值,如果field已经存在,则失败。时间复杂度为O(1)。

hincrby key field intCount

hash key对应的field的value值自增intCount。时间复杂度为O(1)。

hincrbyfloat key field floatCount

hash key对应的field的value值自增floatCount。时间复杂度为O(1)。

hash的使用场景

1.记录网站用户在个人主页中的访问量

hincrby user:1:info pageview count

string的数据结构也可以做,不过hash就将用户的相关信息作为一个整体,具体看实际的应用场景。

2.缓存视频基础信息(数据源在mysql中)

//伪代码如下

public VideoInfo get(long id){

String redisKey = redisPrefix + id;

Map hashMap = redis.hgetAll(redisKey);

VideoInfo videoInfo = transferMapToVideoInfo(hashMap);

if(videoInfo == null){

videoInfo = mysql.get(id);

if(videoInfo != null){

redis.hmset(redisKey, transferVideoInfoToMap(videoInfo))

}

return videoInfo;

}

}

3、list 列表

list的结构特点

a867cfad87119335c70d96c40095c07b.png

列表是有序的,value值是可重复,值可从左右两边插入弹出。

可以对列表进行以下的相关操作

ee7590f9f3d4d35ee41dc98aa678a0f3.png

6a4a05640ec7c7a62eadd283d8ef152c.png

list的常用命令

rpush key value1 value2 ... valueN

从列表key右边插入值1~N个。时间复杂度为O(1~n)

lpush key value1 value2 ... valueN

从列表key左边插入值1~N个。时间复杂度为O(1~n)

linsert key before|after value newValue

在list指定的value值前|后插入newValue值。时间复杂度为O(n)

lpop key

从列表左侧弹出一个item。时间复杂度为O(1)

rpop key

从列表右侧弹出一个item。时间复杂度为O(1)

lrem key count value

根据count的值,从列表中删除等于value的值。

当count>0,则从左到右,删除count个与value相同的项。

当count<0,则从右到左,删除Math.abs(count)个value相等的项。

当count=0,删除所有value相等的项。

时间复杂度为O(n)。

ltrim key start end

按照索引范围修剪列表,只保留start、end要求的索引范围。时间复杂度为O(n)。

lrange key start end

获取列表指定索引范围start到end的所有item。时间复杂度为O(n)。

lindex key index

获取列表指定索引index的item。时间复杂度为O(n)。

llen key

获取列表长度。时间复杂度为O(n)。

lset key index newValue

设置列表指定索引值index为newValue。时间复杂度为O(n)。

127.0.0.1:6379> rpush mylist a b c

(integer) 3

127.0.0.1:6379> lrange mylist 0 -1

1) "a"

2) "b"

3) "c"

127.0.0.1:6379> lpush mylist 0

(integer) 4

127.0.0.1:6379> lrange mylist 0 -1

1) "0"

2) "a"

3) "b"

4) "c"

127.0.0.1:6379> rpop mylist

"c"

127.0.0.1:6379> lrange mylist 0 -1

1) "0"

2) "a"

3) "b"

blpop key timeout

lpop的阻塞版本,一直等待这列表有元素再弹出。timeout是阻塞的超时时间,timeout=0为永远不阻塞。

brpop key timeout

rpop的阻塞版本,一直等待这列表有元素再弹出。timeout是阻塞的超时时间,timeout=0为永远不阻塞。

list的使用场景

可以做一些时间线,例如要显示你关注的人更新微博,可以使用LPUSH

edca0beba71de84c9f1bfd94a974d761.png

4、set 集合

集合的结构

6e051ad2a525924b44f7e2e9383208cf.png

集合的特点:

(1)集合是无序的。

(2)集合是不允许插入重复元素的。

(3)集合支持集合间的API交互。

97902bfd619db7e8990a6f109159d49c.png

集合常用的命令

sadd key element

向集合key添加element,如果element存在,则添加失败。时间复杂度为O(1)。

srem key element

将集合key的element元素移除掉。时间复杂度为O(1)。

scard key

计算集合key的大小

sismember key element

判断元素element是否在集合key中存在。

srandmember key count

随机从集合key取出count个元素。不会破坏集合的数据

spop key

从集合key中随机弹出一个元素。

smembers key

取出集合key中的所有元素。这个命令要小心使用,如果集合中有非常多的元素,执行时间较长可能会阻塞redis。

127.0.0.1:6379> sadd user:1:follow hello world test1 test2

(integer) 4

127.0.0.1:6379> smembers user:1:follow

1) "test1"

2) "test2"

3) "world"

4) "hello"

127.0.0.1:6379> spop user:1:follow

"hello"

127.0.0.1:6379> smembers user:1:follow

1) "test1"

2) "test2"

3) "world"

127.0.0.1:6379> scard user:1:follow

(integer) 3

127.0.0.1:6379> sismember user:1:follow hello

(integer) 0

集合间的API

sdiff key1 key2

集合key1和key2的差集。

sinter key1 key2

集合key1和key2的交集。

sunion key1 key2

集合key1和key2的并集。

sdiffstore destkey key1 key2

将集合key1和key2差集的计算结果保存到集合destkey中。

sinterstore destkey key1 key2

将集合key1和key2交集的计算结果保存到集合destkey中。

sunionstore destkey key1 key2

将集合key1和key2并集的计算结果保存到集合destkey中。

127.0.0.1:6379> sadd user:1:follow it music his sport

(integer) 4

127.0.0.1:6379> sadd user:2:follow it news ent sport

(integer) 4

127.0.0.1:6379> sdiff user:1:follow user:2:follow

1) "music"

2) "his"

127.0.0.1:6379> sinter user:1:follow user:2:follow

1) "sport"

2) "it"

127.0.0.1:6379> sunion user:1:follow user:2:follow

1) "news"

2) "it"

3) "his"

4) "music"

5) "ent"

6) "sport"

127.0.0.1:6379> sunionstore follows user:1:follow user:2:follow

(integer) 6

127.0.0.1:6379> smembers follows

1) "news"

2) "it"

3) "his"

4) "music"

5) "ent"

6) "sport"

set的使用场景

1.抽奖系统。可以将抽奖人存在集合中,然后使用spop来随机弹出一个元素。

2.一些社交网站上的like、点赞、踩功能。

3.一些标签tag功能,譬如可以给用户添加标签,给标签添加用户

> sadd user:1:tag tag1 tag2 tag3...

> sadd user:2:tag tag2 tag3...

> sadd tag:1:users user1 user2...

> sadd tag:2:users user2 user3...

4.集合间的API应用场景,例如微博中的共同关注的好友,或者共同关注的兴趣。

应用场景总结起来有几个:

SADD 做一些标签场景

SPOP/SRANDMEMBER 做随机数相关场景

SADD + SINTER 做社交相关的场景

5、zset 有序集合

有序集合的结构

fb53674b7162fd88503b83ab9f1d9af6.png

集合和有序集合的对比:

(1)两者都是没有重复元素的。

(2)集合是无序的,有序集合是有序的。

(3)集合只有元素element,有序集合有元素element和分值score。

有序集合的常用命令

zadd key score1 element1 score2 element2 ...

添加scord和element。score和element可以是多对。时间复杂度为O(logN)。

zrem key element(可以是多个)

删除1个或多个元素。时间复杂度为O(1)。

zscore key element

返回元素element的分值score。时间复杂度为O(1)。

zincrby key increScore element

增加或减少元素element的score分值。如果increScore为负数则为减少。时间复杂度为O(1)。

zcard key

返回元素的总个数。

zrank key element

获取元素element的从小到大升序的排名,排名从0开始算。

zrevrank key element

获取元素element的从大到小降序的排名,排名从0开始算。

zrange key start end [withscores]

返回有序集合key从索引为start到end的所有升序元素,如果带着withescore参数,则会将元素的分值也一同返回。时间复杂度为O(log(n)+m),n代表有序集合的元素个数,m代表要返回的元素个数。

zrevrange key start end [withscores]

返回有序集合key从索引为start到end的所有降序元素

zrangbyscore key minScore maxScore [withscores]

返回有序集合key指定分值范围minScore到maxScore范围内的升序元素。时间复杂度为O(log(n)+m)。

zrevrangbyscore key maxScore minScore [withscores]

返回有序集合key指定分值范围maxScore到minScore范围内的降序元素。

zcount key minScore maxScore

返回有序集合key指定分值范围minScore、maxScore内的个数。时间复杂度为O(log(n)+m)。

zremrangebyrank key start end

删除有序集合key指定排名内的升序元素。时间复杂度为O(log(n)+m)。

zremrangebyscore key minScore maxScore

删除有序集合key指定分值内的升序元素。时间复杂度为O(log(n)+m)。

zinterstore destkey key1 key2

将有序集合key1和key2交集的计算结果保存到集合destkey中。

zunionstore destkey key1 key2

将有序集合key1和key2并集的计算结果保存到集合destkey中。

127.0.0.1:6379> zadd user:1:score 100 nancy1 98 nancy2 70 nancy3 50 nancy4

(integer) 4

127.0.0.1:6379> zscore user:1:score nancy3

"70"

127.0.0.1:6379> zcard user:1:score

(integer) 4

127.0.0.1:6379> zrem user:1:score nancy2

(integer) 1

127.0.0.1:6379> zrank user:1:score nancy4

(integer) 0

127.0.0.1:6379> zrank user:1:score nancy3

(integer) 1

127.0.0.1:6379> zrange user:1:score 0 -1 withscores

1) "nancy4"

2) "50"

3) "nancy3"

4) "70"

5) "nancy1"

6) "100"

127.0.0.1:6379> zadd user:1:score 100 nancy1 98 nancy2 70 nancy3 50 nancy4

(integer) 1

127.0.0.1:6379> zrange user:1:score 0 -1

1) "nancy4"

2) "nancy3"

3) "nancy2"

4) "nancy1"

127.0.0.1:6379> zcount user:1:score 60 100

(integer) 3

127.0.0.1:6379> zremrangebyscore user:1:score 0 59

(integer) 1

127.0.0.1:6379> zrange user:1:score 0 -1

1) "nancy3"

2) "nancy2"

3) "nancy1"

127.0.0.1:6379> zremrangebyrank user:1:score 0 1

(integer) 2

127.0.0.1:6379> zrange user:1:score 0 -1

1) "nancy1"

zset的应用场景

1.排行榜。如音乐排行榜、销售排行榜,新书排行榜(用timestap作为score)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值