Redis
1、什么是Redis
Redis(Remote Dictionary Server),远程字典服务
是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
免费和开源!是当下最热门的NoSQL技术之一!也被人们称之为结构化数据库!
Redis能干嘛?
- 内存存储、持久化,内存中是断电及失,持久化很重要(rdb,aof)
- 效率高,可以用于高速缓存
- 发布订阅系统
- 地图信息分析
- 计时器、计数器(浏览量!)
- …
特性
- 多样的数据类型
- 持久化
- 集群
- 事务
- …
Redis中文网:https://www.redis.net.cn/tutorial/3501.html
2、什么是NoSQL
NoSQL = Not Only SQL(不仅仅是SQL)
关系型数据库:表格,行,列
泛指非关系型数据库的,随着web2.0互联网的诞生,传统的关系型数据库很难对付web2.0时代,尤其是超大规模的高并发的社区,暴露出来很多难以克服的问题,NoSQL在当今大数据环境发展的十分迅速,Redis是发展最快的,而且是我们当下必须要掌握的一个技术!
真正在公司中的实践:NoSQL+RDBMS一起使用
3、连接redis
java阿里云自带8版本,gcc也自带10版本
使用宝塔linux面板安装的redis,在/www/server下面
redis-cli在/www/server/redis/src下面
redis-cli -p 6379
查看redis进程是否开启:
ps -ef|grep redis
关闭连接:
shutdown
启动redis,如果通过宝塔安装的话,redis-server执行不了,直接在宝塔面板开启redis即可
redis-server文件位置输入如下命令查询位置
find / -name redis-server
未使用Linux宝塔面板安装的redis操作如下:
1、下载安装包 `redis**.tar.gz`
2、移动Redis安装包,程序放在/opt下 `mv redis**.tar.gz /opt`
3、解压安装包 `tar -zxvf redis**.tar.gz`
4、进入解压后的文件,找到redis.conf配置文件,拷贝到别处
5、安装gcc `yum install gcc-c++`
6、查看gcc是否安装成功 `gcc -v`
7、把需要的文件全部配置上 `make && make install`
8、redis的默认安装路径 /usr/local/bin
9、将redis的配置文件拷贝到这个bin目录下,创建一个文件夹
`mkdir myconfig` `cp /opt/redis**/redis.conf myconfig`
10、更改配置文件 `vim redis.conf`
将里面的 daemonize no 改为 daemonize yes
11、在bin目录下,启动redis服务 `redis-server myconfig/redis.conf`
12、客户端连接redis `redis-cli -p 6379`
13、测试连接:
输入`ping` 输出PONG 表示连接成功
设置名称 `set name heze`
得到名称 `get name`
显示所有名称 `keys *`
14、查看redis进程是否开启 `ps -ef|grep redis` 两进程开启
15、关闭服务 `shutdown` 退出 `exit`
16、再次查看进程是否开启 `ps -ef|grep redis`
4、基础的知识
redis默认有16个数据库
基本指令:
1、`select 3` #表示选择第3个数据库,从0开始,默认为0
2、`dbsize` #显示DB大小
3、`flushdb` #清除当前数据库
4、`flushall` #清除全部数据库的内容
5、`exists heze` #判断是否存在heze这个key,1存在,0不存在
6、`move heze 1` #移除heze这个key 1:当前数据库
7、`expire heze 10` #10秒后key过期
8、`ttl heze` #查询还剩多久时间
9、`type heze` #查看类型
10、``
Redis 是单线程的
明白Redis是很快的,Redis是基于内存操作,CPU不是Redis性能瓶颈,Redis的瓶颈是根据机器的内存和网络带宽,就使用单线程操作了。
Redis 是C语言写的,官方提供的数据为 100000+的QPS,这个不比同样使用key-value的Memecache差!
Redis 为什么单线程还是这么快?
1、误区1:高性能的服务器一定是多线程的么?
2、误区2:多线程(CPU上下文会切换!)一定比单线程效率高?
核心:redis是将所有的数据全部放在内存中的,所以说使用单线程操作效率就是最高的,多线程(CPU上下文会切换:耗时的操作!!!),对于内存系统来说,如果没有上下文切换效率就是最高的。多次读写都是在一个CPU上的,在内存情况下,这个就是最佳的方案!
5、五大数据类型
Redis是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件MQ。
5.1、String(字符串)
使用tab键能够快速补全命令
1、`set key1 value1` #设置一个键值对
2、`get key1` #获得key1的值
2、`append key1 “hello“` #追加字符串“hello”,得到value1hello
3、`strlen key1` #获得key1的值字符串长度
4、数字增减操作
-`set views 0` #设置views值为0
-`incr views` #自增1
-`decr views` #自减1
-`incrby views 10` #增加10
-`decrby views 10` #减少10
5、`getrange key1 0 3` #获取[0-3]范围的字符串
6、`getrange key1 0 -1` #获取全部字符串,与get key1一样
7、`setrange key1 1 xx` #替换字符串,得到vxxue1hello
8、`setex key2 30 "hello"` #设置key2的值为hello,30秒后过期
9、`setnx key3 "redis"` #如果key3不存在,创建key3,保证数据唯一,若为set创建,则直接会更新掉原来的数据
10、`setnx key3 "MongoDB"` #如果key3存在,创建key3失败
11、`mset k1 v1 k2 v2 k3 v3` #同时创建多个键值对
12、`mget k1 k2 k3` #同时获得k1k2k3的值
13、`msetnx k1 v2 k4 v4` #操作失败,因为k1已经存在,原子性操作
14、`set user:1 {name:zhangsan,age:3}` #设置一个user:1对象,值为json字符来保存一个对象
15、`getset k2 v3` #组合命令,先get然后set
String类型的使用场景:
- 计数器
- 统计多单位的数量
- 粉丝数
- 对象缓存存储
5.2、List(列表)
list可以扩展成栈、队列、阻塞队列使用 l
所有的list命令都是用l开头的
1、`lpush list one` #将名为list的链表push一个one,左边插入
2、`rpush list two` #从右边插入
3、`lrange list 0 -1` #列出所有的元素
4、`lpop list` #移除左边第一个元素
5、`rpop list` #移除右边第一个元素
6、`lindex list 1` #显示下标为1的元素
7、`llen list` #获取list的长度
8、`lrem list 1 one` #移除list集合中指定个数的value,精确匹配
9、`ltrim list 1 2` #通过下标截取指定的长度,这个list被修改成截取的
10、`rpoplpush list list1` #移除list最右边元素到list1从左边插入
11、`lset list 1 other` #将存在的list下标的值更新为other
12、`linsert list before/after "word" "other"` #在list中的word前/后插入other
5.3、Set(集合)
set中的值是不能重复的!s
1、`sadd myset "hello"` #向名为myset的集合添加一个名为hello的元素
2、`smembers myset` #查看myset集合中的所有成员
3、`sismember myset hello` #判断myset集合中是否有hello这个值
4、`scard myset` #获得myset集合中元素的个数
5、`srem myset hello` #移除hello这个元素
6、`srandmember myset` #随机抽选一个元素
7、`spop myset` #随机删除一个set集合中元素
8、`smove myset myset2 "hello"` #将一个指定的值,移动到另一个set集合
9、`sdiff myset myset2` #获取两个set集合中的不同的元素
10、`sinter myset myset2` #获取两个set集合中相同的元素
11、`sunion myset myset2` #获取两个set集合中并集
12、``
使用用途:
- 微博共同关注
- 共同爱好
- 可能认识的好友
5.4、Hash(哈希)
Map集合,里面存值为key-value形式 h
1、`hset myhash k1 v1` #向myhash里面添加一个键值对k1-v1
2、`hget myhash k1` #从myhash里面获得k1的值
3、`hmset myhash k2 v2 k3 v3` #同时添加多个键值对
4、`hmget myhash k1 k2` #同时获取多个键的值
5、`hgetall myhash` #获得全部的hash键值对
6、`hdel myhash k1` #删除k1的键值对
7、`hlen myhash` #获得键值对的个数
8、`hexist myhash k1` #判断是k1位key的键值对是否存在
9、`hkeys myhash` #只获得hash中所有的key
10、`hvals myhash` #只获得hash中的所有value
11、`hincrby myhash k1 -1` #向k1的值进行-1操作
12、`hsetnx myhash k1 v2` #如果存在k1,执行失败,不存在再创建
hash变更的数据user name age,尤其是用户信息之类的,经常变动的信息,hash更适合对象的存储,String更加适合字符串存储!
5.5、Zset(有序集合)
在set的基础上,增加了一个值,实现排序功能 z
1、`zadd myset 1 one` #添加标识符为1的one
2、`zadd myset 2 two 3 three` #同时添加多个有标识符的值
3、`zrange myset 0 -1` #获取所有值,从小到大
4、`zrevrange myset 0 -1` #获取所有值,从大到小
5、`zrangebyscore myset -inf inf` #从无限小到无限大进行排序
6、`zrangebyscore myset -inf 2500` #获得从最小到2500的值
7、`zrevrangebyscore myset inf -inf` #从大到小排序
8、`zrem myset one` #删除one这个元素
9、`zcard myset` #获得总共元素个数
10、`zcount myset 1 3` #标识值[1-3]之间的元素数量
如其他命令,建议查官方文档
案例思路:
- set 排序 存储班级成绩表,工资表排序
- 排行榜应用实现
6、三种特殊数据类型
geospatial 地理位置-> geo
朋友的定位,附近的人,打车距离计算?
Redis的Geo在Redis3.2版本就退出了!这个功能可以推算地理位置的信息,两地之间的距离,方圆几里的人!
geoadd
geoadd : 添加地理位置
`geoadd china:city 116.40 39.90 beijing` #添加beijing经度维度
geopos
geopos : 获得经度维度(定位)
`geopos china:city beijing chongqing` #获取beijing和chongqing经度纬度
geodist
geodist : 定位距离
`geodist china:city beijing shanghai km` #获取北京和上海的距离km
georadius
georadius : 以给定的经纬度为中心,找出某一半径内的元素
我附近的人?(获得所有附近的人的住址,定位!)通过半径来查询!
`georadius china:city 110 30 1000 km withdist withcoord count 2` #查找存在此经纬度范围1000km的所有地理信息,包含地点名称,距离和限定查找最多两个数量,在地理集合里面存在
georadiusbymember
georadiusbymember : 找某一元素周围的其他元素
`georadiusbymember china:city shanghai 400 km` #查询离上海400km范围内的城市,包含自己
GEO底层实现原理就是Zset,我们可以用Zset命令来操作geo
例如:
`zrange china:city 0 -1` #查看所有的城市成员
7、Hyperloglog
简介
HyperLogLog是Redis的高级数据结构,是统计基数的利器。pf
基数:不重复元素的个数
优点:占用的内存是固定的,2^64不同的元素的技术,只需要12KB内存,从内存的角度来看,Hyperloglog为首选。
网页的UV(一个人访问一个网站多次,但还是算作一个人!)
UV是指Unique Visitor,即独立访客,即1天内访问某站点的用户数。
传统的方式,set保存用户的id,然后就可以统计set中的元素数量作为判断标准!
这个方式如果保存大量的用户id,就会比较麻烦,我们的目的是为了计数,而不是保存用户id
测试使用pf
1、`pfadd mykey a b c` #创建:第一组元素 mykey
2、`pfcount mykey` #统计:mykey元素的基数数量
3、`pfmerge mykey3 mykey mykey2` #合并:将mykey和mykey2合并成mykey3
8、Bitmap
简介
Bitmap 称为位图,一种数据结构,采用位存储,都是使用二进制位来进行记录,只有0和1两个状态。
用途:统计用户信息:
- 活跃、不活跃
- 登录、不登录
- 打卡
- 两个状态的,都可以使用Bitmap
1、`setbit sign 0 1` #周一打卡
2、`setbit sign 2 0` #周三未打卡,0未打卡,1打卡
3、`getbit sign 2` #查看星期三是否打卡
4、`bitcount sign` #查看打卡天数(1)
9、事务
Redis事务本质:一组命令的集合!一个事务中的所有命令都会被序列化,在事务执行过程中,会被顺序执行!
执行一系列的命令:一次性、顺序性、排他性
----队列 set set set 执行----
Redis事务没有隔离级别的概念
所有的命令在事务中,并没有被直接执行,只有发起执行指令的时候才会执行!
Redis单条命令是保存原子性的,但是事务不保证原子性!
redis的事务:
- 开启事务(Multi)
- 命令入队(…)
- 执行事务(exec)
正常执行事务!
127.0.0.1:6379> MULTI #开启事务
OK
127.0.0.1:6379(TX)> set k1 v1 #入队
QUEUED
127.0.0.1:6379(TX)> set k2 v2 #入队
QUEUED
127.0.0.1:6379(TX)> get k2 #入队
QUEUED
127.0.0.1:6379(TX)> set k3 v3 #入队
QUEUED
127.0.0.1:6379(TX)> EXEC #执行事务
1) OK
2) OK
3) "v2"
4) OK
执行一个事务,就结束了,不存在了
放弃事务
`discard` #放弃,取消事务
编译型异常(代码有问题,命令有错),事务中的所有命令都不会执行
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> set k1 v1
QUEUED
127.0.0.1:6379(TX)> set k2 v2
QUEUED
127.0.0.1:6379(TX)> setget k2 #错误命令
(error) ERR unknown command 'setget', with args beginning with: 'k2'
127.0.0.1:6379(TX)> get k2
QUEUED
127.0.0.1:6379(TX)> EXEC #所有的命令都不会被执行
(error) EXECABORT Transaction discarded because of previous errors.
运行时异常(1/0),如果事务队列中存在语法性,那么执行命令的时候,其他命令是可以正常执行的,错误命令抛出异常
127.0.0.1:6379> set k1 "v1"
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> incr k1 #运行时错误,string类型不能加1
QUEUED
127.0.0.1:6379(TX)> get k1
QUEUED
127.0.0.1:6379(TX)> exec
1) (error) ERR value is not an integer or out of range #报错
2) "v1" #正常执行
10、Redis实现乐观锁
监控 watch (乐观锁)
悲观锁:
- 很悲观,认为什么时候都会出问题,无论做什么都会加锁!
乐观锁:
- 很乐观,认为什么时候都不会出问题,所以不会上锁!更新数据的时候去判断一下,在此期间是否有人修改过这个数据
- 获取version
- 更新的时候比较version
Redis测试
正常执行成功!
127.0.0.1:6379> set money 100
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> WATCH money #监视 money 对象
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> DECRBY money 20
QUEUED
127.0.0.1:6379(TX)> INCRBY out 20
QUEUED
127.0.0.1:6379(TX)> EXEC #事务正常结束,数据期间没有发生变动,这个时候正常执行成功
1) (integer) 80
2) (integer) 20
测试多线程修改值,使用watch可以当做redis的乐观锁操作
# 线程1
127.0.0.1:6379> get money
"80"
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379(TX)> DECRBY money 20
QUEUED
127.0.0.1:6379(TX)> INCRBY out 20 # 线程2介入修改值
QUEUED
127.0.0.1:6379(TX)> exec
(nil)
127.0.0.1:6379> get money
"60"
# 线程2,在进入事务未执行时进行如下操作
127.0.0.1:6379> get money
"80"
127.0.0.1:6379> DECRBY money 20
(integer) 60
此时若需继续事务操作
1、`unwatch` #解锁
2、`watch money` #加锁,在最新值上加锁
3、*** #正常事务操作
总结:
到此第一阶段基础redis学习结束,记录了redis的一些基础知识、命令和一些数据类型,高级数据结构、乐观锁事务等操作,进行记录以供学习。后面第二阶段就是使用ieda使用redis等一系列的操作,放在下一篇文章 。