Redis的使用

技术分享
1247 0

Redis使用

一、redis安装

使用docker安装时遇到的问题

使用docker安装时遇到的问题以及解决办法

redis安装启动

今天启动redis突然出现连接端口有问题,错误如下:

truedei@truedei:~$ 
truedei@truedei:~$ sudo docker start redis
Error response from daemon: driver failed programming external connectivity on endpoint redis (87977922525bd67cb6e39c26b2f0229081ff428693991d923c595752a733d41b): Error starting userland proxy: listen tcp 0.0.0.0:6379: bind: address already in use
Error: failed to start containers: redis
truedei@truedei:~$ 

就查了下端口,果然有在使用的:

truedei@truedei:~$ netstat -ntpl |grep 6379
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
tcp        0      0 127.0.0.1:6379          0.0.0.0:*               LISTEN      -                   
truedei@truedei:~$ 

查一下是谁在占用,让我很是惊讶,居然是他自己,端口被占用了,而且还没启动

truedei@truedei:~$ ps -aux |grep 6379
redis     1397  0.0  0.0  42608  2292 ?        Ssl  07:30   0:00 /usr/bin/redis-server 127.0.0.1:6379
truedei   5096  0.0  0.0  14536   976 pts/4    S+   07:33   0:00 grep 6379
truedei@truedei:~$ 
truedei@truedei:~$ sudo docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
truedei@truedei:~$ 
truedei@truedei:~$ 

先结束掉在说,奇怪的是,杀都杀不掉

truedei@truedei:~$ sudo kill -9 1397
truedei@truedei:~$ ps -aux |grep 6379
redis     8263  0.0  0.0  42608  2164 ?        Ssl  07:35   0:00 /usr/bin/redis-server 127.0.0.1:6379
truedei   8291  0.0  0.0  14536   984 pts/4    S+   07:35   0:00 grep 6379
truedei@truedei:~$ 

仔细看了一下这个redis并不是docker中安装的redis,而是在本机的:

/usr/bin/redis-server

那就停止掉本机的,果然可以了:

truedei@truedei:~$ /etc/init.d/redis-server stop
[ ok ] Stopping redis-server (via systemctl): redis-server.service.
truedei@truedei:~$ 
truedei@truedei:~$ ps -aux |grep 6379
truedei  10583  0.0  0.0  14536   948 pts/4    S+   07:37   0:00 grep 6379
truedei@truedei:~$ 
truedei@truedei:~$ 

然后再启动:

truedei@truedei:~$ 
truedei@truedei:~$ sudo docker start redis
redis
truedei@truedei:~$ 
truedei@truedei:~$ sudo docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
85cb7d83a2ff        redis               "docker-entrypoint.s…"   22 hours ago        Up 5 seconds        0.0.0.0:6379->6379/tcp   redis
truedei@truedei:~$ 
truedei@truedei:~$ 

解决docker中redis警告
The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128
说个题外话,如果想要子在创建容器后执行多条命令,可以使用 sh -c “命令1 && 命令2 && 命令3” 这种模式
例如:这个是我尝试在容器启动时修改参数,但是失败了

docker run -p 6388:6379 --name redis6388 --sysctl net.core.somaxconn=1024 \
-v /mnt/docker/redis6388/data:/data \
-v /mnt/docker/redis6388/conf/redis.conf:/usr/local/etc/redis/redis.conf \
-d --restart=always redis \
sh -c "redis-server /usr/local/etc/redis/redis.conf --appendonly yes && sudo echo 511 > /proc/sys/net/core/somaxconn"

终极解决方案
首先查看docker run --help 中是否有 –sysctl 这个参数,如果有的话,可以使用下述命令解决此警告,命令如下:

docker run -p 6399:6379 --name redis6399 --sysctl net.core.somaxconn=1024 \
-v /mnt/docker/redis6399/data:/data \
-v /mnt/docker/redis6399/conf/redis.conf:/usr/local/etc/redis/redis.conf \
-v /proc/sys/net/core/somaxconn:/proc/sys/net/core/somaxconn \
-e TIME_ZONE="Asia/Shanghai" -e TZ="Asia/Shanghai" \
-d --restart=always redis redis-server /usr/local/etc/redis/redis.conf \
--appendonly yes

重点在 –sysctl net.core.somaxconn=1024 这里,至此问题解决
附加送解决redis时区不同步问题:
-e TIME_ZONE=“Asia/Shanghai” -e TZ=“Asia/Shanghai”

二、Redis使用教程

redis介绍

redis默认有16个数据库

redis数据库数量

默认使用的第一个数据库,可以使用select 进行切换数据库

127.0.0.1:6379> select 3 #切换数据库
OK
127.0.0.1:6379[3]> dbsize #查看库的大小
(integer) 0

不同的数据库可以存储不同的值

Redis是单线程的:

Redis是很快的,官方表示,Redis是基于内存操作,CPU不是Redis性能瓶颈,Redis的瓶颈是根据机器的内存和网络带宽,既然可以使用单线程来实现,就使用单线程了 !

核心: Redis 是将所有的数据全部放在内存中的,所以说使用单线程去操作效率就是最高的,多线程(CPU上下文会切换:耗时的操作!!!),对于内存系统来说,如果没有上下文切换效率就是最高的!多次读写都是在一个CPU上的,在内存情况下,这个就是最佳的方案!

Redis 是在内存中的数据结构存储系统,可以用作数据库、缓存和消息中间件MQ。它支持多种类型的
数据结构,如字符串 ( strings),散列(hashes ),列表(lists),集合( sets ),有序集合( sorted sets ) 与范围查询
bitmaps,hyperloglogs 和地理空间(geospatial) 索引半径查询。Redis 内置了 复制(replication ),LUA脚本(Lua
scripting ),LRU驱动事件(LRU eviction ),事务( transactions ) 和不同级别的 磁盘持久化 ( persistence) ,并通过 Redis哨兵( Sentinel) 和自动 分区( Cluster ) 提供高可用性( high availability )。

redis命令使用

进去redis 使用 redis-cli -p 6379 进行连接测试

string类

string类使用场景

  • 计数器
  • 统计过单位的数量
  • 粉丝数
  • 对象缓存存储

redis命令区分大小写

使用set进行设置值,使用get获取值,del删除值

set get del

查看是否存在

查看元素是否存在

使用keys * 查看全部键,keys name 查找这个键

127.0.0.1:6379> select 3
OK
127.0.0.1:6379[3]> get age
"188"
127.0.0.1:6379[3]> keys age
1) "age"
127.0.0.1:6379[3]> keys age1
(empty array)

查找以me结尾的键 keys *me

flushall 或者 flushdb 删除所有的键

flashdb清除当前数据库

flashall请求全部数据库

127.0.0.1:6379[3]> flushdb
OK
127.0.0.1:6379[3]> keys *
(empty array)
127.0.0.1:6379[3]> set name 123
OK
127.0.0.1:6379[3]> get name
"123"
127.0.0.1:6379[3]> flushall
OK

使用expire设置过期时间

127.0.0.1:6379> set name 11
OK
127.0.0.1:6379> expire name 30  #设置过期时间为30秒
(integer) 1
127.0.0.1:6379> ttl name
(integer) 25

比如单点登陆可以设置过期时间

使用ttl查询过期时间,使用expire设置过期时间,时间为-1是没设置过期时间,为-2说明为已经过期或者没有值了

设置过期时间查询过期时间

查询key类型

127.0.0.1:6379> type name #查询key类型
string

使用incr进行增量存贮或使用decr进行递减

incrby和decrby进行批量增加 / 减少

127.0.0.1:6379> set views 0
OK
127.0.0.1:6379> get views
"0"
127.0.0.1:6379> incr views
(integer) 1
127.0.0.1:6379> incr views
(integer) 2
127.0.0.1:6379> get views
"2"
127.0.0.1:6379> decr views
(integer) 1
127.0.0.1:6379> decr views
(integer) 0
127.0.0.1:6379> decr views
(integer) -1
127.0.0.1:6379> get views
"-1"
127.0.0.1:6379> get views
"-1"
127.0.0.1:6379> INCRBY views 10  #incrby批量增加
(integer) 9
127.0.0.1:6379> DECRBY views 5   #decrby批量减少
(integer) 4

获取指定字符串范围 getrange

127.0.0.1:6379> set strings abcdefghijklmn
OK
127.0.0.1:6379> getrange strings 0 3 #截取字符串指定长度
"abcd"

替换指定位置开始的字符串

127.0.0.1:6379> set key2 abcdefghijklmn
OK
127.0.0.1:6379> get key2
"abcdefghijklmn"
127.0.0.1:6379> setrange key2 1 xx  # 替换为xx
(integer) 14
127.0.0.1:6379> get key2
"axxdefghijklmn"

setex (set with exprie) #设置过期时间

setnx (set if not exist) #不存在则设置 (在分布锁中经常使用)比如修改成功版本号加修改失败就不加1

127.0.0.1:6379> set name nbsb
OK
127.0.0.1:6379> setnx name 111
(integer) 0
127.0.0.1:6379> setex age 20 100 #设置age为100并20秒后过期
OK
127.0.0.1:6379> ttl age #查询过期时间,-2为过期,-1为永久
(integer) 17
127.0.0.1:6379> msetnx k1 v1 k4 v4 #msetnx 是原子性操作,要么一起成功要么一起失败
(integer) 0

使用mset批量设置key

127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 #批量设置值
OK
127.0.0.1:6379> keys *
1) "k1"
2) "k3"
3) "k2"
127.0.0.1:6379> get k1
"v1"
127.0.0.1:6379> mget k1 k2 k3  #批量获取值
1) "v1"
2) "v2"
3) "v3"

实战演示string

# 这里的key是一个巧妙的设计 : user:{id}:{filed}
# 设置user:1:name是键 ,张三是值 | user:1:age 是键 18是值
127.0.0.1:6379> mset user:1:name zhangsan user:1:age 18 
OK
127.0.0.1:6379> keys *
1) "user:1:name"
2) "user:1:age"
#存贮一个json字符串
127.0.0.1:6379> set user:2 {name:zhangsan,age:19}
OK
127.0.0.1:6379> get user:2
"{name:zhangsan,age:19}"

使用getset命令

先get在set

127.0.0.1:6379> getset db redis
(nil)
127.0.0.1:6379> get db
"redis"
127.0.0.1:6379> getset db docker
"redis"
127.0.0.1:6379> get db
"docker"

修改键的名称

127.0.0.1:6379> set name qwer
OK
127.0.0.1:6379> keys *
1) "name"
127.0.0.1:6379> rename name namess
OK
127.0.0.1:6379> keys *
1) "namess"

列表

在redis可以将list用成栈、队列、阻塞队列

消息队列(Lpush Rpop) 栈(Lpush Lpop)

使用lpush 添加列表,使用lrange查询列表

使用lpush 添加列表,使用lrange查询列表

lpush是从左至右依次添加,所以是第一个在最下面

lrange查询列表的是查询从0到-1 ,-1表示是最后一个元素

lrange查询列表

lrange查询列表

使用rpush添加到尾部,使用lpsuh添加到头部(相当于双端队列,可以两边都可以插值)

使用rpush添加到尾部,使用lpsuh添加到头部

使用lpop和rpop进行删除头部或者尾部

使用lpop和rpop进行删除头部或者尾部

查询list元素

127.0.0.1:6379> lrange list 0 -1  #查询list
1) "three"
2) "two"
3) "one"
127.0.0.1:6379> lindex list 1  #使用指定索引查询
"two"
llen list1  #使用llen查询列表长度
ltrim list1 1 3 #修剪:删除1-3索引以外的元素
lrem list 1 one #移除指定个数的value,精确匹配
rpoplpush #移除列表最后一个元素,并将他移动到一个新的列表中

修改指定元素值

127.0.0.1:6379> lrange list 0 -1
1) "lll"
2) "three"
3) "one"
127.0.0.1:6379> lset list 0 element
OK
127.0.0.1:6379> lrange list 0 -1
1) "element"
2) "three"
3) "one"

在指定元素前/后插入

127.0.0.1:6379> rpush mylist hello
(integer) 1
127.0.0.1:6379> rpush mylist world
(integer) 2
127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "world"
127.0.0.1:6379> LINSERT mylist before world other  # 在这个元素之前插入
(integer) 3
127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "other"
3) "world"
127.0.0.1:6379> LINSERT mylist after hello one  # 在这个元素之后插入
(integer) 4
127.0.0.1:6379> lrange mylist 0 -1
1) "hello"
2) "one"
3) "other"
4) "world"

集合

set是不能重复的集合,可以用于抽取随机

增加元素

sadd myset hello

增加元素

查询元素

127.0.0.1:6379> sadd myset one two stree
#查询a是否在集合set1中
(integer) 3
127.0.0.1:6379> sismember myset one
(integer) 1
127.0.0.1:6379> sismember myset one2
(integer) 0 
127.0.0.1:6379> SMEMBERS myset #查询所有元素
1) "stree"
2) "one"
3) "two" 

删除元素

srem set1 a #删除里面元素
127.0.0.1:6379> SMEMBERS myset 
1) "stree"
2) "one"
3) "two"
127.0.0.1:6379> spop myset  #随机删除集合中的元素
"two"
127.0.0.1:6379> spop myset 
"stree"

将指定元素移动到指定集合中

也可以类似于删除指定元素,将这个元素移动到另一个集合在将那个集合删除

SADD set1 "A"
SADD set1 "B"
SADD set1 "C"
SADD set2 "X"
SADD set2 "Y"

SMOVE set1 set2 "A"

交集、差集、并集

可以用于类似 b站共同关注

  • 并集 sunion
  • 交集 sinter (共同好友例子)
  • 差集 sdiff
127.0.0.1:6379> sadd myset1  a b c d e
(integer) 5
127.0.0.1:6379> sadd myset2  a b f g h
(integer) 5
127.0.0.1:6379> SUNION myset1 myset2 #并集
1) "c"
2) "d"
3) "a"
4) "g"
5) "b"
6) "e"
7) "h"
8) "f"
127.0.0.1:6379> SINTER myset1 myset2 #交集
1) "a"
2) "b"
127.0.0.1:6379> sdiff myset1 myset2 #差集
1) "e"
2) "d"
3) "c"

获取元素数目

scard myset 

随机获取指定数量成员

127.0.0.1:6379> SRANDMEMBER myset 2
1) "stree"
2) "two" 
127.0.0.1:6379> SRANDMEMBER myset 2
1) "stree"
2) "two"
127.0.0.1:6379> srandmember myset  #默认获取一个
"one"

有序集合

添加一个值和添加多个值

127.0.0.1:6379> zadd myset 1 one #前面必须加一个数字,可以理解为id,这个id可以是任意的,可以用于排名
(integer) 1
127.0.0.1:6379> zadd myset 2 two 3 three
(integer) 2
127.0.0.1:6379> zrange myset 0 -1
1) "one"
2) "two"
3) "three"

排序

127.0.0.1:6379> zadd salary 2500 xiaohong
(integer) 1
127.0.0.1:6379> zadd salary 5000 zhangsan
(integer) 1
127.0.0.1:6379> zadd salary 500 nbsb
(integer) 1
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf
1) "nbsb"
2) "xiaohong"
3) "zhangsan"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf withscores #获取全部参数并且有比分的参数
1) "nbsb"
2) "500"
3) "xiaohong"
4) "2500"
5) "zhangsan"
6) "5000"
127.0.0.1:6379> zrange salary 0 -1 withscores #获取有比分的参数
1) "nbsb"
2) "500"
3) "xiaohong"
4) "2500"
5) "zhangsan"
6) "5000"
127.0.0.1:6379> ZRANGEBYSCORE salary -inf 2500 withscores #获取2500以内比分的升序参数
1) "nbsb"
2) "500"
3) "niuma"
4) "500"
5) "xiaohong"
6) "2500"
127.0.0.1:6379> ZREVRANGE salary 0 -1 #从大到小 降序排序
1) "zhangsan"
2) "xiaohong"
3) "niuma"
4) "nbsb"
127.0.0.1:6379> ZREVRANGE salary 0 -1 withscores #降序排序并且附带比分
1) "zhangsan"
2) "5000"
3) "xiaohong"
4) "2500"
5) "niuma"
6) "500"
7) "nbsb"
8) "500"

集合排序

移除rem元素

zrem result qinghua #删除元素

获取集合中个数

127.0.0.1:6379> zcard salary  #获取这个集合中的个数
(integer) 3 
127.0.0.1:6379> zadd myset 1 nbsb 2 xrkx 3 macsb 
(integer) 3
127.0.0.1:6379> zcard mysetr
(integer) 0
127.0.0.1:6379> zcard myset
(integer) 3
127.0.0.1:6379> zcount myset 0 1  #获取指定区间成员数量
(integer) 1
127.0.0.1:6379> zcount myset 0 2
(integer) 2

哈希Hash

Map集合 , key-map 这时候值是一个map集合,本质和string类型没有太大区别,还是简单的key-value

hash变更的数据 user name age,尤其是是用户信息之类的,经常变动的信息! hash 更适合于对象的存储,String更加适合字符串存储!

设置hash

设置hash获取hash

127.0.0.1:6379> hset person name nbsb #设置
(integer) 1
127.0.0.1:6379> hget person name #获取
"nbsb"
127.0.0.1:6379> hmset person name1 zhangsan name2 lisi name3 niubi #批量设置
OK
127.0.0.1:6379> hmget person name1
1) "zhangsan"
127.0.0.1:6379> hmget person name1 name2 name3 # 批量获取
1) "zhangsan"
2) "lisi"
3) "niubi"
127.0.0.1:6379> hsetnx person name 11 #如果已经存在就不设置
(integer) 0 #已经存在设置失败
127.0.0.1:6379> hsetnx person name5 11
(integer) 1#设置成功

获取hash值

127.0.0.1:6379> hgetall person
1) "name"
2) "niubi"
3) "name1"
4) "zhangsan"
5) "name2"
6) "lisi"
7) "name3"
8) "niubi"

person中的对应的name是nbsb,age是100,都是成对出现的

以及设置的就没法在设置

只获取键或者值
127.0.0.1:6379> hkeys person
1) "name"
2) "name1"
3) "name2"
4) "name3"
127.0.0.1:6379> hvals person
1) "niubi"
2) "zhangsan"
3) "lisi"
4) "niubi"
递增方法 incr decr

hash中没有decr,但是incrby可以设置负值,上面的string中的incr也可以设置负值

127.0.0.1:6379> hset myhash field 0
(integer) 1
127.0.0.1:6379> HINCRBY myhash field 1
(integer) 1
127.0.0.1:6379> HINCRBY myhash field 2
(integer) 3
127.0.0.1:6379> HINCRBY myhash field -1
(integer) 2
127.0.0.1:6379> HINCRBY myhash field -2
(integer) 0

删除元素使用hdel

hdel person age
hexists person name #查寻person 中的 name 是否存在
hkeys person
hlen person

发布订阅模式

publish helloword nihao #向helloword进行发布订阅
subscribe helloword #接受helloword的订阅消息

订阅模式弊端,没法记录历史,消息不可持久化

发送订阅

使用xadd添加stream消息

image-20230727135023358

xadd geenkhour * coures docekr  # *代表自动生成id,这个消息idd必须是递增的
xadd geenkhour 1-1 coures docekr # 第一个整数表示时间戳,第二个整数表示序列号

添加消息

xtrim geekhour maxlen 0  # 表示删除所有消息

读取消息

geospatial添加地理位置

添加城市

127.0.0.1:6379> geoadd china:city 116 40 beijing
(integer) 1
127.0.0.1:6379> geoadd china:city 121 31 shanghai
(integer) 1
127.0.0.1:6379> geoadd china:city 120 30 hangzhou 108 34 xian
(integer) 2

获取城市经纬度

127.0.0.1:6379> geopos china:city xian
1) 1) "108.00000160932540894"
   2) "34.00000062127011091"

获取两地距离

  • m 表示米
  • km千米
  • mi 英里
  • ft 英尺
127.0.0.1:6379> geodist china:city beijing shanghai #查询两地直线举例
"1098158.0237"
127.0.0.1:6379> geodist china:city beijing shanghai km
"1098.1580"
127.0.0.1:6379> georadius china:city 110 30 1100 km 
#查询离这个经纬度方圆1100千米内的城市
1) "xian"
2) "hangzhou"
3) "shanghai"
127.0.0.1:6379> GEORADIUSBYMEMBER china:city beijing 2000 km #指定查询位置
1) "xian"
2) "hangzhou"
3) "shanghai"
4) "beijing"

测试redis性能

redis 性能测试工具可选参数如下所示:

序号选项描述默认值
1-h指定服务器主机名127.0.0.1
2-p指定服务器端口6379
3-s指定服务器 socket
4-c指定并发连接数50
5-n指定请求数10000
6-d以字节的形式指定 SET/GET 值的数据大小2
7-k1=keep alive 0=reconnect1
8-rSET/GET/INCR 使用随机 key, SADD 使用随机值
9-P通过管道传输 请求1
10-q强制退出 redis。仅显示 query/sec 值
11--csv以 CSV 格式输出
12*-l*(L 的小写字母)生成循环,永久执行测试
13-t仅运行以逗号分隔的测试命令列表。
14*-I*(i 的大写字母)Idle 模式。仅打开 N 个 idle 连接并等待。
测试100并发连接 100000请求
redis-benchmark -h localhost -p 6379 -c 100 -n 100000

我这里是在 redis/src下测试的,因为redis-benchmark在这个文件下

测压的返回值解释

100 parallel clients 是100个并发客户端

3 bytes payload 是每次只写入3个字节

keep alive 每次只有一个服务器连接

最后更新 2023-07-28
评论 ( 0 )
OωO
隐私评论