1 NoSQl数据库
1.1 技术的发展
技术的分类:
①解决功能性问题:javase
②解决扩展性问题:框架
③解决性能问题:redis
1.2 NoSQL数据库概述
NoSQL(Not Only SQL),不仅仅是SQL ,泛指非关系型数据库。不依赖业务逻辑存储,而是以简单的key-value
键值对存储,大大增加了数据库的扩展能力。
-
不遵循SQL标注
-
不支持ACID(原子性、隔离性、持久性、一致性)(支持事务但是不支持ACID)
-
远超SQL的性能
1.3 NoSQL适用场景
-
对数据高并发地读写
-
海量数据的读写
-
对数据要求高可扩展性
1.4 NoSQL不适用的场景
-
需要事务支持的
-
基于sql的
结构化查询
,处理复杂的关系,即需要即席
查询
1.5 几种常见的NoSQL数据库
① Memcache
② Redis 支持持久化
③ MongoDB 文档型数据库
1.6 常见的关系型数据库
① 行式数据库
② 列式数据库
2 Redis
2.1 概述
-
redis是一个开源的
key-value
存储系统。 -
和Memcached类似,它支持存储的value类型相对更多,包括
string(字符串)
、list(链表)
、set(集合)
、zset(sorted-set有序集合)
和hash(哈希类型)
。 -
这些数据类型都支持
push\pop
、add\remove
以及取交集、并集和差集及更丰富的操作,而且这些操作都是原子性的。 -
在此基础上,Redis支持
各种不同方式的排序
。 -
与memcached一样,为了保证效率,数据都是
缓存在内存
中的。 -
区别是Redis会
周期性
地把更新的数据写入磁盘
或者把修改操作写入追加的记录文件。
2.2 安装
正常安装需要下载压缩包解压到虚拟机安装,还需要c的编译器运行环境,我这里直接用之前docker里面安装的redis了,解压安装详细可以参考视频:https://www.bilibili.com/video/BV1Rv41177Af?p=4&spm_id_from=pageDriver&vd_source=7f52e70d44280156501483f0968c6c3e
docker安装redis可以参考我之前的博客:【谷粒商城】(一)docker搭建以及项目的创建 - Tod4 - 博客园
2.3 redis的启动
① 前台启动
执行命令redis-server,前台命令执行后窗口不能关闭,否则服务器会停止,类似于Nacos服务器。
② 后台启动
-
备份redis.conf,拷贝一份配置文件到其他目录
-
在配置文件中设置daemonize no 为 yes
-
执行redis-server + 拷贝的配置文件
后台启动即是命令窗口关闭也不会停止redis服务器
③ docker启动redis
首先安装redis镜像
docker pull redis
创建实例并运行
mkdir -p /mydata/redis/conf
touch /mydata/redis/conf/redis.conf
docker run -p 6379:6379 --name redis -v /mydata/redis/data:/data \
-v /mydata/redis/conf/redis.conf:/etc/redis/redis.conf \
-d redis redis-server /etc/redis/redis.conf
-v /mydata/redis/data:/data 表示将redis数据文件挂载到/mydata/redis/data
-v /mydata/redis/conf/redis.conf:/etc/redis/redis.conf 表示将配置文件挂载到/mydata/redis/conf/redis.conf
-d redis redis-server /etc/redis/redis.conf 表示在启动的时候配置/etc/redis/redis.conf
最新版本的redis配置文件挂载到了/usr/local/etc/redis,所以应该修改为:
mkdir -p /mydata/redis/conf
touch /mydata/redis/conf/redis.conf
docker run -p 6379:6379 --name redis -v /mydata/redis/data:/data \
-v /mydata/redis/conf/redis.conf:/usr/local/etc/redis/redis.conf \
-d redis redis-server /usr/local/etc/redis/redis.conf
这里先创建配置文件的原因是,容器中目录/usr/local/etc/redis/下是没有conf文件的,如果linux也不创建的话redis.cong就会被当做目录(linux是没有文件后缀名的,而且目录也被视作文件
docker设置redis自动启动
docker update redis --restart=always
④ 通过redis-cli客户端访问redis数据库
直接执行redis-cli命令即可,docker则执行:
docker exec -it redis redis-cli
2.4 redis相关知识介绍
端口号6379
来源:总结为程序员追星夹带私货(
数据库:redis默认16个数据库,类似数组下标从0开始,初始默认使用0号库,使用select+下标 访问。
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]>
单线程+多路IO复用技术
:相较于memcached的多线程加锁
的方式以及传统的串行方式,redis采用单线程+多路IO复用技术
的技术,即使用一个线程来检查多个文件描述符(Socket)
的就绪状态,比如调用select
和poll
函数,传入多个文件描述符,如果有一个文件描述符就绪则返回,否则阻塞直到超时。得到就绪状态后真正的操作可以在同一个线程里执行,也可以启动线程执行。
2.5 key键操作
keys *:查看当前库的所有key
exists key:判断某个key是否存在
type key:查看key是什么类型
del key:删除指定的key数据
unlink key:根据value选择非阻塞删除
仅将keys从keyspace中删除,真正地删除是在后续的异步操作
expire key 10: 设置key10s后过期
ttl key: 查看多少秒过期,-1表示永不过期,-2表示已经过期
select:命令切换数据库
dbsize:查看当前数据库key的数量
flushdb:清空当前库
flushall:通杀所有库
2.6 redis常用数据类型及其操作
String
String 是Redis最基本的数据类型,一个key对应一个value,value的最大值为512M
String类型是二进制安全的,意味着redis可以包含任何数据,如图片、视频(可以转换为二进制编码)和序列化对象
set 添加键值对
127.0.0.1:6379> set v1 100
OK
127.0.0.1:6379> set v2 100
OK
对相同的key添加会覆盖掉原来的
get 根据键获取值
127.0.0.1:6379> set v1 1100
OK
127.0.0.1:6379> get v1
"1100"
append 在key对应的value后面追加value
127.0.0.1:6379> append v1 abc
(integer) 7
127.0.0.1:6379> get v1
"1100abc"
添加后会返回添加完成的长度
strlen 获取key对应value的字符串长度
127.0.0.1:6379> strlen v1
(integer) 7
setnx 不存在key的时候才能设置键值对
127.0.0.1:6379> setnx v1 acx
(integer) 0
127.0.0.1:6379> setnx v3 100
(integer) 1
incr、decr 将key对应的数字值加、减一
127.0.0.1:6379> incr k4 10
(error) ERR wrong number of arguments for 'incr' command
127.0.0.1:6379> incr k4
(integer) 101
127.0.0.1:6379> decr k4
incrby、decrby 将key对应的数字值加、减步长
127.0.0.1:6379> incrby k4 100
(integer) 200
127.0.0.1:6379> decrby k4 200
(integer) 0
mset、mget 设置获取多个
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> mget k1 k2
1) "v1"
2) "v2"
msetnx 设置多个且key不存在
127.0.0.1:6379> msetnx k4 v4 k5 v5 k1 v2
(integer) 0
由于原子性,有一个设置失败则全部会设置失败
getrange 获取指定位置的字符串 包前包后
127.0.0.1:6379> set name lucymary
OK
127.0.0.1:6379> getrange name 03
(error) ERR wrong number of arguments for 'getrange' command
127.0.0.1:6379> getrange name 0 3
"lucy"
setrange 设置指定位置的字符串 包前包后
127.0.0.1:6379> setrange name 3 abc
(integer) 8
127.0.0.1:6379> get name
"lucabcry"
setex
127.0.0.1:6379> setex k20 10 val20
OK
127.0.0.1:6379> ttl k20
(integer) 6
127.0.0.1:6379> ttl k20
(integer) 5
127.0.0.1:6379> ttl k20
(integer) 4
127.0.0.1:6379> keys *
1) "k1"
2) "k2"
3) "k3"
4) "name"
原子操作
指的是不会被线程机制(如cpu时间片轮换)打断的操作,这种操作一旦开始就会执行到结束,不会切换到其他线程。
-
在单线程中,能在一条指令内完成的操作都被认为是原子操作
-
在多线程中,不被其他线程打断的操作都被认为是原子操作
Redis的原子性得益于redis的单线程
java的i++是原子操作吗
不是。比如两个线程分别进行100次i++,得到的结果是2~200
i++操作可以被看做三部分:
① 获取i的取值
② 执行加一
③ 将结果赋值给i
这三步操作是随时能出现切换线程的情况的
数据结构
String的数据结构为简单动态字符串(Simple Dynamic String, SDS)是可以修改的字符串,内部结构实现上类似于Java的ArratList,采取预先分配冗余空间的方式来减少内存的频繁分配。
当字符串小于1M的时候,扩容都是加倍现有的空间,如果超过1M,扩容只会增加1M的空间,需要注意字符串的最大长度为512M。
3 Redis列表
3.1 简介
单键多值,redis列表是简单的字符串列表,按照插入顺序排序,可以选择添加一个元素到列表的表头或者表尾。
底层其实是一个双向链表,对两边的操作性能很高,但是按照索引下标操作中间元素的性能较差。
3.2 常用命令
lpush、rpush 从表尾表头添加字符串
127.0.0.1:6379> lpush k1 v1 v2 v3
(integer) 3
127.0.0.1:6379> keys *
1) "k1"
127.0.0.1:6379> get k1
(error) WRONGTYPE Operation against a key holding the wrong kind of value
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"
lpush类似于链表的头插法,插入结果为倒序
rpush类似于尾插法,为正序
lrange 根据下标获取列表key
如上,0 -1表示从左边第一个元素到右边最后一个元素
lpop rpop 从左边、右边取出一个值
值在键在,值光键亡
127.0.0.1:6379> rpop k1
"v1"
127.0.0.1:6379> rpop k1
"v2"
127.0.0.1:6379> rpop k1
"v3"
127.0.0.1:6379> rpop k1
(nil)
127.0.0.1:6379> keys *
1) "k2"
count表示取出的个数,可以不写
rpoplpush 从列表key1右边取出一个值key的左边
127.0.0.1:6379> rpoplpush k1 k2
"v3"
127.0.0.1:6379>
127.0.0.1:6379>
127.0.0.1:6379> lrange k1 0 -1
1) "v1"
2) "v2"
127.0.0.1:6379> lrange k2 0 -1
1) "v3"
2) "v1"
3) "v2"
4) "v3"
注意没有其他的命令
127.0.0.1:6379> lpoprpush k1 k2
(error) ERR unknown command `lpoprpush`, with args beginning with: `k1`, `k2`,
lindex 按照下标获取key对应的value
127.0.0.1:6379> lindex k1 1
"v2"
llen
127.0.0.1:6379> llen k1
(integer) 2
linsert before/after 在key列表中的value前、后插入
127.0.0.1:6379> lrange k1 0 -1
1) "v1"
2) "v2"
127.0.0.1:6379> linsert k1 before v2 v
(integer) 3
127.0.0.1:6379> lrange k1 0 -1
1) "v1"
2) "v"
3) "v2"
127.0.0.1:6379> linsert k1 after v2 v
(integer) 4
127.0.0.1:6379> lrange k1 0 -1
1) "v1"
2) "v"
3) "v2"
4) "v"
127.0.0.1:6379> linsert k1 after v vv
(integer) 5
127.0.0.1:6379> lrange k1 0 -1
1) "v1"
2) "v"
3) "vv"
4) "v2"
5) "v"
可以看到上面有多个对应的value只会在第一个匹配的value前后新增
lrem 从key列表的左边开始删除count个value
127.0.0.1:6379> lrange k1 0 -1
1) "v1"
2) "v"
3) "vv"
4) "v2"
5) "v"
127.0.0.1:6379> lrem k1 3 v
(integer) 2
127.0.0.1:6379> lrange k1 0 -1
1) "v1"
2) "vv"
3) "v2"
可以看到上面删除的个数大于已经有的会删除全部的
lset 将列表key下标为index的值替换为value
127.0.0.1:6379> lrange k1 0 -1
1) "v1"
2) "vv"
3) "v2"
127.0.0.1:6379> lset k1 0 v
OK
127.0.0.1:6379> lrange k1 0 -1
1) "v"
2) "vv"
3) "v2"
4 Redis集合
Redis set对外提供的与list类似是一个列表的功能,特殊之处在于list可以自动排重。并且set提供了一个能够判断成员是否存在的重要接口,这也是列表不能提供的。
redis的set是String类型的无序集合,底层其实是一个value为null的hash表,所以添加删除修改的复杂度都是O(1)
4.1 常用命令
sadd ... 添加
127.0.0.1:6379> sadd k1 v1 v2 v2 v3
(integer) 3
127.0.0.1:6379>
smembers
127.0.0.1:6379> smember k1
(error) ERR unknown command `smember`, with args beginning with: `k1`,
127.0.0.1:6379> smembers k1
1) "v3"
2) "v2"
3) "v1"
注意s
sismember 判断一个值是否为集合中的值
127.0.0.1:6379> sismember k1 v1
(integer) 1
127.0.0.1:6379> sismember k1 v4
(integer) 0
scard 返回集合中的值
127.0.0.1:6379> scard k1
(integer) 3
srem ... 删除集合中的值
127.0.0.1:6379> srem k1 v1
(integer) 1
127.0.0.1:6379> smembers
(error) ERR wrong number of arguments for 'smembers' command
127.0.0.1:6379> smembers k1
1) "v3"
2) "v2"
scard 随机突出一些值
127.0.0.1:6379> spop k1
"v2"
127.0.0.1:6379> scard
(error) ERR wrong number of arguments for 'scard' command
127.0.0.1:6379> scard k1
(integer) 1
count不写默认为1
全部吐出之后则集合销毁
srandmember 随机从集合中取出count个值,集合个数不变
127.0.0.1:6379> srandmember k1
"v3"
count不写默认为1
smove
127.0.0.1:6379> sadd k1 v1 v2 v3
(integer) 3
127.0.0.1:6379> sadd k2 v4 v2 v3
(integer) 3
127.0.0.1:6379> smove k1 k2 v1
(integer) 1
127.0.0.1:6379> smembers k1
1) "v3"
2) "v2"
127.0.0.1:6379> smembers k2
1) "v3"
2) "v4"
3) "v2"
4) "v1"
从一个集合移动一个值到另一个集合
sinter 返回两集合交集
sunion 返回两集合并集
sdiff 返回两集合差集
127.0.0.1:6379> sinter k1 k2
1) "v3"
2) "v2"
127.0.0.1:6379> sunion k1 k2
1) "v3"
2) "v4"
3) "v2"
4) "v1"
127.0.0.1:6379> sdiff k1 k2
(empty array)
数据结构
Set数据结构是dict字典,字典使用哈希表实现的,java中的HashSet内部也是使用的HashMap,只不过所有的value都指向同一个对象,Redis的set结构也是一样,所有的value指向同一个内部值。
5 Redis 哈希 Hash
5.1简介
Redis是一个键值对集合,RedisHash是一个String类型的 field 和 value 的映射表,hash特别适合于存储对象,类似于java里面的 Map<String, Object>
5.2 为什么要采用这种存储结构?
如上图,存储对象的数据的时候,通常三种存储方式: 第一种是json格式的字符串,但是这种需要修改数据的时候及其困难:需要转换为对象然后修改再转换为字符串进行存储。第二种是类似于yml格式的存储,这种存储方式的缺点是数据存储较为分散,当存储的对象和对象的属性特别多的时候,数据会非常凌乱。
而采用key-field-value的格式,修改和存储都比较简单。
5.3 常用命令
hset 设置对象字段值
hget 获取对象的字段值
127.0.0.1:6379> hset user:101 id 1
(integer) 1
127.0.0.1:6379> hset user:101 name zhangsan
(integer) 1
127.0.0.1:6379> hget user:101 id
"1"
127.0.0.1:6379> hget user:101 name
"zhangsan"
hmset 设置多个字段值
127.0.0.1:6379> hmset user:102 id 2 name lisi
OK
hexists 判断对象是否存在字段field
127.0.0.1:6379> hexists user:102 id
(integer) 1
127.0.0.1:6379> hexists user:102 gender
(integer) 0
hkeys 获取key的所有字段
127.0.0.1:6379> hkeys user:101
1) "id"
2) "name"
hvals 获取key的所有字段值
127.0.0.1:6379> hvals user:101
1) "1"
2) "zhangsan"
hincrby
127.0.0.1:6379> hincrby user:101 id 2
(integer) 3
127.0.0.1:6379> hvals user:101
1) "3"
2) "zhangsan"
hsetnx 当不存在字段才能设置字段值
127.0.0.1:6379> hsetnx user:101 id 2
(integer) 0
127.0.0.1:6379> hsetnx user:101 gender nan
(integer) 1
127.0.0.1:6379> hkeys user:101
1) "id"
2) "name"
3) "gender"
5.4 数据结构
Hash类型对应的数据结构有两种,一种是ziplist(压缩列表),另一种是hashtable(哈希表)。当filed-value长度较短且个数较少的时候,使用ziplist否则使用hsahtable。
6 有序集合 Zset
6.1 简介
Zset与普通集合Set类似,是一个没有重复元素的字符串集合。不同之处在于Zset有序集合的每一个成员都关联了一个评分(score),这个评分被用于从低到高的形式排序有序集合中的成员。集合中的成员是惟一的,但是集合中的评分是可以重复的。
6.2 常用命令
zadd
127.0.0.1:6379> zadd topn 100 java 200 c++ 300 python
(integer) 3
zrange [withscores] 返回有序集合key中下标在 start-end 范围内的成员
127.0.0.1:6379> zrange topn 0 -1 withscores
1) "java"
2) "100"
3) "c++"
4) "200"
5) "python"
6) "300"
zrangebyscore [withscores] [limit offset count] 取出有序集合中评分在200-300范围之内的成员
127.0.0.1:6379> zrangebyscore topn 200 300 withscores
1) "c++"
2) "200"
3) "python"
4) "300"
zrevrangebyscore [withscores] 倒序输出
127.0.0.1:6379> zrevrange topn 0 -1 withscores
1) "python"
2) "300"
3) "c++"
4) "200"
5) "java"
6) "100"
zincrby 对有序集合中key的value增加increament
127.0.0.1:6379> zincrby topn 50 java
"150"
127.0.0.1:6379> zrange topn 0 -1 withscores
1) "java"
2) "150"
3) "c++"
4) "200"
5) "python"
6) "300"
zrem 删除有序集合key中的value
zcount 统计分数在min-max范围之内的成员
127.0.0.1:6379> zcount topn 100 200
(integer) 2
zrank 返回成员value在有序集合key中的排名 从0开始
127.0.0.1:6379> zrank topn java
(integer) 0
6.3 底层数据结构
zset底层使用了两种数据结构
① hash:用于关联元素value和权重score,保障元素value的唯一性,通过value能够查找到对应的score。
② 跳表:跳表的目的在于给元素value排序,根据score的范围获取列表。
6.4 跳表
跳表由表头(维护各个层的首个节点指针),跳表节点(保存元素值和多个层),层(保存指向该层的其他节点的指针,高层指针的跨度要大于底层指针的跨度),表尾(全部由空指针组成)。
跳表查找的时候,会从高层先进行访问,然后随着元素范围的缩小慢慢降低层次,时间复杂度为O(log n)。
标签:127.0,0.1,数据类型,redis,Redis,6379,k1,五种,integer From: https://www.cnblogs.com/tod4/p/16969103.html