[前言]关系数据库与非关系型数据库
关系型数据库
- 一个结构化的数据库,创建在关系模型基础上
- 一般面向于记录
- 包括:Oracle、MySQL、SQL Server、Microsoft Access、DB2等非关系型数据库
非关系型数据库
- 除了主流的关系型数据库外的数据库,都认为是非关系型·
- 包括:Redis、MongBD、Hbase、CouhDB等
非关系型数据库产生背景
- High performance———对数据库高并发读写需求
- Huge Storage———对海量数据高效存储与访问需求
- High Scalability && High Availability———对数据库高可扩展性与高可用性需求
一、Redis概述
- REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统。
- Redis是一个开源的使用ANSI C语言编写、遵守BSD协议。
- 支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
- 它通常被称为数据结构服务器,因为值(value)可以是 字符串(String), 哈希(Hash), 列表(list), 集合(sets) 和 有序集合(sorted sets)等类型。
Rdis简介
Redis 是完全开源的,遵守 BSD 协议,是一个高性能的 key-value 数据库。
Redis 与其他 key - value 缓存产品有以下三个特点:
- Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
- Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
- Redis支持数据的备份,即master-slave模式的数据备份。
- Redis基于内存运行并支持持久化采用key-value(键值对)的存储形式
Redis配置文件
配置参数(/etclredis/6379.conf为主配置文件)
参数 | 描述 |
---|---|
bind | 监听的主机地址 |
port | 端口 |
daemonize yes | 启用守护进程 |
pidfile | 指定PID文件 |
loglevel notice | 日志级别notice是通告级别,日志有8个警告级别 |
logfile | 指定日志文件 |
Redis 优势
- 性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
- 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
- 原子 性– Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
- 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。
Redis多数据库概念
- Redis支持多个数据库,并且每个数据库的数据是隔离的不能共享,并且基于单机(单实例)才有,如果是集群就没有数据库的概念。
- Redis是一个字典结构的存储服务器,而实际上一个Redis实例提供了多个用来存储数据的字典,客户端可以指定将数据存储在哪个字典中。这与我们熟知的在一个关系数据库实例中可以创建多个数据库类似,所以可以将其中的每个字典都理解成一个独立的数据库。
- 每个数据库对外都是一个从O开始的递增数字命名,Redis默认支持16个数据库(可以通过配置文件支持更多,无上限),可以通过配置databases来修改这一数字。
- 客户端与Redis建立连接后会自动选择0号数据库,不过可以随时使用SELECT命令更换数据库,如要选择1号数据库:select 1
注意:
♦ Redis不支持自定义数据库的名字,每个数据库都以编号命名,开发者必须自己记录哪些数据库存储了哪些数据。另外Redis也不支持为每个数据库设置不同的访问密码,所以一个客户端要么可以访问全部数据库,要么连一个数据库也没有权限访问。最重要的一点是多个数据库之间并不是完全隔离的,比如FLUSHALL命令可以清空一个Redis实例中所有数据库中的数据。
◆ 综上所述,这些数据库更像是一种命名空间,而不适宜存储不同应用程序的数据。
多数据库的作用场景:
- 可以使用0号数据库存储某个应用生产环境中的数据,使用1号数据库存储测试环境中的数据,但不适宜使用0号数据库存储A应用的数据而使用1号数据库B应用的数据,不同的应用应该使用不同的Redis实例存储数据。
- 由于Redis非常轻量级,一个空Redis实例占用的内存只有1M左右,所以不用担心多个Redis实例会额外占用很多内存。
二、安装redis
1.下载redis源码包
[root@localhost ~]# wget https://download.redis.io/releases/redis-5.0.10.tar.gz ##下载软件包
[root@localhost ~]# ls ##查看源码包
redis-5.0.10.tar.gz
2.编译安装redis
[root@localhost ~]# yum -y install gcc-c++ gcc make ##安装编译环境
[root@localhost ~]# tar zxvf redis-5.0.10.tar.gz -C /opt
[root@localhost ~]# cd /opt/redis-5.0.10/
[root@localhost redis-5.0.10]# ls ##查看没有configure脚本,redis不需要./configure,已经对其进行封装化了
00-RELEASENOTES COPYING Makefile redis.conf runtest-moduleapi src
BUGS deps MANIFESTO runtest runtest-sentinel tests
CONTRIBUTING INSTALL README.md runtest-cluster sentinel.conf utils
[root@localhost redis-5.0.10]# make
[root@localhost redis-5.0.10]# make PREFIX=/usr/local/redis install ##安装是需要指定安装路径
3.执行配置文件脚本install_server.sh,自动生成相关配置文件
[root@localhost redis-5.0.10]# cd /usr/local/redis/
[root@localhost redis]# ls
bin
[root@localhost redis]# cd bin/
[root@localhost bin]# ls ##在bin目录下有一些连接终端
redis-benchmark redis-check-aof redis-check-rdb redis-cli redis-sentinel redis-server
[root@localhost bin]# cd /opt/redis-5.0.10/ ##源码目录下有个utils工具目录
[root@localhost redis-5.0.10]# cd utils/
[root@localhost utils]# ls ##utils目录下install_server.sh给redis配置各种配置文件的脚本
build-static-symbols.tcl generate-command-help.rb install_server.sh
……省略部分
[root@localhost utils]# ./install_server.sh ##执行脚本
……省略部分
Please select the redis port for this instance: [6379] ##直接回车,默认端口6379
Selecting default: 6379
Please select the redis config file name [/etc/redis/6379.conf] ##配置文件存放位置
Selected default - /etc/redis/6379.conf
Please select the redis log file name [/var/log/redis_6379.log] ##日志文件存放位置
Selected default - /var/log/redis_6379.log
Please select the data directory for this instance [/var/lib/redis/6379] ##实例的数据文件位置
Selected default - /var/lib/redis/6379
Please select the redis executable path [] /usr/local/redis/bin/redis-server ##扩展路径,需要自己手动设置
Is this ok? Then press ENTER to go on or Ctrl-C to abort. ##这里直接回车确认
……省略部分
[root@localhost utils]# cd /etc/redis
[root@localhost redis]# ls ##执行完成后可以查看到配置文件了
6379.conf
[root@localhost redis]# netstat -anupt |grep redis ##执行完脚本,redis自动开启
tcp 0 0 127.0.0.1:6379 0.0.0.0:* LISTEN 6412/redis-server 1
4.优化命令调用
[root@localhost redis]# ln -s /usr/local/redis/bin/* /usr/local/bin/
5.测试关闭/启动脚本(/etc/init.d/redis_6379),安装是自动生成的脚本
[root@localhost ~]# /etc/init.d/redis_6379 stop ##关闭redis
Stopping ...
Redis stopped
[root@localhost ~]# netstat -anupt |grep redis ##端口关闭了
[root@localhost ~]#
[root@localhost ~]# /etc/init.d/redis_6379 start##开启redis
Starting Redis server...
[root@localhost ~]# netstat -anupt |grep redis ##启动成功
tcp 0 0 127.0.0.1:6379 0.0.0.0:* LISTEN 6691/redis-server 1
6.设置service系统服务管理
[root@localhost init.d]# cp -p /etc/init.d/redis_6379 /etc/init.d/redis_6379bak
[root@localhost init.d]# mv /etc/init.d/redis_6379 /etc/init.d/redis
[root@localhost init.d]# vi redis
#Configurations injected by install_server below....
#chkconfig: 2345 90 25 ##插入这条声明
……省略部分
[root@localhost init.d]# chkconfig --add redis
[root@localhost init.d]# chkconfig --list redis
Note: This output shows SysV services only and does not include native
……省略部分
redis 0:off 1:off 2:on 3:on 4:on 5:on 6:off
[root@localhost ~]# service redis stop ##关闭redis
Stopping ...
Redis stopped
[root@localhost ~]# netstat -anupt |grep redis
[root@localhost ~]# service redis start ## 开启redis
Starting Redis server...
[root@localhost ~]# netstat -anupt |grep redis ##成功开启
tcp 0 0 127.0.0.1:6379 0.0.0.0:* LISTEN 42014/redis-server
7.本地进入redis服务器
[root@localhost ~]# redis-cli -p 6379 ##本地进入不需要指定主机,可以直接登入
127.0.0.1:6379>
8.远程进入redis服务器,需要修改监听地址
[root@localhost ~]# redis-cli -h 192.168.10.10 -p 6379 ##这时候指定主机还无法进入redis客户端,需要修改配置,指定监听地址,redis-cli是客户端登入命令
Could not connect to Redis at 192.168.10.10:6379: Connection refused
not connected>
[root@localhost ~]# vim /etc/redis/6379.conf ##修改配置文件
'70行//' bind 127.0.0.1 192.168.10.10 ##在后面追加监听地址192.168.10.10
[root@localhost ~]# service redis restart ##重启服务
[root@localhost ~]# redis-cli -h 192.168.10.10 -p 6379 ##修改完配置后可以直接进入了
192.168.10.10:6379>
三、了解redis的数据结构类型
Redis支持五种数据类型:
- String: 字符串
- Hash: 散列
- List: 列表
- Set: 集合
- Sorted Set: 有序集合
1.String(字符串)
- string 是 redis 最基本的类型,你可以理解成与 Memcached 一模一样的类型,一个 key 对应一个 value。
- string 类型是二进制安全的。意思是 redis 的 string 可以包含任何数据。比如jpg图片或者序列化的对象。
- string 类型是 Redis 最基本的数据类型,string 类型的值最大能存储 512MB。
[root@localhost ~]# redis-cli --raw ##加上--raw选项可以识别中文
127.0.0.1:6379> set A 你好
OK
127.0.0.1:6379> get A
你好
127.0.0.1:6379> type A
string
在以上实例中我们使用了 Redis 的 SET 和 GET 命令
注意:一个键最大能存储 512MB。
2.Hash(哈希)
- Redis hash 是一个键值(key=>value)对集合。
- Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。
哈希类型的数据引用了类和对象的概念,哈希类型数据中的键相当于一个对象,一个对象可以拥有多个属性,一个哈希类型的键值对(K-V)可以抽象的看做关系型数据库中的库
127.0.0.1:6379> hmset test word1 "hello" word2 "world" ##设置键(对象)test,属性1的值为“hello”,属性2的值为“world”
OK
127.0.0.1:6379> HMGET test word1
hello
127.0.0.1:6379> HMGET test word2
world
127.0.0.1:6379> HMGET test word1 word2
hello
world
127.0.0.1:6379> del test
1
127.0.0.1:6379> HMSET name id1 "张三" ##设置哈希键name,给字段id1赋值“张三”
OK
127.0.0.1:6379> HMSET name id2 "李四"
OK
127.0.0.1:6379> HMGET name id1 ##获取哈希键name中字段id1的值
张三
127.0.0.1:6379> HMGET name id2
李四
127.0.0.1:6379> HMGET name id1 id2
张三
李四
127.0.0.1:6379> del name ##删除哈希数据类型键值,同样用del
1
实例中我们使用了 Redis HMSET, HMGET 命令,HMSET 设置了两个 field=>value 对, HGET 获取对应 field 对应的 value。
每个 hash 可以存储 232 -1 键值对(40多亿)。
3.List(列表)
- Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。
- List数据类型的数值是可以重复的
- 列表最多可存储 232 - 1 元素 (4294967295, 每个列表可存储40多亿)。
127.0.0.1:6379> lpush name "张三" ##给列表name中添加值
1
127.0.0.1:6379> lpush name "李四"
2
127.0.0.1:6379> lpush name "王五"
3
127.0.0.1:6379> lpush name "赵柳"
4
127.0.0.1:6379> lpush name "孙七"
5
127.0.0.1:6379> LRANGE name 0 1 ##查看name列表0-1的值
孙七
赵柳
127.0.0.1:6379> LRANGE name 2 3
王五
李四
127.0.0.1:6379> LRANGE name 4 4
张三
127.0.0.1:6379> LRANGE name 3 3
李四
127.0.0.1:6379> del name
1
List类型数据以类似索引的方式存储,最后加入的值得索引为0,最早插入值得索引号为列表值得总数减1
4.Set(无序集合)
- Redis 的 Set 是 string 类型的无序集合。
- 集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。
- 集合类型的数据具有唯一性,不能重复
sadd 命令作用:
- 添加一个 string 元素到 key 对应的 set 集合中,成功返回 1,如果元素已经在集合中返回 0。
- 格式:sadd key member
127.0.0.1:6379> sadd name "章子怡" ##给name集合添加元素
1
127.0.0.1:6379> sadd name "路易斯"
1
127.0.0.1:6379> sadd name "王麻子"
1
127.0.0.1:6379> sadd name "章子怡"
0
127.0.0.1:6379> sadd name "王麻子"
0
127.0.0.1:6379> SMEMBERS name ##查看集合内所有元素
王麻子
路易斯
章子怡
127.0.0.1:6379> del name
1
注意:以上实例中 "章子怡"添加了两次,但根据集合内元素的唯一性,第二次插入的元素将被忽略。
集合中最大的成员数为 232 - 1(4294967295, 每个集合可存储40多亿个成员)。
5.zset(sorted set:有序集合)
- Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。
- 不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的降序排序。
- zset的成员是唯一的,但分数(score)却可以重复。
- 可以根据score值精确查找,比无序集合更加人性化
zadd 命令的作用
- 添加元素到集合,元素在集合中存在则更新对应score
- 格式:zadd key score member
######
192.168.10.10:6379> zadd colour 0 red 0 yellow 1 black 2 white ## 设置有序集合colour,插入4个元素
(integer) 4
192.168.10.10:6379> zrangebyscore colour 0 10 ## 有序集合类型中的元素以score值的大小进行排序,
1) "red"
2) "yellow"
3) "black"
4) "white"
192.168.10.10:6379> zrangebyscore colour 0 0 ## 可以根据score值精确查找数据
1) "red"
2) "yellow"
192.168.10.10:6379> ZRANGEBYSCORE colour 2 2
1) "white"
192.168.10.10:6379> zadd colour 1.5 green ##插入一个score为1.5的值
(integer) 1
192.168.10.10:6379> zrangebyscore colour 0 10 ## 新插入的值按score排列
1) "red"
2) "yellow"
3) "black"
4) "green"
5) "white"
127.0.0.1:6379> ZADD score 60 "小明"
1
127.0.0.1:6379> ZADD score 76 "小王"
1
127.0.0.1:6379> ZADD score 85 "小志"
1
127.0.0.1:6379> ZADD score 96 "小李"
1
127.0.0.1:6379> ZRANGEBYSCORE score 0 100
小明
小王
小志
小李
各个数据类型应用场景:
类型 | 简介 | 特性 | 场景 |
---|---|---|---|
String(字符串) | 二进制安全 | 可以包含任何数据,比如jpg图片或者序列化的对象,一个键最大能存储512M | — |
Hash(字典) | 键值对集合,即编程语言中的Map类型 | 适合存储对象,并且可以像数据库中update一个属性一样只修改某一项属性值(Memcached中需要取出整个字符串反序列化成对象修改完再序列化存回去) | 存储、读取、修改用户属性 |
List(列表) | 链表(双向链表) | 增删快,提供了操作某一段元素的API | 1. 最新消息排行等功能(比如朋友圈的时间线) 2. 消息队列 |
Set(集合) | 哈希表实现,元素不重复 | 1、添加、删除,查找的复杂度都是O(1) 2、为集合提供了求交集、并集、差集等操作 | 1、共同好友 2、利用唯一性,统计访问网站的所有独立ip 3、好友推荐时,根据tag求交集,大于某个阈值就可以推荐 |
ZSet(有序集合) | 将Set中的元素增加一个权重参数score,元素按score有序排列 | 数据插入集合时,已经进行天然排序 | 1、排行榜 2、带权重的消息队列 |
四、了解Redis 事务
-
Redis 事务可以一次执行多个命令, 并且带有以下三个重要的保证:
◆ 批量操作在发送 EXEC 命令前被放入队列缓存。
◆ 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
◆ 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。 -
一个事务从开始到执行会经历以下三个阶段:
◆ 开始事务。
◆ 命令入队。
◆ 执行事务。 -
Redis 事务命令
相关命令 | 用途描述 |
---|---|
MULTI | 标记一个事务块的开始。 |
EXEC | 执行所有事务块内的命令。 |
DISCARD | 取消事务,放弃执行事务块内的所有命令。 |
事务示范
[root@promote ~]# redis-cli -h 192.168.10.10 -p 6379
192.168.10.10:6379> MULTI ##开始事务
OK
192.168.10.10:6379> set A 123
QUEUED
192.168.10.10:6379> set B 456
QUEUED
192.168.10.10:6379> set C 789
QUEUED
192.168.10.10:6379> DISCARD ##中断事务
OK
192.168.10.10:6379> keys * ##A、C、B三个键并没有被设置,事务具有原子性
1) "key:__rand_int__"
2) "counter:__rand_int__"
3) "handsome-boy"
4) "*"
5) "myset:__rand_int__"
6) "mylist"
192.168.10.10:6379> MULTI ##开始事务
OK
192.168.10.10:6379> set A 123
QUEUED
192.168.10.10:6379> set B 456
QUEUED
192.168.10.10:6379> set C 789
QUEUED
192.168.10.10:6379> EXEC ##提交事务
1) OK
2) OK
3) OK
192.168.10.10:6379> keys * ##查看,事务执行成功,产生了A、B、C三个键
1) "counter:__rand_int__"
2) "key:__rand_int__"
3) "B"
4) "handsome-boy"
5) "myset:__rand_int__"
6) "*"
7) "C"
8) "mylist"
9) "A"
五、演示redis的基础命令用法
[root@localhost ~]# redis-cli -h 192.168.10.10 -p 6379
192.168.10.10:6379>
192.168.10.10:6379> help ##列出帮助信息
redis-cli 5.0.10
To get help about Redis commands type:
"help @<group>" to get a list of commands in <group>
"help <command>" for help on <command>
……省略部分
192.168.10.10:6379> help @list ##列出主列表信息
BLPOP key [key ...] timeout
summary: Remove and get the first element in a list, or block until one is available
since: 2.0.0
……省略部分
192.168.10.10:6379> help set ##查看set的帮助信息,set的用法
SET key value [expiration EX seconds|PX milliseconds] [NX|XX]
summary: Set the string value of a key
since: 1.0.0
group: string
192.168.10.10:6379> set teacher ltp ##设置键值对,键是teacher、值是ltp
OK
192.168.10.10:6379> set handsome-boy ltp
OK
192.168.10.10:6379> keys * ##可以查看所有键的信息
1) "handsome-boy"
2) "teacher"
192.168.10.10:6379> set tea good
OK
192.168.10.10:6379> keys t?? ##这里的?可以充当一个任意字符
1) "tea"
192.168.10.10:6379> get handsome-boy ##可以使用get来获取key(键)的值
"ltp"
192.168.10.10:6379> EXISTS handsome-boy ##可以查看某个可以是否存在,用exists命令,支持Tab键补全
(integer) 1
192.168.10.10:6379> exist aaa ##查看是否存在key,返回1和0,1代表存在、0代表不存在
(integer) 0
192.168.10.10:6379> keys * ##查看所有的key
1) "handsome-boy"
2) "teacher"
3) "tea"
192.168.10.10:6379> del tea ##使用del删除key
(integer) 1
192.168.10.10:6379> keys * ##再次查看,tea这个键已经被删除了
1) "handsome-boy"
2) "teacher"
192.168.10.10:6379> type teacher 使用type命令来查看键对应值的类型
string
192.168.10.10:6379> rename teacher tea ##rename可以对key进行更名
OK
192.168.10.10:6379> keys * ##更名成功
1) "handsome-boy"
2) "tea"
192.168.10.10:6379> get tea ##更名不影响它的值
"ltp"
192.168.10.10:6379> select 1 ## 选择库2
OK
192.168.10.10:6379[1]> keys * ## 库之间互相隔离
(empty list or set)
192.168.10.10:6379> exit ##退出
六、可以利用redis-benchmark测试工具进行压测
常用选项:
-h:指定监听主机地址
-p:指定端口
-c:指定并发请求,如并发100个
-n:指定一共发多少个请求
-p:强制退出redis
-d:发送的字节数
[root@localhost ~]# redis-benchmark -h 192.168.10.10 -p 6379 -c 100 -n 100000
##利用redis-benchmark 命令进行压力测试
……省略部分
====== SET ====== ##set设置命令的能力测试
100000 requests completed in 0.57 seconds
100 parallel clients
3 bytes payload
keep alive: 1
====== GET ====== ##get获取值得能力测试
100000 requests completed in 0.56 seconds ##十万个包花了0.56秒,真实环境更低
100 parallel clients
3 bytes payload
keep alive: 1
……省略部分
[root@localhost ~]# redis-benchmark -h 192.168.10.10 -p 6379 -q -d 100
……省略部分
SET: 175438.59 requests per second ##可以测试set和get测试值
GET: 186567.16 requests per second
……省略部分
七、Redis数据库key管理
192.168.10.10:6379[1]> select 0
OK
192.168.10.10:6379> keys *
1) "colour"
192.168.10.10:6379> rename colour col ## 修改键名
OK
192.168.10.10:6379> keys *
1) "col"
########################## PEXPIRE 设置超时时间########################
192.168.10.10:6379> PEXPIRE col 3000 ## 设置key的超时时间,以毫秒为单位
(integer) 1
192.168.10.10:6379> keys * ## 立即查看,clo还在
1) "col"
192.168.10.10:6379> keys * ## 3秒后再次查看,clo超时,自动消失
(empty list or set)
######################### PTTL 查看key的剩余超时时间####################
192.168.10.10:6379> set name zhangshan
OK
192.168.10.10:6379> keys * ## 再建一个key用于测试
1) "name"
192.168.10.10:6379> PEXPIRE name 500000
(integer) 1
192.168.10.10:6379> PTTL name
(integer) 493981
192.168.10.10:6379> PTTL name
(integer) 492326
192.168.10.10:6379> PTTL name
(integer) 488399
192.168.10.10:6379> PTTL name
(integer) 480297
############################ PERSIST 取消超时时间,-1为永不超时################
192.168.10.10:6379> PERSIST name
(integer) 1
192.168.10.10:6379> PTTL name
(integer) -1
########################### mset 多键创建,mget 多键查询#######################
192.168.10.10:6379> mset colour read score 80 sex boy
OK
192.168.10.10:6379> keys *
1) "sex"
2) "colour"
3) "score"
192.168.10.10:6379> mget colour score sex
1) "read"
2) "80"
3) "boy"