1. NoSQL数据库简介
Not Only SQL,非关系型数据库
NoSQL有什么用:
- 解决CPU和内存压力
- 解决IO压力
NoSQL数据库特点:
- 非关系型数据库,不依赖业务逻辑数据库存储,以key-value存储,因此大大增加了数据库的扩展能力
- 不遵守SQL标准
- 不支持ACID(原子性、隔离性、一致性、持久性)
- 远超于SQL的性能
适用于:
- 高并发读写
- 海量数据读写
- 数据可扩展
不适用于场景:
- 需要事务支持
- 需要基于SQL结构化查询存储
NoSQL优点:
- 缓存数据库,完全在内存中,速度快,数据结构简单
- 减少io操作,数据库和表拆分,虽然破坏业务逻辑,即外加一个缓存数据库,提高数据库速度,也可以用专门的存储方式,以及针对不同的数据结构存储
常见的NoSQL数据库
- Memcache:不支持持久化
- Redis:支持持久化
- MongoDB:文档数据库
2. Redis安装与配置
0. Redis概述
redis特性:
- Redis是一个开源的key-value存储系统
- 支持string,list,set,zset,hash类型
- 这些数据类型都支持push/pop,add/remove,取交集并集等,这些操作都是原子性的
- 支持不同方式的排序
- 数据都是缓存在内存中
- 实现了主从同步
1. 下载与安装
下载网址:Redis,只支持Linux版本,下载后在Ubuntu下解压,切入到对应文件夹下
首先需要gcc运行环境
sudo apt install gcc gcc --version //查看gcc版本
安装Redis:
make sudo make install
判断是否安装成功:
安装目录:/usr/local/bin
查看默认安装目录:
- redis-benchmark:性能测试工具
- redis-check-aof:修复有问题的 aof文件
- redis-check-dump:修复有问题的dump.rdb文件
- redis-sentinel:Redis集群使用
- redis-server:Redis服务器启动命令
- redis-cli:客户端,操作入口
2. 运行
a. 前台启动方式
(不推荐)
打开了就不问关闭该终端
cd /usr/local/bin redis-server
b. 后台启动方式
把终端断掉了后台还在运行redis
cd redis-6.2.6 //切入Redis的文件夹 cp redis.conf etc/redis.conf //拷贝一份redis.conf cd /etc/ gedit redis.conf //查找daemo 修改后面的no->yes redis-server /etc/redis.conf //后台启动redis服务器端
查看是否在运行:
退出方法:
exit
或关闭后台redis-cli shutdown
或关闭进程号 kill -9 进程号
总结:以后这样登录redis:/usr/local/bin/redis-cli
3. set插入数据报错解决方法
参考博客:
https://blog.csdn.net/Sophia_0331/article/details/107779165
https://blog.csdn.net/zdyueguanyun/article/details/83449912
解决方法:
config set stop-writes-on-bgsave-error no
3. 常用五大数据类型
0. Redis相关知识
- 登录redis:
/usr/local/bin/redis-cli
- Redis端口:6379
- Redis默认0号数据库
- Redis是单线程+多路IO复用
1. key
数据的操作:
- 插入数据:
set key value
- 查看当前库的所有key:
keys *
- 是否存在当前键:
exists key
,存在返回1,否则返回0
- 删除键值对:
del key
- 删除键值对:
unlink key
,选择非阻塞删除
- 查看键对应的值的类型:
type key
- 给键设置过期时间:
expire key time
,time以秒为单位
- 查看键多长时间过期:
ttl key
,-1表示永不过期,-2表示已过期
zdb@zdb-virtual-machine:~$ /usr/local/bin/redis-cli 127.0.0.1:6379> keys * (empty array) //插入数据 127.0.0.1:6379> set k1 lucy OK 127.0.0.1:6379> set k2 mary OK 127.0.0.1:6379> set k3 jack OK //查询数据 127.0.0.1:6379> keys * 1) "k3" 2) "k1" 3) "k2" 127.0.0.1:6379> exists k1 (integer) 1 127.0.0.1:6379> exists k4 (integer) 0 127.0.0.1:6379> type k1 string //删除数据 127.0.0.1:6379> del k3 (integer) 1 127.0.0.1:6379> unlink k2 (integer) 1 //设置定时器 127.0.0.1:6379> expire k1 10 (integer) 1 127.0.0.1:6379> ttl k1 (integer) 4 127.0.0.1:6379> ttl k1 (integer) -2
库的操作:
- 选择库:
select 库号
,默认库为0号
- 查看当前数据库的key数量:
dbsize
- 清空当前库:
flushdb
- 清空所有库:
flushall
127.0.0.1:6379> select 1 OK 127.0.0.1:6379[1]> select 0 OK 127.0.0.1:6379> dbsize (integer) 0 127.0.0.1:6379> keys * (empty array) 127.0.0.1:6379> flushdb OK
2. string
关于string的介绍:
- 一个key对应一个value
- 二进制安全的,string可包含任何数据,比如jpg图片转换成string存储
- value最多可以是512M
- 动态的字符串,会扩容
常用命令:
- 获取值:
get key
- 在值尾部追加元素:
append key add_value
- 获取值的长度:
strlen key
- 当key不存在,才存入key-value对:
setnx key value
- value增1:
incr key
- value减1:
decr key
- value增一个常数:
incrby key 步长
- value减一个常数:
decr key 步长
127.0.0.1:6379> set k1 v100 OK 127.0.0.1:6379> set k2 v200 OK 127.0.0.1:6379> keys * 1) "k1" 2) "k2" 127.0.0.1:6379> get k1 "v100" 127.0.0.1:6379> set k1 v1100 OK 127.0.0.1:6379> get k1 "v1100" 127.0.0.1:6379> append k1 bac (integer) 8 127.0.0.1:6379> get k1 "v1100bac" 127.0.0.1:6379> strlen k1 (integer) 8 127.0.0.1:6379> setnx k1 v300 (integer) 0 127.0.0.1:6379> setnx k3 v300 (integer) 1 127.0.0.1:6379> set k4 500 OK 127.0.0.1:6379> incr k4 (integer) 501 127.0.0.1:6379> get k4 "501" 127.0.0.1:6379> decr k4 (integer) 500 127.0.0.1:6379> incrby k4 10 (integer) 510 127.0.0.1:6379> decrby k4 20 (integer) 490
Redis的incr是原子性操作,java中不是原子性操作
其他命令:
- 设置多个键值对:
mset k1 v1 k2 v2...
- 获取多个value:
mget k1 k2 ...
msetnx k1 v1 k2 v2 k3 v3
:原子操作,要么全部成功,要么全部失败
getrange key start_index end_index
:get范围内的值,索引从0开始
setrange key startindex val
:将startindex位置上的值用val替代
setex key time value
:设置过期时间,同时设置值
getset key value
:返回旧值,同时设置新值
127.0.0.1:6379> flushdb OK 127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 OK 127.0.0.1:6379> keys * 1) "k3" 2) "k1" 3) "k2" 127.0.0.1:6379> mget k1 k2 k3 1) "v1" 2) "v2" 3) "v3" //msetnx要么全部成功,要么全部失败 127.0.0.1:6379> msetnx k11 v11 k12 v12 k1 v11 (integer) 0 127.0.0.1:6379> msetnx k11 v11 k12 v12 k13 v13 (integer) 1 127.0.0.1:6379> keys * 1) "k1" 2) "k3" 3) "k12" 4) "k13" 5) "k2" 6) "k11" 127.0.0.1:6379> set name lucymary OK 127.0.0.1:6379> getrange name 0 3 "lucy" 127.0.0.1:6379> setrange name 3 abc (integer) 8 127.0.0.1:6379> get name "lucabcry" //设置定时器 127.0.0.1:6379> setex age 10 value30 OK 127.0.0.1:6379> ttl age (integer) 4 127.0.0.1:6379> ttl age (integer) -2 //给key设置新值 127.0.0.1:6379> getset name jack "lucabcry" 127.0.0.1:6379> get name "jack"
string的数据结构是简单的动态字符串
3. list
单键多值
多个value存储为list,底层为双向链表
常用命令:
lpush/rpush key value value...
:从左或者右插入一个或者多个值(头插与尾插)
lpop/rpop key
:从左或者右吐出一个或者多个值(值在键在,值都没,键都没)
rpoplpush key1 key2
: 从key1列表右边吐出一个值,插入到key2的左边
lrange key start stop
按照索引下标获取元素(从左到右)
lrange key 0 -1
: 获取所有值
lindex key index
按照索引下标获得元素
llen key
获取列表长度
linsert key before/after value newvalue
在value的前面插入一个新值
lrem key n value
从左边删除n个value值
lset key index value
在列表key中的下标index中修改值value
127.0.0.1:6379> lpush k1 v1 v2 v3 (integer) 3 127.0.0.1:6379> lrange k1 0 -1 1) "v3" 2) "v2" 3) "v1" 127.0.0.1:6379> rpush k2 v1 v2 v3 (integer) 3 127.0.0.1:6379> lrange k2 0 -1 1) "v1" 2) "v2" 3) "v3" 127.0.0.1:6379> lpop k1 "v3"
127.0.0.1:6379> lpush k1 v1 v2 v3 (integer) 3 127.0.0.1:6379> rpush k2 v11 v12 v13 (integer) 3 127.0.0.1:6379> rpoplpush k1 k2 "v1" 127.0.0.1:6379> lrange k2 0 -1 1) "v1" 2) "v11" 3) "v12" 4) "v13" //索引取值 127.0.0.1:6379> lindex k2 (error) ERR wrong number of arguments for 'lindex' command 127.0.0.1:6379> lindex k2 0 "v1" 127.0.0.1:6379> lindex k2 2 "v12" //获取长度 127.0.0.1:6379> llen k2 (integer) 4 //指定位置插入值 127.0.0.1:6379> linsert k2 before "v11" "newv11" (integer) 5 127.0.0.1:6379> lrange k2 0 -1 1) "v1" 2) "newv11" 3) "v11" 4) "v12" 5) "v13" 127.0.0.1:6379> linsert k2 before "v12" "newv11" (integer) 6 127.0.0.1:6379> lrange k2 0 -1 1) "v1" 2) "newv11" 3) "v11" 4) "newv11" 5) "v12" 6) "v13" 127.0.0.1:6379> linsert k2 before "v13" "newv11" (integer) 7 127.0.0.1:6379> lrange k2 0 -1 1) "v1" 2) "newv11" 3) "v11" 4) "newv11" 5) "v12" 6) "newv11" 7) "v13" //删除指定值,且可以指定个数 127.0.0.1:6379> lrem k2 2 "newv11" (integer) 2 127.0.0.1:6379> lrange k2 0 -1 1) "v1" 2) "v11" 3) "v12" 4) "newv11" 5) "v13" //指定位置替换值 127.0.0.1:6379> lset k2 1 newnew OK 127.0.0.1:6379> lrange k2 0 -1 1) "v1" 2) "newnew" 3) "v12" 4) "newv11" 5) "v13"
4. set
底层是字典,通过哈希表实现
自动排重且为无序的
常用命令:
sadd key value value...
将一个或者多个member元素加入集合key中,已经存在的member元素被忽略
smembers key
取出该集合的所有值
sismember key value
判断该集合key是否含有改值
scard key
返回该集合的元素个数
srem key value value
删除集合中的某个元素
spop key
随机从集合中取出一个元素
srandmember key n
随即从该集合中取出n个值,不会从集合中删除
smove <一个集合a><一个集合b>value
将一个集合a的某个value移动到另一个集合b
sinter key1 key2
返回两个集合的交集元素
sunion key1 key2
返回两个集合的并集元素
sdiff key1 key2
返回两个集合的差集元素(key1有的,key2没有)
127.0.0.1:6379> sadd k1 v1 v2 v3 (integer) 3 127.0.0.1:6379> smembers k1 1) "v2" 2) "v3" 3) "v1" //判断set中是否有值 127.0.0.1:6379> sismember k1 v1 (integer) 1 127.0.0.1:6379> sismember k1 v4 (integer) 0 //返回集合中元素个数 127.0.0.1:6379> scard k1 (integer) 3 //删除元素 127.0.0.1:6379> srem k1 v1 v2 (integer) 2 127.0.0.1:6379> smembers k1 1) "v3"
127.0.0.1:6379> sadd k2 v1 v2 v3 v4 (integer) 4 //随机删除元素 127.0.0.1:6379> spop k2 "v1" 127.0.0.1:6379> spop k2 "v4" 127.0.0.1:6379> spop k2 "v3"
127.0.0.1:6379> flushdb OK 127.0.0.1:6379> sadd k2 v1 v2 v3 v4 (integer) 4 127.0.0.1:6379> srandmember k2 2 //随机取值 1) "v2" 2) "v3" 127.0.0.1:6379> srandmember k2 2 1) "v1" 2) "v3"
127.0.0.1:6379> sadd k1 v1 v2 v3 (integer) 3 127.0.0.1:6379> sadd k2 v3 v4 v5 (integer) 3 //将k1中的v3移入到k2中 127.0.0.1:6379> smove k1 k2 v3 (integer) 1 127.0.0.1:6379> smembers k1 1) "v2" 2) "v1" 127.0.0.1:6379> smembers k2 1) "v5" 2) "v3" 3) "v4" 127.0.0.1:6379> sadd k3 v3 v4 v5 v7 (integer) 4 127.0.0.1:6379> sinter k2 k3 //交集 1) "v5" 2) "v3" 3) "v4" 127.0.0.1:6379> sunion k2 k3 //并集 1) "v7" 2) "v3" 3) "v4" 4) "v5" 127.0.0.1:6379> sdiff k3 k2 //差集 1) "v7"
5. hash
hash是键值对集合,是一个string类型的field和value的映射表,hash特别适合用于存储对象。
常用命令:
hset key field value
: 给key集合中的filed键赋值value
hget key1 field
:集合field取出value
hmset key1 field1 value1 field2 value2
:批量设置hash的值
hexists key1 field
: 查看哈希表key中,给定域field是否存在
hkeys key
:列出该hash集合的所有field
hvals key
:列出该hash集合的所有value
hincrby key field increment
: 为哈希表key中的域field的值加上增量1 -1
hsetnx key field value
:将哈希表key中的域field的值设置为value,当且仅当域field不存在
127.0.0.1:6379> hset user:1001 id 1 //插入数据:键 域 值 (integer) 1 127.0.0.1:6379> hset user:1001 name zhangsan (integer) 1 127.0.0.1:6379> hget user:1001 id "1" 127.0.0.1:6379> hget user:1001 name "zhangsan" //一次性插入 127.0.0.1:6379> hmset user:1002 id 2 name lisi age 30 OK //判断是否存在域 127.0.0.1:6379> hexists user:1002 id (integer) 1 127.0.0.1:6379> hexists user:1002 gender (integer) 0 //获取所有域 127.0.0.1:6379> hkeys user:1002 1) "id" 2) "name" 3) "age" //获取所有值 127.0.0.1:6379> hvals user:1002 1) "2" 2) "lisi" 3) "30" 127.0.0.1:6379> hincrby user:1002 age 2 (integer) 32 127.0.0.1:6379> hsetnx user:1002 age 40 (integer) 0 127.0.0.1:6379> hsetnx user:1002 gender 1 (integer) 1
hash类型对应的数据结构是两种:ziplist(压缩列表),hashtable(哈希表)。当field-value长度较短且个数较少时,使用ziplist,否则使用hashtable。
6. zset
有序集合zset与普通集合set非常相似,是一个没有重复元素的字符串集合
常用命令:
zadd key score1 value1 score2 value2
:将一个或多个member元素及其score值加入到有序key中
zrange key start stop (withscores)
:返回有序集key,下标在start与stop之间的元素,带withscores,可以让分数一起和值返回到结果集。
zrangebyscore key min max(withscores)
: 返回有序集key,所有score值介于min和max之间(包括等于min或max)的成员。有序集成员按score的值递增次序排列
zrevrangebyscore key max min (withscores)
:同上,改为从大到小排列
zincrby key increment value
:为元素的score加上增量
zrem key value
:删除该集合下,指定值的元素
zcount key min max
:统计该集合,分数区间内的元素个数
zrank key value
:返回该值在集合中的排名,从0开始
127.0.0.1:6379> zadd topn 200 java 300 c++ 400 mysql 500 php (integer) 4 127.0.0.1:6379> zrange topn 0 -1 1) "java" 2) "c++" 3) "mysql" 4) "php" 127.0.0.1:6379> zrange topn 0 -1 withscores 1) "java" 2) "200" 3) "c++" 4) "300" 5) "mysql" 6) "400" 7) "php" 8) "500" 127.0.0.1:6379> zrangebyscore topn 300 500 1) "c++" 2) "mysql" 3) "php" 127.0.0.1:6379> zrangebyscore topn 300 500 withscores 1) "c++" 2) "300" 3) "mysql" 4) "400" 5) "php" 6) "500" 127.0.0.1:6379> zrevrangebyscore topn 500 300 1) "php" 2) "mysql" 3) "c++" 127.0.0.1:6379> zincrby topn 50 java "250" 127.0.0.1:6379> zcount topn 200 300 (integer) 2 127.0.0.1:6379> zrank topn java (integer) 0 127.0.0.1:6379> zrank topn mysql (integer) 2 127.0.0.1:6379> zrange topn 0 -1 withscores 1) "java" 2) "250" 3) "c++" 4) "300" 5) "mysql" 6) "400" 7) "php" 8) "500"
zset底层使用了两个数据结构:
- hash,hash的作用就是关联元素value和权重score,保障元素value的唯一性,可以通过元素value找到相应的score值。
- 跳跃表,跳跃表的目的在于给元素value排序,根据score的范围获取元素列表。
4. 配置文件
先打开配置文件:sudo gedit /etc/redis.conf
//1. 注释掉这一行 #bind 127.0.0.1 -::1 //2. 保护模式改成no protected-mode no //3. 后台启动改成yes daemonize yes
添加密码:
5. 发布和订阅
什么是发布订阅:
发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。