目录
介绍
redis将所有数据存放在内存中(纯内存访问,100ns),还可以将内存的数据利用快照和日志的形式保存到硬盘上。官方给出的读写性能是10w/s,使用C语言实现的单线程架构,预防了多线程可能产生的竞争问题(避免线程切换和竞态产生的消耗)。使用I/O多路复用,非阻塞I/O。
使用场景:缓存,提供键过期功能、内存溢出淘汰策略;排行榜,提供列表和有序集合;计数系统,O(1)的计数功能;社交网络;消息队列,提供简单的发布订阅和阻塞队列功能。
不适用场景:数据量太大,冷数据,浪费内存。
redis版本号中第二位如果是奇数则表示非稳定版本,redis3.0增加了redis cluster分布式实现。
内部编码
每种数据结构底层有自己的内部编码实现,而且都有两种以上,例如list有linkedlist和ziplist两种实现,ziplist比较节省内存,但是元素比较多的情况下性能有所下降。redis会在合适的场景选择合适的内部编码。objects encoding key可以查看内部编码。
一、常用命令
通用命令,不分数据结构:
命令 | 说明 | 复杂度 |
keys * | 查看所有键 | O(n),遍历所有键 |
dbsize | 键总数 | O(1),内置键总数变量 |
exists key | 键是否存在,存在返回1 | |
del key [key ...] | 删除key,返回删除成功的个数 | |
expire key seconds | 添加键过期时间,单位是秒 | |
ttl key | 查看键过期时间,返回-1没设置过期时间,-2键不存在 | |
type key | key的数据结构类型 |
String
键是字符串类型,值可以是字符串(string, json, xml)、数字(整数, 浮点数)、二进制(图片, 音频, 视频),值最大不超过512MB。
命令 | 说明 | 复杂度 |
set key value [ex seconds] [px milliseconds] [nx|xx] | 设置键值对 ex seconds:键秒级过期时间 px million:毫秒级过期时间 nx:键不存在时设置成功 xx:键存在时更新,不存在不成功 | O(1) |
setex key seconds value | 和 set key ex seconds 一样 | O(1) |
setnx key value | 和 set key value nx一样,多个客户端执行setnx时只有一个会成功,可以作为分布式锁的实现方案 | O(1) |
get key | O(1) | |
mset key value [key value ...] | 批量设置,节省网络传输时间 | O(k),k = key个数 |
mget key [key ...] | 批量获取 | 同上 |
incr key decr key,自减 incrby/decrby key increment,自增/自减指定数 incrbyfloat key increment,自增指定浮点数 | 对value自增,返回自增后的结构;value不是整数返回error;键不存在时按值=0自增,返回1 | O(1) |
不常用命令 | ||
append key value | 字符串尾部追加值 | O(1) |
strlen key | value长度 | O(1) |
getset key value | set新值,返回原值 | O(1) |
setrange key offset value | 设置指定位置的字符,offset是下标 | O(1) |
getrange key start end | 获取部分字符串,左闭右闭 | O(n),n = 字符串长度 |
内部编码:根据值的类型和长度决定,int(8字节长整型),embstr(<=39字节的字符串),raw(>39字节的字符串)。
Hash
value本身又是一个键值对结构,key value(field value)。
命令 | 说明 | 复杂度 |
hset key field value | 设置键值对 | O(1) |
hget key field | 获得value | O(1) |
hmset key field value [field value ...] | 批量设置field value | O(k),k = field个数 |
hmget key field [field ...] | 批量获得value | O(k) |
hgetall key | 获得field+value | O(n),n = field总数 |
hkeys key | 获得所有field | O(n),n = field总数 |
hvals key | 获得所有value | O(n),n = field总数 |
hdel key field [field] | 删除一个或多个field | O(k),k = field个数 |
hlen key | 计算field个数 | O(1) |
hexists key field | field是否存在 | O(1) |
内部编码:当key和value都小于一定值时,用ziplist,结构更加紧凑,节省内存。不满足使用ziplist的条件时,用hashtable,读写复杂度O(1),ziplist不满足性能要求。
List
存储多个有序字符串,可以重复,支持索引范围内获取元素。redis中可以对列表两端插入、弹出,可以充当栈和队列的角色。
命令 | 说明 | 复杂度 |
lpush/rpush key value [value ...] | 从左边/右边添加元素 | O(k),k = key个数 |
linsert key before|after pivot value | 向列表指定元素(pivot)前|后插入value | O(n),pivot到表头/尾的距离 |
lrange key start end (lrange key 0 -1:从左到右获取全部元素) | 获取指定范围内的元素 左闭右闭 从左到右下标0 ~ N-1 从右到左下边-1 ~ -N | O(s + n),start的偏移和范围的长度 |
lindex key index | 获取指定下标的value | O(1) |
llen key | 列表长度 | O(1) |
lpop/rpop key | 弹出最左侧/右侧元素 | O(1) |
lrem key count value | 删除值=value的元素 count>0,从左到右删除最多count个 count<0,从右到左删除最多|count|个 count=0,删除所有 | O(n),n = 列表长度 |
ltrim key start end | 只保留列表[start end] | O(n),n = 修剪的元素个数 |
lset key index index newValue | 修改指定下标的元素 | O(n),n = 索引偏移量 |
blpop/brpop key [key ...] timeout | 阻塞式弹出,超时时间内客户端等待 timeout = 0时一直等待 | O(1) |
内部编码:元素较少时ziplist,较多时用linkedlist链表。
使用场景:消息队列(lpush和brpop可实现阻塞队列)、文章列表
Set
存储多个字符串元素,不能重复,无序,不能通过索引下标获取元素。redis支持集合内的增删改查、集合间的并交差。
命令 | 说明 | 复杂度 |
sadd key element [element ...] | 添加元素 | O(k),k = 元素个数 |
srem key element [element ...] | 删除元素 | 同上 |
scard key | 计算元素个数 | O(1) |
sismember key element | 元素是否在集合中 | O(1) |
srandmember key [count默认1] | 随机返回指定个数元素 | O(count) |
spop key | 随机弹出元素 | O(1) |
smembers key | 获取全部元素 | O(n),n = 元素总数 |
集合间操作 | ||
sinter key [key ...] | 交集 | O(m*k),m=集合个数,k=元素最少的集合元素个数 |
sunion key [key ...] | 并集 | O(k),多个集合元素个数和 |
sdiff key [key ...] | 差集 | 同上 |
sinterstore/sunionstore/sdiffstore destination key [key ...] | 保存并交差的结果 | 同上 |
内部编码:intset整数集合(元素个数较少且都是整数),hashtable。
使用场景:标签、社交场景,每个用户感兴趣的标签不同,通过集合可以得到喜欢同一个标签的用户,和用户共同喜好的标签。
Zset
集合元素不能重复,但可以排序。不是和list一样通过下标,它给每个元素设置一个score作为排序的依据。
命令 | 说明 | 复杂度 |
zadd key score member [score member ...] | 添加成员和分数 nx:member不存在时添加成功 xx:member存在时操作成功,用于更新 incr:对score自增 | O(log(n)) |
zcard key | 计算成员个数 | |
zscore key member | 成员分数 | |
zrank/zrerank key member | 返回成员排名,从0开始排,zrank从低到高排,zrerank从高到低 | |
zrem key member [member ...] | 删除成员 | |
zincrby key increment member | 给member增加分数increment | |
zrange/zrerange key start end [withscores] | 返回指定排名范围的成员 [start, end] | |
zrangebyscore/zrerangebyscore key min max [withscores] | 返回指定分数范围内的成员 | |
zcount key min max | 返回指定分数范围成员个数 | |
集合间操作 | ||
zinterstore destination numkeys key [key ...] [weights weight [weight ...] [aggregate sum|min|max] | 交集 destination保存到这个键 numkeys做交集的键个数 weights每个键的权重,默认1,权重*score是最后的score aggregate:得到的交集,成员的分值取sum、min还是max,默认sum | |
zunionstore destination numkeys key [key ...] [weights weight [weight ...] [aggregate sum|min|max] | 并集 |
内部编码:ziplist(默认元素小于128个,且元素值小于64B),skiplist跳表。
使用场景:排行榜系统。
其他
Bitmaps
Bitmaps本省并不是一个数据结构,实际上还是字符串,但是它可以对字符串的位进行操作,可以看做一个以位为单位的数组,每个单元值存储0和1。
命令 | 说明 | 复杂度 |
setbit key offset value | 设置值 | |
getbit key offset | 获取键的第offset位的值 | |
bitcount key [start end] | 获取指定范围内值为1的个数 start, end代表起始和结束字节数 | |
bitop op destkey key [key ...] | bitmaps间的运算 op可取and(交)、or(并)、not(非)、xor(异或) | |
bitpos key targetBit [start] [end] | 计算第一个值=targetBit的偏移量 |
使用场景:统计独立用户数量(例如某个用户id是否访问过网站,在id对应的位上置0或1,不适用访问用户很稀疏的情况,这样每个用户id占一位,但是需要开辟的位很多,中间都是0)。
HyperLogLog
HyperLogLog是一种基数算法,可以用极小的内存空间完成独立总数的统计,数据集可以是IP、Email、ID等。
命令 | 说明 | 复杂度 |
pfadd key element [element ...] | 添加元素 | |
pfcount key [key ...] | 计算独立用户数 | |
pfmerge destkey sourcekey [sourcekey ...] | 求并集 |
HyperLogLog计数存在0.81%的误差率。
GEO
地理位置信息。
命令 | 说明 | |
geoadd key longitude latitude member [...] | 添加经、纬度、成员 geoadd cities 116.28 39.55 beijing | |
geopos key member [member ...] | 获得成员经纬度 | |
gepdist key member1 member2 [unit单位] | 获得成员间的地理位置距离 | |
georadius key longitude latitude radiusm|km|ft|mi [] | 以一个地理位置为中心,算出指定半径内的其他地理信息位置 | |
geohash key member [member ...] | 将二维经纬度转换为一维字符串 |
二、事务
pipeline
Lua
三、客户端
客户端与服务端通信协议
这里的客户端指的是编程语言中的Redis客户端。客户端和服务端之间的通信协议是在TCP协议之上构建的,Redis制定了RESP(Redis序列化协议)实现客户端和服务端的正常交互。
客户端将命令封装成符合RESP的格式,发送一条命令的格式:
// set hello world
// 格式:
*参数数量
$参数1字节数
参数1
$参数2字节数
参数2
...
// 示例
*3
$3
SET
$5
hello
$5
world
// 实际格式
*3\r\n$3\r\nSET\r\n$5hello\r\n$5\r\n$5\r\nworld\r\n
// redis.clients.jedis.Protocol#sendCommand
private static void sendCommand(RedisOutputStream os, byte[] command, byte[]... args) {
try {
os.write((byte)42);
os.writeIntCrLf(args.length + 1);
os.write((byte)36);
os.writeIntCrLf(command.length);
os.write(command);
os.writeCrLf();
byte[][] var3 = args;
int var4 = args.length;
for(int var5 = 0; var5 < var4; ++var5) {
byte[] arg = var3[var5];
os.write((byte)36);
os.writeIntCrLf(arg.length);
os.write(arg);
os.writeCrLf();
}
} catch (IOException var7) {
throw new JedisConnectionException(var7);
}
}
redis-cli按照RESP进行结果解析,我们只能看到最终的结果,服务端返回的结果的格式:
// RESP格式在返回结果前加相应代码
状态回复:+ // 例如客户端发送set, 服务端回复+OK
整数回复:: // 客户端执行incr, 回复:1
字符串回复:$ // get hello, 回复$5 world
多个字符串:* // mget
Java客户端Jedis
使用方法:
// 直连方式, 生产环境一般使用连接池JedisPool管理Jedis连接, 预先初始化好Jedis连接, 每次使用时从Jedis连接池中借用, 不需要每次创建TCP连接, 还能限制Jedis对象个数
Jedis jedis = new Jedis("127.0.0.1", port); // Redis实例的ip和端口
// 方法与redis-cli对应, redis.clients.jedis.JedisCommands
Jedis提供连接池JedisPool,通过GenericObjecPoolConfig设置JedsiPool属性:
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
// 常用的有:设置最大连接数为默认值的5倍
poolConfig.setMaxTotal(GenericObjectPoolConfig.DEFAULT_MAX_TOTAL * 5);
// 设置最大空闲连接数为默认值的3倍
poolConfig.setMaxIdle(GenericObjectPoolConfig.DEFAULT_MAX_IDLE * 3);
// 设置最小空闲连接数为默认值的2倍
poolConfig.setMinIdle(GenericObjectPoolConfig.DEFAULT_MIN_IDLE * 2);
// 设置开启jmx功能
poolConfig.setJmxEnabled(true);
// 设置连接池没有连接后客户端的最大等待时间(单位为毫秒)
poolConfig.setMaxWaitMillis(3000);
// 初始化Jedis连接池
JedisPool jedisPool = new JedisPool(poolConfig, "127.0.0.1", 6379);
lettuce API
提供三种API
-
sync(同步),RedisCommands,在所有命令调用之后立即返回结果
-
async(异步),RedisAsyncCommands,所有方法执行返回的结果都是RedisFuture
-
reactive(反应式),RedisReactiveCommands
待补充