一、redis 安装
1.1 编译 redis
检验 gcc
gcc -v
安装 gcc
sudo apt-get install gcc g++
下载 redis 安装包 [redis.io] 并解压
tar -zxvf redis-6.2.2.tar.gz
进入解压目录编译源文件
cd cd redis-6.2.2;make
等待编译结束
安装 redis 到指定目录
make PREFIX=~/redis install
复制源文件中的 redis.conf 到安装目录
cp ../redis-6.2.2/redis.conf ./
这时候除了安装目录,其他文件都可以删了
1.2 启动 redis
redis 有两种启动方式:前台启动和后台启动
前台启动[不推荐]:运行 redis-server
启动成功后该 session 将阻塞无法使用
后台启动[推荐]:修改配置文件 redis.conf 将 daemonize no 改为 daemonize yes 后,指定配置文件启动
./redis-server ../redis.conf
1.3 停止 redis
./redis-cli shutdown
或
ps -ef | grep redis
kill -9 PID
推荐第一种,更加优雅
二、redis 操作
2.1 操作 key
查看所有符合条件的 key,支持正则,返回符合条件的 key,没有返回 (empty array)
keys pattern
判断某些 key 是否存在,返回存在的个数
exists key [key ...]
查看某个 key 的数据类型,返回这个 key 对应 value 的数据类型
type key
删除某个 key,删除返回 1,未删除(key不存在)返回 0
del key
异步删除某个 key,仅将 key 从 keyspace 中删除,真正的数据删除在后续异步操作中
unlink key
给 key 设置过期时间,单位秒,到达指定时间 key 自动删除,设置成功返回 1,key 不存在返回 -1
expire key seconds
查看 key 的过期时间,返回将要过期的秒数,-1表示永不过期,-2表示已过期或不存在
ttl key
切换数据库,redis 默认 16 个数据库 0-15,默认为 0,指定数据库索引即可
select index
查看当前数据库的 key 的个数
dbsize
清空当前数据库
flushdb
清空所有数据库
flushall
2.2 操作 string
- string 是 redis 最基础的类型且二进制安全,因此 string 可以包含任意数据,包括视频、图片等;redis 的 string 最多可以是 512M
- redis 的 string 是可以修改的字符串,内部结构采用预分配冗余空间的方式减少内存的频繁分配,类型 java 的 ArrayList 扩容,当 string 长度小于 1M 时,每次增加现有空间,当长度大于 1M 时,每次增加 1M
设置一个键值对,多次设置会覆盖原有 key 的 value
set key value
设置一个键值对,当 key 不存在是设置成功返回 1,key 存在设置失败,返回 -1
setnx key value
设置一个键值对,同时指定过期时间,单位秒,多次设置覆盖原有 key 的 value,同时刷新过期时间
setex key seconds value
获取 key 对应的 value,key 不存在返回 (nil)
get key
将给定的 value 追加到原有 key 的 value 的末尾,返回最终 value 的长度,若 key 不存在则新建
append key value
将 key 存储的值自增 1,若 value 不是数字类型则报错,返回自增后的数值
incr key
指定自增步长,功能类似 incr
incrby key increment
将 key 存储的值自减 1,若 value 不是数字类型则报错,返回自减后的数值
decr key
指定自减步长,功能类似 decr
decrby key increment
同时设置一个或多个键值对
mset key value [key value ...]
同时获取一个或多个键值对
mget key [key ...]
同时设置多个不存在的键值对,具有原子性,一个设置失败全部设置失败,成功返回 1,失败返回 0
msetnx key value [key value ...]
获取 key 的返回值,类型 substring,下标从 0 开始,包含开始和结束位置,end = -1 表示全部
getrange key start end
获取旧值同时设置新值,若 key 不存在,返回 (nil) 同时设置新值
getset key value
2.3 操作 list
- redis 的 list 单键多值,底层为双向链表,对两端操作性很高,查询效率相对较低
- 当 list 元素较少时会使用一块连续的内存存储,即 ziplist
- 当 list 元素较多时会将若干个ziplist通过双向指针连接起来形成链表,即 quicklist
- redis 的 list 即满足了快速插入删除性能,也不会出现太大的空间冗余
从左边插入一个或多个值,返回当前 key 的元素个数
lpush key element [element ...]
注:插入多值想清楚顺序,如 lpush k1 v1 v2 v3,则获取 k1 是 v3 v2 v1,即依次放到最左边
从右边插入一个或多个值,返回当前 key 的元素个数
rpush key element [element ...]
按照索引获取元素,顺序是从左到右,key 不存在返回 (empty array)
lrange key start stop
注:索引从 0 开始,获取 list 所有值:lrange k1 0 -1
从左边吐出一个或多个值,返回吐出的值,key 不存在返回 (nil)
lpop key [count]
注:默认吐出一个;吐出的值会从原 list 中删除,当 key 全部吐完这个 key 就不存在了
从右边吐出一个或多个值,返回吐出的值,key 不存在返回 (nil)
rpop key [count]
从一个 list 右边吐出一个值,插入到另一个 list 的左边,返回吐出的值,待吐出的 key 不存在返回(nil),命令不会产生效果,待插入的 key 不存在会被创建
rpoplpush source destination
注:源和目标 list 可以是同一个,效果即为把右边的值放到左边
获取 list 长度,若 key 不存在返回 0
llen key
按照索引获取元素(不删除),顺序从左到右,返回索引对应的值,若 key 不存在或索引越界返回(nil)
lindex key index
在 list 中某个值的前面或者后面插入一个新值,返回最终 list 长度,若 key 或者 pivot 不存在返回 -1
linsert key before|after pivot element
注:若 list 有多个 piovt,只会对第一个元素生效,顺序从左到右
从左边开始顺序删除若干个特定值,返回删除的元素个数,若 key 不存在或待删除元素不存在返回 -1
lrem key count element
设置指定下标元素的值,成功返回 ok,key 不存在返回 err no such key,索引越界返回 err index out of range
lset key index elemet
2.4 操作 hash
- redis 的 hash 是一个键值对集合,即 value 对应一个 field 和 val 的映射表
- 类比 java:key - Map<String,String>,因此 redis 的 hash 适合存储对象
- 当 hash 数据量小使用ziplist(压缩列表),数据量大使用hashtable(哈希表)
设置 hash 的值,返回新增的 value 的映射个数,多次执行回覆盖原值返回 0
hset key field value [field value]
获取 hash 的值,若 key 或 field 不存在返回 (nil)
hget key field
获取 hash 的所有值,格式为 field 后面紧跟它的 value
hgetall key
判断指定 key 的 field 是否存在,存在返回 1,反之返回 0
hexists key field
获取 hash 的所有 field
hkeys key
获取 hash 的所有 value
hvals key
给 hash 的 field 对应的 value 自增 n,若 key 或 field 不存在新建并返回 value 值,若 对应的 value 不是数字类型报错
hincrby key field increment
当指定的 field 不存在时设置,存在不设置
hsetnx key field value
2.5 操作 set
- redis 的 set 提供了和 list 一样的列表功能,区别于 list 的是 set 元素无序不可重复
- redis 的 set 底层是 hash,所有的 value 指向同一个内部值,类比 java 的 HashSet
- 底层结构决定 set 添加、删除、查找的复杂度都是O(1)
设置 set 的值,已存在的值会被忽略,返回设置成功的元素个数
sadd key member [member ...]
获取 set 所有值,key 不存在返回(empty array)
smembers key
判断 set 中是否存在某个值。存在返回 1,不存在返回 -1
sismember key member
获取 set 元素个数,key 不存在返回 0
scard key
删除 set 指定元素,返回删除成功的个数
srem key member [member ...]
从 set 中随机吐出(删除)若干个元素,全部吐完 key 自动删除,返回被删除的元素
spop key [count]
注:默认删除一个,可指定数量
从 set 随机取 n 个元素(不删除)
srandmember key [count]
把一个集合的一个值移动到另一个集合中,若 source 的不存在命令不生效,若 destination 不存在会被创建,成功返回 1,反之返回 0
smove source destination member
取若干 set 的交集
sinter key [key ...]
取若干 set 的并集
sunion key [key ...]
取若干 set 的差集
sdiff key [key ...]
2.6 操作 zset
- redis 的 zset 提供了和 set 想似的功能,但每个内部成员都维护一个分数 score
- zset 的 score 被用来按照从低到高的方式排序集合的成员
- zset 的底层结构为 hash,每个 field 对应的 value 存储分数 score
- 为了实现根据 socre 的返回获取元素,zset 使用了跳跃表给元素根据 score 排序
设置 zset 的值,返回新增的元素个数,若 value 存在则覆盖原值
zadd key source member [source member ...]
返回 zset 下标在 start 和 stop 之间的元素,下标从 0 开始且为闭区间;元素按照分数从小到大排序
zrange key start stop [withscores]
注:withscores:显示元素的分数 score
返回 zset 下标在 start 和 stop 之间的元素,下标从 0 开始且为闭区间;元素按照分数从大到小排序
zrevrange key start stop [withscores]
返回 zset 分数在 min 和 max 之间的元素,为闭区间;元素按照分数从小到大排序
zrangebyscore key min max [withscores]
注:指定全体技巧:min -> -inf ; max -> +inf,下同
返回 zset 分数在 min 和 max 之间的元素,为闭区间;元素按照分数从大到小排序
zrevrangebyscore key max min [withscores]
统计 zset 指定分数范围内的元素个数
zcount key min max
删除 zset 指定元素,返回删除成功个数
zrem key member [member ...]
返回 zset 指定元素的排名,排名从小到大从 0 开始,指定的元素不存在返回 (nil)
zrank key member
2.7 操作 bitmaps
- bitmaps 本身不是一种数据类型,是可以提供位运算的字符串,可以理解为只能存储 0、1 的数组
- 其中数据的下标在 bitmaps 中称为偏移量
- 合理的使用位运算可以有效的提高内存使用率和开发效率
bitmaps 本身就是一个 string
127.0.0.1:6379> type k1
string
设置 bitmaps 某个偏移量的值
setbit key offset value
偏移量从 0 开始,且 string 最大容量为 512M,因此偏移量最大值为 8*512*1024*1024-1 = 2^32-1,减一是因为 c 语言中字符串末尾需要存储一个分隔符
获取 bitmaps 某个偏移量的值
getbit key offset
获取指定范围内的偏移量中 1 的个数
bitcount key [start end]
不指定范围返回全部 1 的个数
多个 bitmaps 做交、并、差等操作
bitop operator destkey key [key ...]
operator可以取:and(交集)、or(并集)、not(非)、xor(异或),并将结果写入 destkey中
应用场景
用户签到
用户当天签到记 1,未签到记 0(默认就为 0),考虑使用 bitmaps 存储,其中
key :用户id_年份
offset :今天是一年中的第几天
活跃用户
记录所有注册用户每日活跃情况,其中
key:日期
offset:用户 id
每日用户活跃在对该用户的 id 的 offset 设置为 1
对于上述两个情况,可以做很多分析,如:
- 利用 bitcount 的返回可以判断该用户是否连续签到
- 利用 bitop 的 and 可以判断哪些用户在指定天数活跃
对于上述场景使用 bitmaps 比 string 的比较
- 若每日活跃用户很多,使用 bitmaps 存储效率极高,每个用户只需一位保存
- 若每日活跃用户较少,使用 bitmaps 不合理,存储大量无意义数据 0,此时使用 set/list 更好
2.8 操作 hyperloglog
- hyperloglog 是 redis 用来做基数统计的算法,优点在于:输入的元素数量或体积非常大时,计算基数所需要的空间总是固定的,并且非常小
- hyperloglog 只会根据输入元素来计算基数,而不会存储输入元素本身,对于要求元素精度的则不适用
添加指定元素到 hyperloglog 中,执行后若基数发生变化返回 1,否则返回 0
pfadd key element [element ...]
计算若干个 hyperloglog 的基数,返回基数
pfcount key [key ...]
合并若干个 hyperloglog,并将结果写入新的 hyperloglog 中
pfmerge destkey sourcekey [sourcekey ...]
2.9 操作 geospatial
- 支持对地图经纬度的操作
- 本质是一个 zset
添加地理信息(经度、纬度、城市)
geoadd key longitude latitude member [longitude latitude member ...]
注:南极北极无法添加;有效的经度[-180,180];有效的纬度[-85.05112878,85.05112878];当输入的经纬度超出范围会报错
获取地理信息,返回位置的经纬度坐标
geopos key member [member ...]
获取两个位置的直线距离
geodist key member1 member2 [m|km|ft|mi]
注:m 表示米(默认),km 表示千米;ft 表示英尺;mi 表示英里
给定一个位置,返回该位置附近的位置
georadius key longitude latitude radius m|km|ft|mi
三、redis 配置
3.1 Unit
# 1k => 1000 bytes
# 1kb => 1024 bytes
# 1m => 1000000 bytes
# 1mb => 1024*1024 bytes
# 1g => 1000000000 bytes
# 1gb => 1024*1024*1024 bytes
配置文件 unit 单位对大小写不敏感
3.2 INCLUDES
# include /path/to/local.conf
# include /path/to/other.conf
类似 jsp 中的 include,多实例的情况可以把公用的配置文件提取出来
3.3 MODULES
# loadmodule /path/to/my_module.so
# loadmodule /path/to/other_module.so
需要加载的 redis 模块
3.4 NETWORK
bind 127.0.0.1 -::1
默认情况 bind=127.0.0.1 只接受本机的访问请求,不写表示接受任意 ip 访问
若开启了 protected-mode,接受任意 ip 访问且没有设置密码时,redis 同样只接受本机访问请求
protected-mode yes
本机访问模式,建议设置为 no
port 6379
redis 端口号
tcp-backlog 511
设置 tcp 的backlog,backlog 其实是一个连接队列,backlog 队列总和 = 未完成三次握手队列 + 已经完成三次握手队列。在高并发环境下你需要一个高 backlog 值来避免慢客户端连接问题。
注意 Linux 内核会将这个值减小到 /proc/sys/net/core/somaxconn 的值(128),所以需要确认增大 /proc/sys/net/core/somaxconn 和 /proc/sys/net/ipv4/tcp_max_syn_backlog(128)两个值来达到想要的效果
timeout 0
一个空闲的客户端维持多久会关闭,0 表示永不关闭
tcp-keepalive 300
对客户端的心跳检测,每 n 秒检测一次,0 表示不检测
3.5 GENERAL
daemonize yes
守护进程,后台启动
pidfile /var/run/redis_6379.pid
pid 文件存放位置,每一个实例都会产生一个不同的 pid 文件
loglevel notice
设置日志等级
debug (a lot of information, useful for development/testing)
verbose (many rarely useful info, but not a mess like the debug level)
notice (moderately verbose, what you want in production probably)
warning (only very important / critical messages are logged)
logfile ""
设置日志文件名,可以为空,也可以设置到 /dev/null 丢弃
databases 16
设置 redis 数据库的个数
3.6 SECURITY
# requirepass foobared
设置密码,客户端通过 auth xxx 输入密码连接
3.7 LIMITS
# maxclients 10000
redis 最大连接数,若超过此限制会拒接新的连接并抛出:max number of clients reached
# maxmemory <bytes>
- 建议设置,否则内存占满,造成服务器宕机
- 设置 redis 可使用的内存,一旦内存达到上限,redis 会根据 maxmemory-policy 指定的规则移除数据
# maxmemory-policy noeviction
设置移除数据的策略
volatile-lru:使用 LRU 算法移除 key,只对设置了过期时间的键;(最近最少使用)
allkeys-lru:在所有集合 key 中,使用 LRU 算法移除 key
volatile-random:在过期集合中移除随机的 key,只对设置了过期时间的键
allkeys-random:在所有集合 key 中,移除随机的 key
volatile-ttl:移除那些 TTL 值最小的 key,即那些最近要过期的 key
noeviction:不进行移除。针对写操作,只是返回错误信息
# maxmemory-samples 5
设置样本数量,LRU 算法和最小TTL 算法都并非是精确的算法,而是估算值,所以你可以设置样本的大小,redis 默认会检查这么多个 key 并选择其中 LRU 的那个。数值越小约不精确,但开销越小
四、redis 客户端
- Jedis 是 redis 的 java 实现的客户端,其 API提供了比较全面的 redis 命令支持
- Jedis 中的方法调用是比较底层的暴露的 redis 的 API,即 Jedis 中 Java 方法基本和 redis 保持一致
- Jedis 使用阻塞的 I/O,其方法调用都是同步的,不支持异步
- Jedis 客户端不是线程安全,所以需要使用连接池
4.1 pom 文件
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.6.0</version>
</dependency>
4.2 测试用例
import redis.clients.jedis.Jedis;
public class JedisDemo {
public static void main(String[] args) {
Jedis jedis = new Jedis("localhost", 6379);
String ping = jedis.ping();
System.out.println(ping);
jedis.close();
}
}
注:测试失败检查:关闭防火墙或开放 redis 端口;注释掉 bind;关闭 protected-mode
jedis 的 API 和 redis 命令基本一致,通过 Jedis 对象调用即可
五、redis 事务
- All the commands in a transaction are serialized and executed sequentially. It can never happen that a request issued by another client is served in the middle of the execution of a Redis transaction. This guarantees that the commands are executed as a single isolated operation.
- Redis 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序的执行。事务在执行过程中,不会被其他客户端发送来的命令请求所打断
- Redis 事务的主要作用就是串联多个命令防止别的命令插队
5.1 事务操作
5.1.1 multi、exec、discard
redis 关于事务的命令有:multi、exec、discard
multi 相当于开启事务,multi 之后的所有命令将会进入命令队列中,但不会执行
exec 依次执行命令队列中的所有命令,执行完本次事务结束
discard 相当于回滚,中断事务,命令队列中的命令全部丢弃,事务结束
正常事务
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 1
QUEUED
127.0.0.1:6379(TX)> set k2 2
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) OK
127.0.0.1:6379> get k1
"1"
127.0.0.1:6379> get k2
"2"
中断事务
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 1
QUEUED
127.0.0.1:6379(TX)> set k2 2
QUEUED
127.0.0.1:6379(TX)> discard
OK
127.0.0.1:6379> keys *
(empty array)
5.1.2 事务错误处理
redis 事务可以分为两个阶段:组队阶段和执行阶段,在这两个阶段出现错误的处理方式是不同的
组队阶段出错则整个命令队列都会被取消;执行阶段出错时出错的命令不会执行,其他命令正常执行不会回滚
演示:组队阶段出错
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 1
QUEUED
127.0.0.1:6379(TX)> set k2
(error) ERR wrong number of arguments for 'set' command
127.0.0.1:6379(TX)> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> keys *
(empty array)
演示:执行阶段出错
127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k1 1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> incr k1
QUEUED
127.0.0.1:6379(TX)> incr k2
QUEUED
127.0.0.1:6379(TX)> exec
1) OK
2) OK
3) (integer) 2
4) (error) ERR value is not an integer or out of range
127.0.0.1:6379> keys *
1) "k2"
2) "k1"
127.0.0.1:6379> get k1
"2"
127.0.0.1:6379> get k2
"v2"
5.2 事务冲突
工作冲突,尽情期待...
评论区