1、下载安装包!www.redis.io redis-6.2.1.tar.gz
2、解压Redis的安装包! 程序放在 /opt目录下
[root@fuyang fuyang]# mv redis-6.2.1.tar.gz /opt
[root@fuyang fuyang]# cd /opt
[root@fuyang opt]# ll
total 2400
drwx--x--x 4 root root 4096 Apr 15 17:41 containerd
drwxr-xr-x 8 root root 4096 Jan 8 18:18 knem-1.1.4.90mlnx1
drwxr-xr-x 8 root root 4096 Jan 8 18:19 mellanox
-rw-r--r-- 1 root root 2438367 Apr 16 14:23 redis-6.2.1.tar.gz
drwxr-xr-x. 2 root root 4096 Oct 31 2018 rh
3、解压文件 tar -zxvf redis-6.2.1.tar.gz,进入解压后的文件,可以看到redis的配置文件
[root@fuyang redis-6.2.1]# ls
00-RELEASENOTES CONDUCT COPYING INSTALL MANIFESTO redis.conf runtest-cluster runtest-sentinel src TLS.md
BUGS CONTRIBUTING deps Makefile README.md runtest runtest-moduleapi sentinel.conf tests utils
4、基本的环境安装
#安装gcc环境
[root@fuyang redis-6.2.1]# yum install gcc-c++
Loaded plugins: fastestmirror, langpacks
Loading mirror speeds from cached hostfile
docker-ce-stable | 3.5 kB 00:00:00
epel | 4.7 kB 00:00:00
extras | 2.9 kB 00:00:00
os | 3.6 kB 00:00:00
updates | 2.9 kB 00:00:00
(1/2): epel/7/x86_64/updateinfo | 1.0 MB 00:00:00
(2/2): epel/7/x86_64/primary_db | 6.9 MB 00:00:00
Package gcc-c++-4.8.5-44.el7.x86_64 already installed and latest version
Nothing to do
#make命令会把所有需要的文件全都配置上
[root@fuyang redis-6.2.1]# make
cd src && make all
make[1]: Entering directory `/opt/redis-6.2.1/src‘
CC Makefile.dep
make[1]: Leaving directory `/opt/redis-6.2.1/src‘
make[1]: Entering directory `/opt/redis-6.2.1/src‘
Hint: It‘s a good idea to run ‘make test‘ ;)
make[1]: Leaving directory `/opt/redis-6.2.1/src‘
#确认是否安装完成 (不执行也可以)
[root@fuyang redis-6.2.1]# make install
cd src && make install
make[1]: Entering directory `/opt/redis-6.2.1/src‘
Hint: It‘s a good idea to run ‘make test‘ ;)
INSTALL install
INSTALL install
INSTALL install
make[1]: Leaving directory `/opt/redis-6.2.1/src‘
[root@fuyang redis-6.2.1]#
5、redis的默认安装路径 /usr/local/bin
[root@fuyang local]# cd bin/
[root@fuyang bin]# ll
total 19824
-rwxr-xr-x 1 root root 1001112 Aug 5 2020 busybox-x86_64
-rwxr-xr-x 1 root root 4833400 Apr 16 14:45 redis-benchmark
lrwxrwxrwx 1 root root 12 Apr 16 14:45 redis-check-aof -> redis-server
lrwxrwxrwx 1 root root 12 Apr 16 14:45 redis-check-rdb -> redis-server
-rwxr-xr-x 1 root root 5003416 Apr 16 14:45 redis-cli
lrwxrwxrwx 1 root root 12 Apr 16 14:45 redis-sentinel -> redis-server
-rwxr-xr-x 1 root root 9451112 Apr 16 14:45 redis-server
[root@fuyang bin]#
6、将redis的配置文件复制到当前目录下
[root@fuyang bin]# mkdir fconfig
[root@fuyang bin]# cp /opt/redis-6.2.1/redis.conf fconfig
[root@fuyang bin]# ls
busybox-x86_64 fconfig redis-benchmark redis-check-aof redis-check-rdb redis-cli redis-sentinel redis-server
[root@fuyang bin]# cd fconfig/
[root@fuyang fconfig]# ll
total 92
-rw-r--r-- 1 root root 92222 Apr 16 14:52 redis.conf
7、redis默认不是后台启动的,修改配置文件!
8、启动redis服务 并连接redis
[root@fuyang bin]# pwd
/usr/local/bin
[root@fuyang bin]# redis-server fconfig/redis.conf #指定在哪个配置文件下启动
[root@fuyang bin]# redis-cli -p 6379 # -h 代表host主机 -p指定端口号
127.0.0.1:6379>
9、基本命令
[root@fuyang fconfig]# redis-cli -p 6379 #使用redis客户端进行连接
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> set name fuyang
OK
127.0.0.1:6379> get name
"fuyang"
127.0.0.1:6379> keys * #查看所有的key
1) "name"
127.0.0.1:6379>
10、查看redis的进程是否开启
[root@fuyang ~]# ps -ef|grep redis
root 12269 1 0 14:59 ? 00:00:03 redis-server 127.0.0.1:6379
root 13892 1681 0 15:11 pts/0 00:00:00 redis-cli -p 6379
root 14503 14452 0 15:15 pts/1 00:00:00 grep --color=auto redis
[root@fuyang ~]#
11、关闭redis服务 shutdown
12、后面会使用单机多Redis启动集群测试!
redis-benchmark是一个压力测试工具!
官方自带的性能测试工具!
redis-benchmark + 命令参数
#测试:100个并发 100000个请求[root@fuyang bin]# redis-benchmark -h localhost -p 6379 -c 100 -n 100000
redis默认有16个数据库
默认使用第0个
可以使用select进行切换数据库!
127.0.0.1:6379> select 3 #切换数据库OK127.0.0.1:6379[3]> dbsize #查看数据库的大小(integer) 0127.0.0.1:6379[3]> get name #通过key 获取值(nil)127.0.0.1:6379[3]> select 0OK127.0.0.1:6379> get name"fuyang"127.0.0.1:6379> select 3OK127.0.0.1:6379[3]> set name yangfuOK127.0.0.1:6379[3]> keys *1) "name"127.0.0.1:6379[3]> flushdb # flushdb:清空该数据库 flushall:清空所有数据库OK127.0.0.1:6379[3]> keys *(empty array)127.0.0.1:6379[3]>
官方表示,Redis是基于内存操作,CPU不是Redis性能瓶颈,Redis的瓶颈是根据机器的内存和网络带宽。
1、误区1:高性能的服务器一定是多线程的?
2、误区2:多线程(CPU上下文会切换)一定比单线程效率高?
CPU>内存>硬盘 速度
核心:redis是将所有的数据全部放在内存中的,所有使用单线程操作效率是最高的。 多线程(CPU上下文会切换:耗时的操作!)
Redis-Key
常用命令:
exists xxx(key) :判断xxx是否存在
move xxx 1:移除当前的key
expire xxx(key):过期时间 单位秒 expire name 10
ttl xxx(key):查看xxx还有几秒过期
type xxx(key):查看xxx的类型**
**
**
**
String(字符串)
append xxx(key) "追加的字符串" 如果当前key不存在就相当与 setkey
127.0.0.1:6379> set name fuyangOK127.0.0.1:6379> append name "hello"(integer) 11127.0.0.1:6379> get name"fuyanghello"127.0.0.1:6379>
strlen xxx(key) 获取字符串的长度
127.0.0.1:6379> strlen name(integer) 11127.0.0.1:6379>
incr xxx(key) 自增 类似与 i++ 每次执行incr xxx 都会自动加1
127.0.0.1:6379> set views 0OK127.0.0.1:6379> incr views(integer) 1127.0.0.1:6379> get views"1"127.0.0.1:6379> incr views(integer) 2127.0.0.1:6379> get views"2"127.0.0.1:6379>
decr xxx(key) 自减 同上
127.0.0.1:6379> decr views(integer) 1127.0.0.1:6379> get views"1"127.0.0.1:6379>
incrby xxx(key) 10 自增10
decrby xxx(key) 10 自减10
127.0.0.1:6379> incrby views 10(integer) 11127.0.0.1:6379> decrby views 5(integer) 6127.0.0.1:6379> get views"6"127.0.0.1:6379>
getrange xxx(key) 0 3 截取字符串 0~3, 0~-1查看全部的字符串
127.0.0.1:6379> get name"fuyanghello"127.0.0.1:6379> getrange name 0 3"fuya"127.0.0.1:6379> getrange name 0 -1"fuyanghello"127.0.0.1:6379>
setrange xxx(key) 下标 想要替换的字符串 替换
127.0.0.1:6379> setrange name 1 xxx(integer) 11127.0.0.1:6379> get name"fxxxnghello"127.0.0.1:6379>
setex (set with expire) 设置过期时间
setnx (set if not expire) 不存在在设置(在分布式锁中会常常使用!)
127.0.0.1:6379> get name"fuyang"127.0.0.1:6379> setex name 10 yangfu #设置name值为yangfu 10秒后过期OK127.0.0.1:6379> get name"yangfu"127.0.0.1:6379> get name(nil)127.0.0.1:6379> 127.0.0.1:6379> set name fuyangOK127.0.0.1:6379> get name"fuyang"127.0.0.1:6379> setnx name yangfu #如果 name 不存在,创建name并赋值,如果存在 创建失败(integer) 0127.0.0.1:6379> get name"fuyang"127.0.0.1:6379> setnx key1 yangfu(integer) 1127.0.0.1:6379> get key1"yangfu"127.0.0.1:6379>
mset 同时设置多个值
msetnx
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 #同时设置多个值OK127.0.0.1:6379> keys *1) "k2"2) "k3"3) "k1"127.0.0.1:6379> mget k1 k2 k3 #同时获取多个值1) "v1"2) "v2"3) "v3"127.0.0.1:6379> msetnx k1 v1 k4 v4 #msetnx 是一个原子性的操作,要么一起成功,要么一起失败(integer) 0127.0.0.1:6379> #对象#set user:1 {name:zhangsan,age:3} #设置一个 user:1 对象 值为json字符串来保存一个对象!#这里的key是一个巧妙的设计: user:{id}:{filed}127.0.0.1:6379> set user:1 {name:zhangsan,age:3}OK127.0.0.1:6379> get user:1"{name:zhangsan,age:3}"127.0.0.1:6379> mset user:1:name zhangsan user:1:age 3OK127.0.0.1:6379> mget user:1:name user:1:age1) "zhangsan"2) "3"#################################################################getset #先get后set127.0.0.1:6379> getset db redis #如果不存在值则返回nil(nil)127.0.0.1:6379> get db"redis"127.0.0.1:6379> getset db mongodb #如果存在值则返回原来的值,并设置新的值"redis"127.0.0.1:6379> get db"mongodb"
string的使用场景:
List(列表)
在redis里可以把list 当成 栈(A边进A边出,先进后出),队列(A边进B边出,先进先出),阻塞队列
所有的list命令都是用l开头的
127.0.0.1:6379> lpush list one #将一个值 或者多个值,插入到列表的头部(左)(integer) 1127.0.0.1:6379> lpush list two(integer) 2127.0.0.1:6379> lrange list 0 -1 # 获取list中的值 通过区间获取具体的值1) "two"2) "one"127.0.0.1:6379> rpush list three #将一个值 或者多个值,插入到列表的尾部(右)(integer) 3127.0.0.1:6379> lrange list 0 -11) "two"2) "one"3) "three"##########################################################################127.0.0.1:6379> lrange list 0 -11) "two"2) "one"3) "three"127.0.0.1:6379> lpop list #移除list左边第一个"two"127.0.0.1:6379> lrange list 0 -11) "one"2) "three"127.0.0.1:6379> rpop list #移除list右边第一个"three"127.0.0.1:6379> lrange list 0 -11) "one"###########################################################################127.0.0.1:6379> lpush list two(integer) 2127.0.0.1:6379> lrange list 0 -11) "two"2) "one"127.0.0.1:6379> lindex list 1 #通过下标获取值"one" #阻塞队列:如果第一个不存在 阻塞等待,如果有值 可以作成生产者消费者模式127.0.0.1:6379> lindex list 0"two"###########################################################################127.0.0.1:6379> lpush list one(integer) 1127.0.0.1:6379> lpush list two(integer) 2127.0.0.1:6379> lpush list three(integer) 3127.0.0.1:6379> llen list #返回列表的长度(integer) 3############################################################################移除指定的值!#取关 uid#lrem127.0.0.1:6379> lrem list 1 one #移除list集合指定个数的value 1个value是 one的(integer) 1127.0.0.1:6379> lrange list 0 -11) "three"2) "three"3) "two"127.0.0.1:6379> lrem list 2 three(integer) 2127.0.0.1:6379> lrange list 0 -11) "two"############################################################################ ltrim 截取指定的长度127.0.0.1:6379> rpush mylist "hello"(integer) 1127.0.0.1:6379> rpush mylist "hello1"(integer) 2127.0.0.1:6379> rpush mylist "hello2"(integer) 3127.0.0.1:6379> rpush mylist "hello3"(integer) 4127.0.0.1:6379> ltrim mylist 1 2 #通过下标截取指定长度,这个list已经被改变了,截断了只剩下截取的元素OK127.0.0.1:6379> lrange mylist 0 -11) "hello1"2) "hello2"############################################################################rpoplpush 移动列表的最后一个元素到另一个列表127.0.0.1:6379> lrange mylist 0 -11) "hello"2) "hello1"3) "hello2"4) "hello3"127.0.0.1:6379> rpoplpush mylist myotherlist"hello3"127.0.0.1:6379> lrange mylist 0 -11) "hello"2) "hello1"3) "hello2"127.0.0.1:6379> lrange myotherlist 0 -11) "hello3"############################################################################exists 判断列表是否存在#lset 将列表中指定下标的值替换为另一个值 更新操作127.0.0.1:6379> exists list #判断这个列表是否存在(integer) 0127.0.0.1:6379> lset list 0 item #如果不存在 去更新列表就会报错(error) ERR no such key127.0.0.1:6379> lpush list value1(integer) 1127.0.0.1:6379> lrange list 0 01) "value1"127.0.0.1:6379> lset list 0 item #如果存在,更新当前下标的值OK127.0.0.1:6379> lrange list 0 01) "item"127.0.0.1:6379> lset list 1 item1(error) ERR index out of range############################################################################linset #将某个具体的value插入到列表中某个元素的前面(before)或者后面(after)127.0.0.1:6379> rpush mylist hello world(integer) 2127.0.0.1:6379> lrange mylist 0 -11) "hello"2) "world"127.0.0.1:6379> linsert mylist before world other(integer) 3127.0.0.1:6379> lrange mylist 0 -11) "hello"2) "other"3) "world"127.0.0.1:6379> linsert mylist after world new(integer) 4127.0.0.1:6379> lrange mylist 0 -11) "hello"2) "other"3) "world"4) "new"
小结
list的使用场景
消息排对! 消息队列 (lpush rpop),栈(lpush,lpop)
Set(集合)
set中的值无序且不能重复
127.0.0.1:6379> sadd myset hello #set集合中添加元素(integer) 1127.0.0.1:6379> sadd myset fuyang(integer) 1127.0.0.1:6379> sadd myset lovefuyang(integer) 1127.0.0.1:6379> smembers myset #查看set中所有的值1) "lovefuyang"2) "hello"3) "fuyang"127.0.0.1:6379> sismember myset hello #判断某个值是不是在set中 1:存在 0不存在(integer) 1127.0.0.1:6379> sismember myset world(integer) 0127.0.0.1:6379> scard myset #获取set集合的个数(integer) 3127.0.0.1:6379> srem myset hello #移除set中的参数(integer) 1127.0.0.1:6379> scard myset(integer) 2127.0.0.1:6379> smembers myset1) "lovefuyang"2) "fuyang"127.0.0.1:6379> sadd myset lovefuyang2(integer) 1127.0.0.1:6379> srandmember myset #随机抽选出一个元素"fuyang"127.0.0.1:6379> srandmember myset"lovefuyang"127.0.0.1:6379> srandmember myset 2 #随机抽选出指定个数的元素1) "lovefuyang"2) "lovefuyang2"127.0.0.1:6379> smembers myset1) "lovefuyang2"2) "lovefuyang"3) "fuyang"127.0.0.1:6379> spop myset #随机删除set中的元素"lovefuyang"127.0.0.1:6379> spop myset"lovefuyang2"127.0.0.1:6379> smembers myset1) "fuyang"############################################################################将一个指定的key移动到指定的set中127.0.0.1:6379> flushdbOK127.0.0.1:6379> sadd myset hello(integer) 1127.0.0.1:6379> sadd myset world fuyang(integer) 2127.0.0.1:6379> sadd myset2 set2(integer) 1127.0.0.1:6379> smove myset myset2 fuyang #将一个指定的值从一个set移动到另一个set中 smove 从哪个set 移动到哪个set 指定的值(integer) 1127.0.0.1:6379> smembers myset1) "world"2) "hello"127.0.0.1:6379> smembers myset21) "set2"2) "fuyang"############################################################################微博,B站,共同关注(并集)#数字集合类:# -差集# -交集# -并集127.0.0.1:6379> sadd key1 a(integer) 1127.0.0.1:6379> sadd key1 b(integer) 1127.0.0.1:6379> sadd key1 c(integer) 1127.0.0.1:6379> sadd key2 c(integer) 1127.0.0.1:6379> sadd key2 d(integer) 1127.0.0.1:6379> sadd key2 e(integer) 1127.0.0.1:6379> sdiff key1 key2 #查看差集 看key1的1) "a"2) "b"127.0.0.1:6379> sinter key1 key2 #查看交集 共同好友可以这样实现1) "c"127.0.0.1:6379> sunion key1 key2 #并集1) "c"2) "b"3) "d"4) "a"5) "e"127.0.0.1:6379>
微博,A用户将所有关注的人放在一个set集合中!将他的粉丝也放在一个集合中!
共同关注,共同爱好,二度好友,推荐好友(六度分割)
Hash(哈希)
Map集合,key-map(key-field-value)!这时候这个值是一个map集合! 本质和string类型没有太大区别,还是一个简单的key-value
127.0.0.1:6379> hset myhash field1 fuyang #set一个具体 key-value(integer) 1127.0.0.1:6379> hget myhash field1 # 获取一个字段值"fuyang"127.0.0.1:6379> hmset myhash field1 hello field2 world #set多个 key-valueOK127.0.0.1:6379> hmget myhash field1 field2 # 获取多个字段值1) "hello"2) "world"127.0.0.1:6379> hgetall myhash # 获取全部的数据1) "field1"2) "hello"3) "field2"4) "world"127.0.0.1:6379> hdel myhash field1 # 删除hash指定的key字段!对应的value值也就消失了。(integer) 1127.0.0.1:6379> hget myhash field1(nil)127.0.0.1:6379> hgetall myhash1) "field2"2) "world"############################################################################hlen127.0.0.1:6379> hmset myhash field1 hello field2 worldOK127.0.0.1:6379> hgetall myhash1) "field2"2) "world"3) "field1"4) "hello"127.0.0.1:6379> hlen myhash #获取hash表的字段数量(integer) 2############################################################################hexists127.0.0.1:6379> hexists myhash field1 #判断hash表中指定的字段是否存在(integer) 1127.0.0.1:6379> hexists myhash field3(integer) 0############################################################################hkeys 自获得所有的field#hvals 自获得所有的value127.0.0.1:6379> hkeys myhash #获得所有的field1) "field2"2) "field1"127.0.0.1:6379> hvals myhash #自获得所有的value1) "world"2) "hello"127.0.0.1:6379> hset myhash field3 5(integer) 1127.0.0.1:6379> hincrby myhash field3 1 #自增(integer) 6127.0.0.1:6379> hincrby myhash field3 -1 #自减(integer) 5127.0.0.1:6379> hsetnx myhash field4 hello #判断field 是否存在 不存在则创建返回1,存在则返回0 (用在设置锁里,如果成功version+1)(integer) 1127.0.0.1:6379> hsetnx myhash field4 hello(integer) 0127.0.0.1:6379>
hash用于存一些变更数据,key:user field:name、age....... 用户信息的保存 或 经常变动的信息!hash更适合对象的存储
**
**
Zset(有序集合)
在set的基础上,增加了一个值,set k1 v1 k2 v2 zset k1 score1 v1 score:1优先级最高,2次之....
127.0.0.1:6379> zadd myzset 1 one #添加一个值(integer) 1127.0.0.1:6379> zadd myzset 2 two 3 three #添加多个值(integer) 2127.0.0.1:6379> zrange myzset 0 -11) "one"2) "two"3) "three"###########################################################################127.0.0.1:6379> zadd salary 2500 xiaohong #添加三个用户(integer) 1127.0.0.1:6379> zadd salary 5000 zhangsan(integer) 1127.0.0.1:6379> zadd salary 10000 fuyang(integer) 1127.0.0.1:6379> zrangebyscore salary -inf +inf #从小到大排序1) "xiaohong"2) "zhangsan"3) "fuyang"127.0.0.1:6379> zrange salary 0 -1 #从小到大排序1) "xiaohong"2) "zhangsan"3) "fuyang"127.0.0.1:6379> zrangebyscore salary -inf +inf withscores #从小到大排序 并显示分数1) "xiaohong"2) "2500"3) "zhangsan"4) "5000"5) "fuyang"6) "10000"127.0.0.1:6379> zrangebyscore salary -inf +5000 withscores #显示工资小于5000员工的升序排序 并显示分数1) "xiaohong"2) "2500"3) "zhangsan"4) "5000"127.0.0.1:6379> zrevrange salary 0 -1 #从大到小排序 并显示分数1) "fuyang"2) "zhangsan"127.0.0.1:6379> zrevrange salary 0 -1 withscores1) "fuyang"2) "10000"3) "zhangsan"4) "5000"127.0.0.1:6379> ############################################################################zrem 移除#zcard 获取个数127.0.0.1:6379> zrange salary 0 -11) "xiaohong"2) "zhangsan"3) "fuyang"127.0.0.1:6379> zrem salary xiaohong # 移除有序集合中指定的元素(integer) 1127.0.0.1:6379> zrange salary 0 -11) "zhangsan"2) "fuyang"127.0.0.1:6379> zcard salary #获取有序集合中的个数(integer) 2127.0.0.1:6379> ############################################################################zcount 获取指定区间的数量127.0.0.1:6379> zadd myset 1 hello 2 world 3 fuyang(integer) 3127.0.0.1:6379> zcount myset 1 3 #获取指定区间的成员数量(integer) 3127.0.0.1:6379> zcount myset 1 2(integer) 2127.0.0.1:6379>
案例思路:set 排序 储存班级成绩,工资表排序,
普通消息 重要消息 带权重进行判断
排行榜
geospatial(地址位置空间)
朋友的定位,附近的人,打车距离计算
这个功能可以推算地理位置的信息,两地之间的距离,方圆几里的人!
官方文档:https://www.redis.net.cn/order/3685.html
只有6个命令
#geoadd 添加地理位置#规则:两级无法直接添加,一般会下载城市数据,直接通过java程序一次性导入!#参数:key 值(纬度 经度 名称)#有效的经度从-180度到180度。#有效的纬度从-85.05112878度到85.05112878度。#当坐标位置超出上述指定范围时,该命令将会返回一个错误。127.0.0.1:6379> geoadd china:city 116.40 39.90 beijing #添加城市信息 china:city是key 116.40和39.90是纬经度 beijing是对应城市(integer) 1127.0.0.1:6379> geoadd china:city 121.47 31.23 shanghai(integer) 1127.0.0.1:6379> geoadd china:city 106.50 29.53 chongqing 114.05 22.52 shenzhen(integer) 2127.0.0.1:6379> geoadd china:city 120.16 30.24 hangzhou 108.96 34.26 xian(integer) 2############################################################################geopos 获得当前定位127.0.0.1:6379> geopos china:city beijing1) 1) "116.39999896287918091" 2) "39.90000009167092543"127.0.0.1:6379> geopos china:city beijing chongqing1) 1) "116.39999896287918091" 2) "39.90000009167092543"2) 1) "106.49999767541885376" 2) "29.52999957900659211"
两个人之间的距离
geodist 返回两个给定位置之间的距离
单位:
127.0.0.1:6379> geodist china:city beijing shanghai #查看北京到上海的直线距离"1067378.7564"127.0.0.1:6379> geodist china:city beijing shanghai km"1067.3788"127.0.0.1:6379> geodist china:city beijing chongqing km #查看北京到重庆的直线距离"1464.0708"
georadius 以给定的经纬度为中心, 找出某一半径内的元素
附近的人(获得所有附近的人的地址,定位)通过半径来查询!
获得指定数量的人
所有的数据应该都录入:china:city(key),才会让结果更加清晰
127.0.0.1:6379> georadius china:city 110 30 1000 km #以 110 30 为中心 半径是1000km内的城市1) "chongqing"2) "xian"3) "shenzhen"4) "hangzhou"127.0.0.1:6379> georadius china:city 110 30 500 km1) "chongqing"2) "xian"127.0.0.1:6379> georadius china:city 110 30 500 km withdist # 查看信息并显示距离1) 1) "chongqing" 2) "341.9374"2) 1) "xian" 2) "483.8340"127.0.0.1:6379> georadius china:city 110 30 500 km withcoord # 查看信息并显示城市的经纬度1) 1) "chongqing" 2) 1) "106.49999767541885376" 2) "29.52999957900659211"2) 1) "xian" 2) 1) "108.96000176668167114" 2) "34.25999964418929977"127.0.0.1:6379> georadius china:city 110 30 500 km withcoord withdist count 1 筛选出指定的结果1) 1) "chongqing" 2) "341.9374" 3) 1) "106.49999767541885376" 2) "29.52999957900659211"127.0.0.1:6379>
georadiusbymember 命令 - 找出位于指定范围内的元素,中心点是由给定的位置元素决定
127.0.0.1:6379> georadiusbymember china:city beijing 1000 km #找出位于指定元素周围的其他元素1) "beijing"2) "xian"127.0.0.1:6379> georadiusbymember china:city shanghai 400 km1) "hangzhou"2) "shanghai"127.0.0.1:6379>
geohash 命令 - 返回一个或多个位置元素的 Geohash 表示
该命令将返回11个字符的Geohash字符串
127.0.0.1:6379> geohash china:city beijing chongqing #将二维的经纬度转成一维的字符串1) "wx4fbxxfke0"2) "wm5xzrybty0"127.0.0.1:6379>
**
**
geo底层的实现原理其实就是zset!我们可以使用zset命令来操作geo!
127.0.0.1:6379> zrange china:city 0 -1 #查看地图中全部的元素1) "chongqing"2) "xian"3) "shenzhen"4) "hangzhou"5) "shanghai"6) "beijing"127.0.0.1:6379> zrem china:city beijing #移除指定元素(integer) 1127.0.0.1:6379> zrange china:city 0 -11) "chongqing"2) "xian"3) "shenzhen"4) "hangzhou"5) "shanghai"127.0.0.1:6379>
hyperloglog(基数)
什么基数
A{1,3,5,7,8,7}
B{1,3,5,7,8}
基数(不重复的元素)= 5(1,3,5,7,8),可以接收误差
hyperloglog 基数统计算法
优点:占用的内存是固定的,long类型(2^64)不同元素的基数,只需要费12KB内存!如果要从内存角度比较的话,hyperloglog首选!
网页的UV(用户访问量,一个人访问一个网站多次,还是算作一个人)
传统的方式:set保存用户的id,不重复,就可以统计set中的元素数量作为标准判断!
这个方式如果保存大量的用户id(可能会很长),比较占内存!目的是计数,而不是保存用户id;
127.0.0.1:6379> pfadd mykey a b c d e f g h i j #创建第一组元素 mykey(integer) 1127.0.0.1:6379> pfcount mykey #统计mykey元素的基数数量(integer) 10127.0.0.1:6379> pfadd mykey2 i j z x c v b n m #创建第二组元素 mykey2(integer) 1127.0.0.1:6379> pfcount mykey2 #统计第二组元素的基数数量(integer) 9127.0.0.1:6379> pfmerge mykey3 mykey mykey2 #创建第三组元素 mykey3 取mykey mykey2的并集OK127.0.0.1:6379> pfcount mykey3 #统计第三组元素的基数数量(integer) 15127.0.0.1:6379>
如果允许容错,那么可以使用hyperloglog!
如果不允许容错,那么使用set数据类型即可!
**
**
bitmaps(位存储)
例:1、疫情 中国14亿人 可以用14亿个0来表示,如果被感染了用1表示
0 1 0 1 没有感染的用0 感染了用1 可以快速统计插值
统计疫情感染人数
? 2、统计用户信息,活跃、不活跃、登录、未登录、打卡!(两个状态的都可以使用bitmaps)
bitmaps位图,数据结构!都是操作二进制位来进行记录,就只有0 和 1 两个状态!
365天 = 365 bit → 1字节 = 8bit → 64 个字节左右!
#统计周一到周日的打开!#周一:1 周二 0 ....127.0.0.1:6379> setbit sign 0 1 #setbit key 位数(从0开始) 值 星期1 打卡了(integer) 0127.0.0.1:6379> setbit sign 1 0 # 星期二 未打卡(integer) 0127.0.0.1:6379> setbit sign 2 0(integer) 0127.0.0.1:6379> setbit sign 3 0(integer) 0127.0.0.1:6379> setbit sign 4 0(integer) 0127.0.0.1:6379> setbit sign 5 1(integer) 0127.0.0.1:6379> setbit sign 6 1(integer) 0#查看某一天是否打卡127.0.0.1:6379> getbit sign 3 #查看周4是否打卡(integer) 0127.0.0.1:6379> getbit sign 6 #查看周日是否打卡(integer) 1
统计操作 统计打卡天数
127.0.0.1:6379> bitcount sign(integer) 3
原子性:要么同时成功,要么同时失败!
Redis事务没有隔离级别的概念 (所有的命令在事务中,并没有直接执行,只有发起了执行命令【Exec】的时候才会执行。)
Redis单条命令是保证原子性的,但是事务是不保证原子性的!
Redis事务本质:一组命令的集合!一个事务中的所有命令都会被序列化,在事务执行过程中,会按照顺序执行!
一次性、顺序性、排他性(不允许别人干扰)
------ 队列 set set set 执行 -------
redis的事务:
正常执行事务
127.0.0.1:6379> multi #开启事务OK127.0.0.1:6379(TX)> set k1 v1 #命令入队QUEUED127.0.0.1:6379(TX)> set k2 v2 #命令入队QUEUED127.0.0.1:6379(TX)> get k2 #命令入队QUEUED127.0.0.1:6379(TX)> set k3 v3 #命令入队QUEUED127.0.0.1:6379(TX)> exec #执行事务1) OK2) OK3) "v2"4) OK
放弃事务
127.0.0.1:6379> multiOK127.0.0.1:6379(TX)> set k1 v1QUEUED127.0.0.1:6379(TX)> set k2 v2QUEUED127.0.0.1:6379(TX)> set k4 v4QUEUED127.0.0.1:6379(TX)> discard #取消事务OK127.0.0.1:6379> get k4 #事务队列中的命令都不会被执行(nil)
错误
编译型异常(代码有问题,命令有错),事务中所有的命令都不会被执行!
127.0.0.1:6379> multiOK127.0.0.1:6379(TX)> set k1 v1QUEUED127.0.0.1:6379(TX)> set k2 v2QUEUED127.0.0.1:6379(TX)> set k3 v3QUEUED127.0.0.1:6379(TX)> getset k3 #错误的命令(error) ERR wrong number of arguments for ‘getset‘ command127.0.0.1:6379(TX)> set k4 v4QUEUED127.0.0.1:6379(TX)> set k5 v5QUEUED127.0.0.1:6379(TX)> exec #执行事务是报错的(error) EXECABORT Transaction discarded because of previous errors.127.0.0.1:6379> get k5 #所有的命令都不会执行(nil)127.0.0.1:6379>
运行时异常(1/0),如果事务队列中存在语法错误,那么执行事务的时候,其他命令是可以被执行的 错误命令会抛出异常
127.0.0.1:6379> set k1 "v1"OK127.0.0.1:6379> multiOK127.0.0.1:6379(TX)> incr k1 #执行失败QUEUED127.0.0.1:6379(TX)> set k2 v2QUEUED127.0.0.1:6379(TX)> set k3 v3QUEUED127.0.0.1:6379(TX)> get k3QUEUED127.0.0.1:6379(TX)> exec1) (error) ERR value is not an integer or out of range #第一条命令报错了,但是依旧执行成功了2) OK3) OK4) "v3"127.0.0.1:6379> get k2"v2"127.0.0.1:6379>
悲观锁:
乐观锁:
监控测试:
正常执行成功
127.0.0.1:6379> set money 100OK127.0.0.1:6379> set out 0OK127.0.0.1:6379> watch money #监视money对象OK127.0.0.1:6379> multiOK127.0.0.1:6379(TX)> decrby money 20QUEUED127.0.0.1:6379(TX)> incrby out 20QUEUED127.0.0.1:6379(TX)> exec #事务正常结束,数据期间没有发生变动1) (integer) 802) (integer) 20
测试多线程修改值,使用用watch 可以当做redis的乐观锁操作
#线程1127.0.0.1:6379> watch money #监控moneyOK127.0.0.1:6379> multi #开启事务OK127.0.0.1:6379(TX)> decrby money 10 #加入命令 money -10QUEUED127.0.0.1:6379(TX)> incrby out 10 #加入命令 out +10QUEUED. #先不执行事务..#线程2127.0.0.1:6379> get money #获取money"80"127.0.0.1:6379> set money 1000 #修改moneyOK127.0.0.1:6379> ...#线程1127.0.0.1:6379(TX)> exec #执行事务,money被修改了,所以执行失败(nil)127.0.0.1:6379> ########################################################127.0.0.1:6379> unwatch #如果发现事务执行失败了,就先解锁OK127.0.0.1:6379> watch money #获取最新的值,再次监视,OK127.0.0.1:6379> multiOK127.0.0.1:6379(TX)> decrby money 10QUEUED127.0.0.1:6379(TX)> incrby out 10QUEUED127.0.0.1:6379(TX)> exec #对比监视的值是否发生了变化,如果没有变化,那么执行成功,如果发生变化,则执行失败1) (integer) 9902) (integer) 30127.0.0.1:6379>
用java来操作redis
jedis是官方推荐的java连接开发工具!使用java操作redis中间件。
1、导入对应的依赖
https://mvnrepository.com/ 在官网上找
2、编码测试
测试连接报错:Failed connecting to host xxx.xxx.xxx.xxx:6379
报错原因:
1、redis的配置文件 绑定了只能本地访问
2、服务器没有开放6379端口
String
List
Set
Hash
Zset
所有的命令跟linux中是一样的
springboot操作数据:spring-data jpa jdbc mongodb redis!
SpringData
注入springboot redis依赖
说明:在springboot2.x之后,原来使用的jedis被替换为lettuce
jedis:采用的直连,多个线程操作的话,是不安全的,如果想要避免不安全的,使用jedis pool连接池 更像BIO模式
lettuce:采用netty,实例可以在多个线程中共享,不存在线程不安全的情况。可以减少线程数量 更像NIO模式
源码分析:
1、找到spring-boot-autoconfigure下的spring.factories
2、通过查询redis 找到spring.factories里的RedisAutoConfiguration
3、查看需要配置的信息 @EnableConfigurationProperties的类
@Bean @ConditionalOnMissingBean(name = "redisTemplate") // 可以自己定义一个redisTemplate来替换这个默认的 public RedisTemplate<Object, Object> redisTemplate( RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { // 默认的RedisTemplate没有过多的设置,redis对象保存都是要序列化! // 两个泛型都是 Object 的类型,后面使用需要强制转换 期望<String, Object> RedisTemplate<Object, Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); return template; } @Bean @ConditionalOnMissingBean //由于String类型是redis中最长使用的类型,所有说单独提出来了一个bean public StringRedisTemplate stringRedisTemplate( RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { StringRedisTemplate template = new StringRedisTemplate(); template.setConnectionFactory(redisConnectionFactory); return template; }
1、导入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId></dependency>
2、配置连接
#redis配置spring.redis.host=121.5.160.37spring.redis.port=6379
3、测试
@Autowired RedisTemplate redisTemplate; /** * 测试1 * @return */ @GetMapping(value = "/1") public String test1() { // redisTemplate 操作不同的数据类型,api的指令是一样的 // opsForValue() 操作字符串 类似String // opsForList() 操作List // opsForSet() // opsForHash() // opsForGeo() // opsForZset() // opsForHyperLogLog() // 除了基本的操作,常用的方法都可以直接通过redisTemplate操作,例如:事务 和基本的crud // 获取redis的连接对象 // RedisConnection connection = redisTemplate.getConnectionFactory().getConnection(); // connection.flushAll(); // connection.flushDb(); return ""; }
序列化
序列化配置
**
**
自己编写一个RedisTemplate
package com.news.test.config;import java.net.UnknownHostException;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.redis.connection.RedisConnectionFactory;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;import org.springframework.data.redis.serializer.StringRedisSerializer;import com.fasterxml.jackson.annotation.JsonAutoDetect;import com.fasterxml.jackson.annotation.PropertyAccessor;import com.fasterxml.jackson.databind.ObjectMapper;@Configurationpublic class RedisConfig { // 编写自己的redisTemplate @Bean @SuppressWarnings("all") public RedisTemplate<String, Object> redisTemplate( RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { // 为了开发方便,一般直接使用<String, Object> RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); // Jackson序列化配置 Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class); // object序列化 ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); // String序列化 StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); // key采用string的序列化方式 template.setKeySerializer(stringRedisSerializer); // hash的key也采用string的序列化方式 template.setHashKeySerializer(stringRedisSerializer); // value序列化采用jackson template.setValueSerializer(jackson2JsonRedisSerializer); // hash的value序列化采用jackson template.setHashValueSerializer(jackson2JsonRedisSerializer); template.afterPropertiesSet(); return template; }}
正常情况会把redis 的 get set expire等方法封装到Utils类里方便使用**
**
通过这个配置文件来启动!
配置文件 unit单位 对大小不敏感!
类似于spring的 Improt
bind 127.0.0.1 -::1 #绑定的ipprotected-mode yes #保护模式 是否受保护port 6379 #端口设置
daemonize yes #以守护进程的方式运行,默认是no,需要自己开启yes,不然的话 一退出 进程就结束了pidfile /var/run/redis_6379.pid #如果以后台的方式运行,就需要指定一个pid文件(进程文件)# 日志# Specify the server verbosity level.# This can be one of:# debug (a lot of information, useful for development/testing) #使用大量的日志 一般用于测试/开发# verbose (many rarely useful info, but not a mess like the debug level) #记录较多的日志信息 很像debug# notice (moderately verbose, what you want in production probably) #生产环境# warning (only very important / critical messages are logged) #警告 打印重要的关键的信息loglevel noticelogfile "" #日志的文件位置名 如果为空,就是标准的输出databases 16 #数据库数量,默认是16个数据库always-show-logo no #是否显示logo 就是执行时控制台打印的那个 yes显示 no不显示
持久化,在规定的时间内,执行了多少次操作,则会持久化到文件 .rdb .aof
redis 是内存数据库,如果没有持久化,那么数据断电及失!
save 3600 1 #如果3600s内,如果至少有1个key进行了修改,那么就进行持久化操作save 300 100 #如果300s内,如果至少有100个key进行了修改,那么就进行持久化操作save 60 10000 #如果60s内,如果至少有10000个key进行了修改,那么就进行持久化操作stop-writes-on-bgsave-error yes #持久化如果出错了 是否还要继续工作!rdbcompression yes #是否压缩 .rdb 文件,需要消耗一些CPU资源!rdbchecksum yes #保存.rdb文件的时候,是否进行检查校验 如果出错了 可以自动进行一些修复dir ./ #rdb文件保存的目录 默认就是当前目录下
设置 redis的密码
两种方式:
requirepass 123456
127.0.0.1:6379> config get requirepass1) "requirepass"2) ""127.0.0.1:6379> config set requirepass "123456"OK127.0.0.1:6379> auth 123456 #使用密码进行登录OK127.0.0.1:6379> config get requirepass1) "requirepass"2) "123456"
maxclients 10000 #限制最大客户端数量maxmemory <bytes> #redis 配置最大内存容量maxmemory-policy noeviction #内存达到上限之后的处理策略↓ 1、volatile-lru:只对设置了过期时间的key进行LPU(默认值) 2、allkeys-lru:删除lru算法的key 3、volatile-random:随机删除即将过期的key 4、allkeys-random:随机删除 5、volatile-ttl:删除即将过期的 6、noeviction:永不过期 返回错误
appendonly no #默认是不开启aof模式的,默认是使用rdb方式持久化的,在大部分所有的情况下,rdb完全够用!appendfilename "appendonly.aof" #持久化文件的名字# appendfsync always #每次修改都会写入 sync,消耗性能appendfsync everysec #每秒执行一次 sync(同步),可能会丢失这1s的数据!# appendfsync no #不执行 sync,这个时候操作系统自己同步数据,速度最快
Redis是内存数据库,如果不将内存中的数据库状态保存到磁盘,那么一旦服务器进程退出,服务器中的数据库状态也会消失,所以Redis提供了持久化功能!
什么是RDB
在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是Snapshot快照,它恢复时是将快照文件直接读到内存里。
Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件,整个过程中,主进程是不进行任何IO操作的。这就确保了极高的性能,如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式要加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。默认的就是RDB,一般情况下不需要修改这个配置!
rdb保存的文件叫 dump.rdb
触发机制
1、save的规则满足的条件下,会自动触发rdb规则
2、执行flushall命令,也会触发rdb规则
3、退出redis,也会产生rdb文件!
备份就会自动生成一个dump.rdb
如何恢复rdb文件!
1、只需要将rdb文件放在redis启动目录就可以了,redis启动的时候会自动检测dump.rdb 恢复其中的数据!
2、查看需要存放的位置
127.0.0.1:6379> config get dir1) "dir"2) "/etc"127.0.0.1:6379>
优点:
1、适合大规模的数据恢复
2、对数据的完整性要求不高
缺点:
1、需要一定的时间间隔进程操作,如果redis意外宕机了,这个最后一次修改的数据就没有了!
2、fork进程的时候,会占用一定的空间!
将所有的命令都记录下来,history,回复的时候就是把这个文件全部再执行一遍
以日志的形式来记录每一个写的操作,将Redis执行过的所有指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作
Aof保存的是appendonly.aof文件
原文:https://www.cnblogs.com/f9264/p/14856240.html