两种数据库类型

关系型数据库与非关系型数据库的区别

传统的RDBMS

  • 结构化组织(表、列)
  • 结构化的SQL语句
  • 数据和关系都存在单独的表中
  • 严格的一致性
  • 基础的事务
  • ....

NoSQL

  • 不仅仅是数据
  • 没有固定的查询语句
  • 键值对存储、列存储、文档存储、图形数据库(设计关系)
  • 最终一致性
  • CAP定理和BASE
  • 高性能、高可用、高可扩展
  • ....

NoSQL的四大分类:KV键值对型(Redis)、文档型数据库(MongoDB)、列存储数据库(HBase)、图关系数据库(Neo4j、InfoGrid)

什么是Redis

Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

什么是Redis?Redis能干嘛?

Redis是一个内存中的数据结构存储系统,它是一个基于内存采用单线程模型的KV键值对数据库,由C语言编写。
1、内存存储、持久化
2、效率高,可用于高速缓存
3、发布订阅系统,做一些简单的消息队列的事情
4、地图信息分析
5、计时器、计数器(浏览量)
6、...

特性
1、多样的数据类型
2、持久化
3、集群
4、事务
5、...

安装步骤

注意:Redis不建议搭建在windows上,官方不支持Windows版本的Redis

  1. 将redis的安装包上传到linux后使用tar -zxvf指令解压
  2. 然后使用:yum install gcc-c++指令配置好基本的环境
  3. 在解压出来的目录下使用make命令,将所有需要的文件配置好,最后使用make install。一般我们安装的应用默认在usr目录的local的bin目录下
  4. 我们最好将redis的解压目录下的redis.conf文件复制到redis的安装目录下,用以备份。
  5. redis默认不是后台启动的,需要修改redis.conf配置文件的属性daemonize为yes
  6. 最后在安装目录下指定配置文件来启动服务:redis-server [配置文件所在位置]
  7. 通过启动客户端来测试是否启动成功:redis-cli -p [端口号],-h是主机地址,默认是本机可以不用写。

查看redis的进程是否开启:ps -ef|grep redis
关闭Redis服务:在连接状态输入 shutdown即可

在redis的安装目录下,存在redis-benchmark,它是redis自带的一个压力测试工具,用于测试性能
使用:

redis-benchmark 命令参数

常用参数:

  • -h 主机名
  • -p 端口
  • -c 指定并发连接数
  • -n 指定每个连接的请求数

示例,测试10000000个请求

redis-benchmark -h localhost -p 6379 -c 100 -n 100000

Redis的常用命令

redis默认有16个数据库,编号从0开始,默认使用的是第0个

切换数据库

select 数据库编号

查看数据库大小

dbsize

查看是否存在指定的key

exists [key]

移动当前数据库指定的key到指定的数据库

move [key] [数据库编号]

设置指定key的过期时间

expire [key] [时间,以秒为单位]

查看指定key的剩余时间

ttl [key]    #如果显示-2表示已过期

查看key的类型:

type [key]

查看当前数据库所有的key

keys *

清空当前数据库

flushdb

清空所有数据库:

flushall

Redis的五大数据类型

String:字符串类型

常用指令:

  • 向指定key的value中追加字符串:append [key] [追加的内容]
  • 查看指定key的value的长度:strlen [key]
  • 自增value的值(相当于i++)该value的值必须为数值类型的字符串:incr [key]
  • 设置步长,指定增量:incrby [key] [增量]
  • 自减value的值(相当于i--)该value的值必须为数值类型的字符串:decr [key]
  • 设置步长,指定减量:decrby [key] [减量]
  • 查看value中部分的字符串(类似于截取):getrange [key] [startIndex] [end] 下标索引从0开始,end为-1表示从开始索引起截取到字符串最后,注意:与java的左闭右开不同,它是全闭区间
  • 替换value中的字符串(类似于replace):setrange [key] [startIndex] [内容]
  • 创建数据时并设置过期时间:setex [key] [过期时间,s] [value]
  • 不存在时设置:setnx [key] [value] 当该key不存在时,创建该数据
  • 一次创建多个kv数据:mset [key1] [value1] [key2] [value2] ...
  • 一次获取多个key对应的value值:mget [key1] [key2] [key3] ...
  • msetnx [key] [value] [key2] [value2] ..:这个可以同时判断多个key不存在时创建数据,但它是原子性的,里面的数据必须同时不存在才能设置成功。
  • 先获取后设置:getset [key] [value] 先获取key的value值,获取完后在设置为指定的value值

使用String存储对象:以user为例
方式一:它的属性以json字符串的形式存储

set user:1 {name:zhangsan,age:21} 

方式二:这里的key是一个巧妙的设计

mset user:1:name zhangsan user:1:age 21
mget user:1:name user:1:age

List:列表

在redis中,我们可以把list玩成栈、队列、阻塞队列
所有的list命令都是以 l 或 r 开头的

常用命令

  • lpush [key] [value] 创建一个列表,并往列表左边(头部)插入数据,其中key为列表名,value为列表中的值
  • lrange [key] [startIndex] [endIndex] 获取列表中的值,从指定开始索引到指定结束索引
  • rpush [key] [value] 往列表中的右边(尾部)插入数据
  • lpop [key] 移除列表左边(头部)的第一个元素
  • rpop [key] 移除列表右边(尾部)的第一个元素
  • lindex [key] [value] 从左边开始,获取列表的指定索引的元素
  • llen [key] 获取指定列表的长度
  • lrem [key] [count] [value] 移除列表中指定数量的value,精确匹配
  • ltrim [key] [start] [stop] 截取列表中从start索引开始到stop索引中的元素
  • rpoplpush [key1] [key2] 移除key1列表中右边最后一个元素,并把它添加到到key2的最左边
  • lset [key] [index] [value] 替换列表的指定索引(左边为0)的元素,注意,该列表的指定索引下必须存在值
  • linsert [key] [before|after] [pivot] [value] 往列表中指定元素的前面或后面插入一个新的元素

总结:

  1. list实际上是一个链表
  2. 如果key不存在,就会创建新的链表
  3. 如果key存在,就会新增元素
  4. 如果移除了key中所有的值,空链表,也代表不存在
  5. 在列表的两边操作元素,效率最高,操作中间元素,效率偏低

Set:集合

集合中的元素无序,不可重复
set命令都是以 s 开头的

  • 添加集合元素,如果该集合不存在,则会创建该集合:sadd [key] [value]
  • 查看集合中的元素:smembers [key]
  • 查看集合中是否包含指定元素(类似于java的contain):sismember [key] [value]
  • 查看集合中包含多少个元素:scard [key]
  • 移除集合中的某个元素:srem [key] [value]
  • 随机筛选出集合中指定个数的元素:srandmember [key] [count]
  • 随机删除集合中指定个数的元素:spop [key] [count]
  • 将key1集合中某个指定的元素移动到key2集合:smove [key1] [key2] [value]
  • 查看key1集合有,key2集合没有的元素:sdiff [key1] [key2]
  • 查看多个集合的交集:sinter [key1] [key2] ...
  • 查看多个集合的并集:sunion [key1] [key2] ...

Hash:哈希

它的结构相当于key-Map,它的value相当于一个Map集合。它适合用来存储对象
hash命令都是以 h 开头的

  • 添加键值对元素元素,其中key为哈希的key,field和value对应的是map的键和值:hset [key] [field] [value]
  • 获取hash中某个键的值:hget [key] [field]
  • 同时添加多个键值对元素:hmset [key] [field1] [value1] [field2] [value2] ...
  • 同时获取多个键对应的值:hmget [key] [field1] [field2] ...
  • 获取hash中的所有的键值对数据:hgetall [key]
  • 删除hash中指定的某个键值对:hdel [key] [field1] [field2]...
  • 获取hash中键值对的个数:hlen [key]
  • 判断hash中是否存在某个键:hexists [key] [field]
  • 获取hash中所有的键:hkeys [key]
  • 获取hash中所有的值:hvals [key]
  • 让hash中指定键的值增加某个值:hincrby [key] [field] [增量]
  • 如果hash中不存在某个键就添加:hsetnx [key] [field] [value]

Zset:有序集合

有序集合中的元素可以排序
zset的命令都是以 z 开头

  • 添加集合元素,其中score是用于排序的标志:zset [key] [score] [value]
  • 获取集合中的值:zrange [key] [startIndex] [endIndex]
  • 对集合中指定范围score的值通过score来进行升序:zrangebyscore [key] [min] [max]

    • 如果后面在加上withscores,则会显示score。-inf和+inf表示负无穷和正无穷,表示显示所有,也可以自己加上范围
  • 倒序:zrevrangebyscore [key] [max] [min]
  • 移除集合中指定的元素:zrem [key] [value1] [value2] ...
  • 查看集合中元素的个数:zcard [key]
  • 查看指定score区间中的元素个数:zcount [key] [min] [max]

三种特殊数据类型

geospatial:地理位置

它在Redis3.2版本推出,这个功能可以推算地理位置的信息,两地之间的距离,方圆几里的人

注意:两极的地理位置无法直接添加,我们一般会下载城市数据,直接通过java程序一次性导入

常用指令

  • 添加:geoadd [key] [经度] [纬度] [名称]
  • 查看集合中指定名称的位置信息:geopos [key] [名称]
  • 返回两个给定位置之间的直线距离:geodist [key] [名称1] [名称2] [单位]

    • 如果两个中有一个不存在,则会返回空值。单位:m、km等
  • 以给定的经纬度为中心,找出指定半径范围内的元素:georadius [key] [经度] [纬度] [半径] [单位:m、km]

    • 后面还可以加上:

      • withdist:展示出两地之间的距离
      • withcoord:显示出元素的经纬度
      • count [count]:指定显示的数量
  • 以一个元素的坐标为中心,找出指定半径范围内的元素:georadiusbymember [key] [member] [半径] [单位]

    • 后面还可以加上:

      • withdist:展示出两地之间的距离
      • withcoord:显示出元素的经纬度
      • count [count]:指定显示的数量
  • 显示出指定元素的经纬度信息转化成的hash值,该命令返回11个字符串的geohash字符串,将二维的经纬度变为一维的字符串:geohash [key] [member1] [member2] ...

geo底层实现原理其实是一个Zset,我们可以使用Zset命令来操作geo

hyperloglog:基数统计算法

Redis2.8.9推出。基数:不重复的元素,可以接受误差。
应用场景:网页的UV
优点:占用的内存是固定的,2^64不同的元素的基数,只需要12KB的内存

hyperloglog的指令以 pf 开头

  • 创建一组元素:pfadd [key] [value1] [value2] ...
  • 统计集合中元素的基数:pfcount [key]
  • 将其他集合的元素复制到key1集合中:pfmerge [key1] [key2] ...

bitmap:位图

是一种数据结构,操作二进制位来进行记录,就只有0和1两种状态

常用指令:

  • setbit [key] [offset] [value] :其中offset和value的关系相当于键值对的关系,但是它们都必须是数字,并且value的值只能是0和1
  • getbit [key] [offset] :获取offset对应的value值
  • bitcount [key] :统计key中value值为1的个数

bitmap一般可以用于统计用户信息:活跃/不活跃、打卡/未打卡 ,这种只有两种状态的数据。

补充扩展

Redis为什么是单线程的?

redis是单线程的,它是基于内存操作的,CPU并不是它的性能瓶颈,机器内存和网络带宽才是它的瓶颈,既然可以用单线程来实现,就使用单线程了

注意:Redis是单线程的意思是:在处理我们的网络请求上是用一个线程来处理,而一个Redis服务器本身运行时,肯定是不止一个线程的

Redis为什么单线程还这么快?

1、Redis是将所有的数据全部放在内存上的,对数据的操作是在内存上的,因此对数据的读写不会受到硬盘IO流速度的限制。
2、数据结构简单,对数据操作也简单,KV键值对类似于HashMap,而HashMap的优势就是查找和操作的时间复杂度为O(1)。
3、使用单线程,无需让多个线程抢占CPU资源,对于内存系统来说,没有线程抢占CPU资源,没有上下文切换效率就是最高的,
多次读写都是在一个线程上,不需要考虑各种锁的问题,不存在加锁释放锁的操作,也不会因为出现死锁而导致性能的消耗
4、多路I/O的复用模型,非阻塞IO。多路:指多个网络连接。复用:指的是复用同一个线程。它可以使单个线程高效处理多个连接请求

最后修改:2021 年 06 月 25 日 09 : 10 AM
如果觉得我的文章对你有用,请随意赞赏