数据库redis
入门
NoSQL的四大分类
入门概述
什么是Redis?
Redis (Remote Dictionary Server ),即远程字典服务,是一个开源的支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。 可以用作数据可、缓存、消息中间件
Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。
它支持多种类型的数据结构,如 字符串(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会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
redis中文官方网站 Redis推荐都是在Linux服务器上搭建的
Redis的作用
- 内存存储、持久化(rdb、aof)
- 效率高,用于高速缓存
- 发布订阅系统
- 地图信息分析
- 计数器
- .......
特性
- 多样的数据类型
- 持久化
- 支持集群、事务等
Windows安装
-
自行下载解压 不做详细说明
-
开启redis 双击运行服务 默认端口号为6379
-
使用redis客户端进行连接
输入
ping
命令返回pong
表示成功连接
windows下就介绍到此,毕竟企业级的开发都是在redis下开发的
Linux安装
-
宝塔轻易安装即可 进入
www/server/redis
可以看到redis的配置 -
因为redis的运行需要依赖gcc 进行编译,所以还要安装gcc环境
yum install gcc-c++
# 检查gcc是否成功安装
gcc -v
# 配置所需要的文件
make
# 检查
make install
-
redis的默认安装路径在
usr/local/bin
目录下 -
新建目录将redis的配置文件移动到当前
# 新建文件
mkdir qdconfig
# 复制配置文件
cp /www/server/redis/redis.conf qdconfig
我们之后用这个配置文件进行启动
- redis默认不是后台启动的,需要修改配置文件
vim redis.conf
6. 以当前配置启动redis服务
redis-server qdconfig/redis.conf
7. 测试set
8. 查看服务
9. 关闭服务
shutdown
exit
ok~~安装至此完成
性能测试
redis-benchmark 是一个压力测试工具,可以通过他测试性能问题
举个栗子:测试10个并发,100个请求
redis-benchmark -h localhost -p 6379 -c 10 -n 100
Reids基础
redis有16个数据库,默认使用第0个数据库,可以使用select
切换数据库 数据不同下标不同
127.0.0.1:6379> SELECT 3 # 切换数据库
OK
127.0.0.1:6379[3]> DBSIZE # 查看当前数据库的大小
(integer) 0
127.0.0.1:6379> exists name # 判断key是否存在
(integer) 0
127.0.0.1:6379> exists name qiandu # 判断key为qiandu是否存在
(integer) 0 # 返回1为存在 返回0为不存在
127.0.0.1:6379> move name 1 # 移除key 1代表当前的数据库
(integer) 1
127.0.0.1:6379> TYPE name # 查看key的具体类型
string
127.0.0.1:6379> expire name 10 # 设置过期时间
(integer) 1
127.0.0.1:6379> ttl name # 查看剩余时间
127.0.0.1:6379[3]> keys * # 查看所有的key
(empty array)
127.0.0.1:6379> FLUSHdb # 清空当前库的key
OK
127.0.0.1:6379> FLUSHALL # 清空全部数据库中的key
OK
更过命令:Redis命令中心
为什么Redis是单线程的呢?
Redis是单线程的! Redis是基于内存操作的,CPU并不是它的瓶颈,Reids的瓶颈是服务器的内存以及网络的带宽,这些可以通过单线程来实现
为什么Redis的单线程还那么快呢?
Redis是C语言写的,官方提供的数据为100000+的QPS(每秒查询率) 不比其他的慢。
另外多线程的效率也不一定比单线程的效率高,Reids将所有的数据都放入内存之中,多次读写都在一个cpu进行,多线程会使用CPU进行切换上下文的操作 这是一个耗时的操作,对于系统内存如果没有内存的切换效率就是最佳的
基本数据类型
Redis有5大常见数据类型 String List Set Hash Zset 3大特殊数据类型geospatial
hyperloglog
bitmaps
String
string是最常用的类型了
127.0.0.1:6379> set name qiandu # 设置key与value
OK
127.0.0.1:6379> append name "hello" # 追加字符串 如果key不存在 相当于新建
(integer) 11
127.0.0.1:6379> get name # 获取值
"qianduhello"
127.0.0.1:6379> STRLEN name # 获取字符串的长度
(integer) 11
#========================================================================
127.0.0.1:6379> incr i # 自增 1
(integer) 1
127.0.0.1:6379> decr i # 自减 1
(integer) 0
127.0.0.1:6379> incrby i 2 # 设置步长 指定增量
(integer) 2
127.0.0.1:6379> decrby i 3 # 设置步长 指定减量
(integer) -1
#========================================================================
127.0.0.1:6379> get name
"qianduhello"
127.0.0.1:6379> getrange name 2 4 # 取指定范围的字符串 [2,4]
"and"
127.0.0.1:6379> getrange name 0 -1 # 获取全部字符串
"qianduhello"
127.0.0.1:6379> setrange name 0 w #替换指定位置的字符串
(integer) 11
#========================================================================
# setex # 设置过期时间
127.0.0.1:6379> setex key1 10 "qinadu"
OK
# setnx # 不存在再设置 (在分布式锁中会使用)
127.0.0.1:6379> setnx key2 "study redis"
(integer) 1
#========================================================================
# mset # 批量设置
127.0.0.1:6379> mset k1 v1 k2 v2 # 设置 k1=v1 k2=v2
OK
127.0.0.1:6379> keys *
1) "k1"
2) "k2"
# mget # 批量获取
127.0.0.1:6379> mget k1 k2 # 获取key为k1、k2的值
1) "v1"
2) "v2"
127.0.0.1:6379> msetnx k1 v1 k4 v4 # msetnx 原子性操作 一起成功或者失败
(integer) 0
127.0.0.1:6379> keys *
1) "k1"
2) "k2"
#========================================================================
# 设置对象
127.0.0.1:6379> set user:1 {name:qiandu,age:12}
# 方法2
127.0.0.1:6379> mset user:2:name qiandu user:2:age 3
#========================================================================
# getset #先获取值,在设置
127.0.0.1:6379> getset name "qiandu666" #如果不存在值 返回nil
(nil)
127.0.0.1:6379> get name
"qiandu666"
127.0.0.1:6379> getset name "qiandu" # 如果存在 获取原来的值并且设置新的值
"qiandu666"
127.0.0.1:6379> get name
"qiandu"
String的使用场景:计数器(文章的统计数量等)、对象的缓存存储
List
List是一种的基本的数据类型
# lpush
127.0.0.1:6379> lpush list one # 将值插入列表头部
(integer) 1
127.0.0.1:6379> lpush list wo
(integer) 2
127.0.0.1:6379> lpush list three
(integer) 3
127.0.0.1:6379> lrange list 0 -1 # 通过区间获取具体的值
1) "three"
2) "wo"
3) "one"
# rpush
127.0.0.1:6379> rpush list four # 将值插入列表尾部
#========================================================================
127.0.0.1:6379> lpop list # 移除头部
"three"
127.0.0.1:6379> rpop list # 移除尾部
"four"
#========================================================================
# lindex
127.0.0.1:6379> lindex list 1 # 获取list的第一个值
"one"
# llen
127.0.0.1:6379> llen list # 获取长度
(integer) 2
# lrem
127.0.0.1:6379> lrem list 1 two # 从list中移除一个值为two的操作
#========================================================================
# rpoplpush 移除列表的最后一个元素并移动到新的列表之中
127.0.0.1:6379> rpoplpush list myList
"one"
127.0.0.1:6379> lrange list 0 -1
(empty array)
127.0.0.1:6379> lrange myList 0 -1
1) "one"
127.0.0.1:6379>
#========================================================================
#lset 将列表中指定下标的值替换为另外一个 不存在列表会报错
127.0.0.1:6379> lset myList 0 qd
OK
127.0.0.1:6379> lrange myList 0 -1
1) "qd"
#========================================================================
# linsert 插入命令
127.0.0.1:6379> linsert myList after qd qd666
(integer) 2
127.0.0.1:6379> lrange myList 0 -1
1) "qd"
2) "qd666"
小结
- list实际上是一个链表
- 如果key不存在,会创建新的链表
- 如果key存在,新增内容
- 如果移除了所有值,就是空链表,也代表不存在
- 在两边插入操作效率更高
- 可以用list实现消息队列的功能
Set
set中的值是不能重复的
127.0.0.1:6379> sadd myset qiandu qiandu666 # sadd 添加元素
(integer) 2
127.0.0.1:6379> smembers myset # smembers 获取指定集合中的元素
1) "qiandu"
2) "qiandu666"
127.0.0.1:6379> sismember myset qiandu # 判断集合中是否含有指定的值
(integer) 1 # 有返回1 无返回0
127.0.0.1:6379> sismember myset qiandua
(integer) 0
127.0.0.1:6379> scard myset # 判断集合中元素的个数
(integer) 2
127.0.0.1:6379> srem myset qiandu666 # 移除集合中的指定元素
(integer) 1
#========================================================================
127.0.0.1:6379> SRANDMEMBER myset 1 # 随机获取元素
1) "qiandu"
127.0.0.1:6379> spop myset # 随机删除元素
"qiandu"
#========================================================================
127.0.0.1:6379> smove myset2 myset qiandu123 # 将指定的元素移动到另一个集合之中
(integer) 1
127.0.0.1:6379> smembers myset
1) "qiandu123"
#========================================================================
127.0.0.1:6379> sdiff myset myset2 # 取myset中myset2没有的元素
(empty array)
127.0.0.1:6379> sinter myset myset2 # 取交集
1) "qiandu123"
127.0.0.1:6379> SUNION myset myset2 # 取并集
1) "qiandu123"
2) "qiandu888"
小结
set集合中存放的是无序的、不可重复的数据、实际应用场景:共同关注、好友推荐
Hash
hash,我们当作一个map集合对待就简单了 存放的是map集合 本质与String没有太大的区别
127.0.0.1:6379> hset myhash field qiandu
(integer) 1
127.0.0.1:6379> hget myhash field
"qiandu"
127.0.0.1:6379> hmset myhash field1 qiandu field2 hello # 设置多个值
OK
127.0.0.1:6379> hmget myhash field1 field2 # 获取多个值
1) "qiandu"
2) "hello"
127.0.0.1:6379> hgetall myhash # 获取所有的key与value
1) "field"
2) "qiandu"
127.0.0.1:6379> hdel myhash field # 删除字段
(integer) 1
127.0.0.1:6379> hlen myhash # 获取长度
(integer) 2
127.0.0.1:6379> hexists myhash field1 # 判断指定字段是否存在
(integer) 1
#========================================================================
127.0.0.1:6379> hkeys myhash # 只获取key
1) "field1"
2) "field2"
127.0.0.1:6379> hvals myhash # 只获取value
1) "field2"
2) "hello"
#========================================================================
hincrby myhash field3 5 # 指定增量
小结
可以用hash存放经常变动的数据、或者用户的信息
Zset
zset与set只差了一个z 但是zset可以排序
127.0.0.1:6379> zadd zset 1 one # 添加一个值
(integer) 1
127.0.0.1:6379> zadd zset 2 two 3 three # 添加多个值
(integer) 2
127.0.0.1:6379> zrange zset 0 -1 # 取得全部数据
1) "one"
2) "two"
3) "three"
#========================================================================
127.0.0.1:6379> zadd score 100 qiandu 200 xiaohong 300 xiaowang
(integer) 3
127.0.0.1:6379> ZRANGEBYSCORE score -inf +inf # 排序 正(负)无穷
1) "qiandu"
2) "xiaohong"
3) "xiaowang"
127.0.0.1:6379> zrevrange score 0 -1 # 排序 从大到小
#========================================================================
127.0.0.1:6379> zrem score xiaohong # 移除元素
(integer) 1
127.0.0.1:6379> zcard score # 获取集合的大小
(integer) 2
127.0.0.1:6379> zcount score 1 3 # 获取指定区间的成员数量
(integer) 0
小结
set可以实现的zset都可以实现还要排序功能 使用场景:权重、排行榜
特殊数据类型
geospatial
这个是提供地理位置的api 比如打车、附近的人、两地距离都可以用它来实现
geoadd 添加地理位置
# getadd 添加地理位置
# 规则:两级无法直接添加,我们一般会下载城市数据,直接通过java程序一次性导入!
# 参数:经度、纬度、名称
127.0.0.1:6379> geoadd china:city 116.40 39.90 beijing
(integer) 1
127.0.0.1:6379> geoadd china:city 121.47 31.23 shanghai
(integer) 1
geopos 获取城市的经度与纬度
127.0.0.1:6379> geopos china:city beijing
1) 1) "116.39999896287918091"
2) "39.90000009167092543"
GEODIST 返回两个给定位置之间的距离。
如果两个位置之间的其中一个不存在, 那么命令返回空值。指定单位的参数 unit 必须是以下单位的其中一个:
- m 表示单位为米。 默认
- km 表示单位为千米。
- mi 表示单位为英里。
- ft 表示单位为英尺。
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"
GEORADIUS :以给定的经纬度为中心,找出某一半径的所有位置元素。应用场景(附近的人) georadius 命令-----详情点我
127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km
# 110 30 模拟定位的经纬度 以当前位置查找半径在1000km以内的城市
GEORADIUSBYMEMBER 中心点是由给定的位置元素决定的
127.0.0.1:6379> GEORADIUSBYMEMBER china:city beijing 2000 km
1) "shanghai"
2) "beijing"
Hyperloglog
基数:两个集合中不重复的元素
应用:可以使用它用作网站统计的访问量,传统方式是使用set来统计它的元素作为判断 ,但是仅仅只因为计数还要保存用户的信息、浪费内存;如果可以忽略容错,可以使用它完成
127.0.0.1:6379> pfadd set a s f e g y # 添加元素
(integer) 1
127.0.0.1:6379> PFCOUNT set # 获取元素个数
(integer) 6
127.0.0.1:6379> PFMERGE set1 set set2 # 合并集合
OK
Bitmap
bitmaps(位图)也是一种数据结构,操作二进制位来进行记录,只有0与1两种状态
使用bitmap的场景有很多:例如 统计(活跃与不活跃)用户的信息、是否登录、打卡......只要是两个状态的,都可以使用bitmap解决
# 假设打卡场景 0:星期一 1:星期二 以此类推
127.0.0.1:6379> setbit sign 0 1 # 星期一成功打卡
(integer) 0
127.0.0.1:6379> setbit sign 1 0 # 星期二未打卡
(integer) 0
127.0.0.1:6379> getbit sign 0 # 检查某天的打卡清空
(integer) 1
127.0.0.1:6379> BITCOUNT sign # 统计打卡记录
(integer) 1
基本的事务操作
事务我们在MysQL这章节开始了解,事务有4个原则; 在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)> exec
1) OK
2) OK
取消事务
# 开启事务
127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> set k2 v2
QUEUED
# 取消事务
127.0.0.1:6379(TX)> DISCARD
OK
# 事务中的命令没有被执行
127.0.0.1:6379> get k2
(nil)
在事务中出现错误情况分为两种 类比java
- 编译时异常(命令有误):事务中的命令是不会被执行的
- 运行时异常:如果事务中出现语义性异常时,其他命令可以正常执行
乐观锁
Reids使用watch(监视)来实现乐观锁
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 10
QUEUED
127.0.0.1:6379(TX)> incrby out 10
QUEUED
127.0.0.1:6379(TX)> exec
1) (integer) 90
2) (integer) 10
如果事务执行失败 先使用unwatch
解锁 然后获取最新的值再次监视
Jedis
什么是Jedis ?
Redis官方推荐的java连接开发工具!使用lava操作Redis 中间件!如果你要使用java操作redis,那么一定要对Jedis十分的熟悉!
- 导入依赖
redis.clients
jedis
3.6.1
com.alibaba
fastjson
1.2.54
- 编码测试
- 连接数据库
- 操作命令
- 关闭连接
测试连接
public static void main(String[] args) {
//1. new jedis对象
Jedis jedis = new Jedis("127.0.0.1", 6379);
//jedis 所有指令就是上述学的指令
System.out.println(jedis.ping());
}
常用API
String