Redis
TRDB
建立在 DBMS 上,通过 DBMS 处理数据吗,单机物理性能受限、横向多机扩充(分布式引用)困难。
技术特点
- 使用强存储模式,表、行、字段的建立都需要预先严格定义,并进行相关数据约束。
- 采用 SQL 技术标准来定义和操作数据库。
- 采用强事务保证可用性和安全性。
- 主要采用单机集中式处理方式。
单机的局限性
- 单机读、写数据的速度瓶颈问题
- 单机存储数据量有限
- 服务器的纵向扩展的局限性
NoSQL
主体符合非关系型、分布式、开源、横向扩展能力的数据库。
技术特点
- 使用弱存储模式(取消数据存入类型的强制检查等一系列技术…),速度快。
- 没有采用 SQL 技术标准来定义和操作数据库。
- 采用弱事务或根本没有事务处理机制。
- 主要采用多机分布式处理(DP, Distributed Processing)。
- 数据存储结构多元化。
NoSql vs TRDB
- TRDB 为强数据存储模式,NoSql 为弱数据存储模式
- 分布式技术是 NoSql 的核心技术思路、TRDB 以集中处理为出发点
- TRDB 的事务严格遵循 ACID 原则,而 NoSql 主体遵循 Base 原则
- TRDB 都遵循 SQL 操作标准,NoSql 没有统一的操作标准
- TRDB 基础单机硬盘数据处理技术为主,NoSql 基于分布式或内存数据处理技术为主
Redis
Redis 是一个开源(BSD 许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings)、散列(hashes)、 列表(lists)、 集合(sets)、 有序集合(sorted sets)等。
Memcached vs Redis
内存管理机制
对比 | 内存管理机制 | 缺点 |
---|---|---|
Memcached | Memcached 默认使用 Slab Allocation 机制管理内存,其主要思想是按照预先规定的大小,将分配的内存分割成特定长度的块 以存储相应长度的 key-value 数据记录,以完全解决内存碎片问题。 | 缺点:会浪费一部分内存,例如 100bytes 需要使用 128btyes 的 chunk 来存储。会浪费 28 byte 【类似于 Java 虚拟机对象的分配,空闲列表】 |
Redis | Redis 使用直接内存分配的方式来存储数据,并且很少使用 free-list 等方式来优化内存分配。 | 缺点:会在一定程度上存在内存碎片。【因为内存是连续,类似于 Java 虚拟机对象的分配,直接内存分配(指针碰撞)】 |
数据持久化方案
对比 | 是否支持持久化 | 特点 |
---|---|---|
Memcached | 不支持 | Memcached 不支持内存数据的持久化操作,所有的数据都以 in-memory 的形式存储。 |
Redis | 支持 | Redis 支持持久化操作。Redis 提供了两种不同的持久化方法来讲数据存储到硬盘里面, 第一种是 rdb(全量备份),一种是 aof(增量备份)。 |
缓存数据过期机制
对比 | 缓存数据过期机制 |
---|---|
Memcached | Memcached 在删除失效主键时也是采用的消极方法,即 Memcached 内部也不会监视主键是否失效,而是在通过 Get 访问主键时才会检查其是否已经失效。 |
Redis | Redis 定时、定期等多种缓存失效机制,减少内存泄漏。 |
支持的数据类型
- Memcached 支持单一数据类型,[k,v]
- Redis 支持五种数据类型。
Redis Usage
数据库
- 优点:没有 Scheme 约束,数据结构的变更相对容易,无法一开始确定数据类型,抗压能力强,性能极高,10 万/qps。
- 缺点:没有索引,没有外键,缺少 int/date 等基本数据类型(基本都是 String 类型),多条件查询需要通过集合内联(sinter,zinterstore) 和连接间接实现开发效率低,可维护性不佳。
缓存
- jedis 整合使用方案 get key,value ["11","22"] 第一层在缓存进行查询,如果得到数据则直接返回, 如果没有查询到,第二层在数据库进行查询,并且刷新缓存,方便下次查询 ["11","22"]
- 作为 mybatis/hibernate 二级缓存使用方案,
1一级缓存:sqlSession,进程缓存,单次链接有效。
2二级缓存的作用域是mapper的同一个namespace。不同的sqlSession两次执行相同的namespace下的sql语句,且向sql中传递的参数也相同,即最终执行相同的sql语句,则第一次执行完毕会将数据库中查询的数据写到缓存,第二次查询会从缓存中获取数据,不再去底层数据库查询,从而提高效率。
Install Redis4.0
1# 下载redis
2[root@localhost test]# wget http://download.redis.io/releases/redis-4.0.6.tar.gz
3
4# 解压
5[root@localhost test]# tar -zxvf redis-4.0.6.tar.gz
6
7# 编译安装
8[root@localhost test]# cd redis-4.0.6/
9[root@localhost redis-4.0.6]# make MALLOC=libc
10[root@localhost redis-4.0.6]# cd src && make install
11
12# 修改配置文件,以守护进程的方式启动
13[root@localhost test]# vim /test/redis-4.0.6/redis.conf
14daemonize yes
15
16# 创建配置文件目录
17[root@localhost test]# mkdir /etc/redis
18
19# 复制配置文件到/etc/redis目录下
20[root@localhost test]# cp redis-4.0.6/redis.conf /etc/redis/6379.conf
21
22
23# 复制启动脚本至/etc/init.d目录下
24[root@localhost test]# cp redis-4.0.6/utils/redis_init_script /etc/init.d/redisd
25
26# 修改启动脚本
27[root@localhost test]# vim /etc/init.d/redisd
28#!/bin/sh
29# chkconfig: 2345 90 10
30
31# 启动Redis服务
32[root@localhost src]# ./redis-server
33
34# 开机自启动
35[root@localhost test]# chkconfig redisd on
36
37# 启动服务
38[root@localhost test]# service redisd start
39[root@localhost test]# ps -ef | grep redis
40root 48778 1 0 12:13 ? 00:00:00 /usr/local/bin/redis-server 127.0.0.1:6379
41root 48834 40010 0 12:13 pts/0 00:00:00 grep --color=auto redis
42
43# 停止服务
44[root@localhost test]# service redisd stop
45Stopping ...
46Redis stopped
47
48# 使用redis-cli连接Redis服务
49[root@localhost test]# /test/redis-4.0.6/src/redis-cli -p 6379
50127.0.0.1:6379>
Data Structure
String
String 是最常用的一种数据类型,普通的 key/value 存储都可以归为此类。
1# set/get:设置获取当个key的值。
2127.0.0.1:6379> set age 35
3OK
4127.0.0.1:6379> get age
5"35"
6
7# mset: 同时设置多个key
8127.0.0.1:6379> mset age1 20 age2 30
9OK
10
11# mget:获取多个key的值。
12127.0.0.1:6379> set name soulboy
13OK
14127.0.0.1:6379> mget age name
151) "35"
162) "soulboy"
17
18# incr && incrby :自增(值必须是Integer类型)
19127.0.0.1:6379> incr age
20(integer) 36
21127.0.0.1:6379> incrby age 4
22(integer) 40
23
24# incr && incrby:自减
25127.0.0.1:6379> decr age
26(integer) 39
27127.0.0.1:6379> decrby age 4
28(integer) 35
29
30# setnx:设置key对应的值为String类型的value,如果key已经存在则返回0(设置不生效),如果key不存在,则设置成功(返回1)
31127.0.0.1:6379> setnx age 100
32(integer) 0
33127.0.0.1:6379> setnx age2 100
34(integer) 1
35127.0.0.1:6379> get age
36"35"
37127.0.0.1:6379> get age2
38"100"
39
40# del:删除指定的key(删除成功返回1)
41127.0.0.1:6379> del age
42(integer) 1
43
44# setex:设置key对应的值为String类型的value,并设定有效期(默认时间单位是秒)
45127.0.0.1:6379> setex test_key 30 test_value
46OK
47127.0.0.1:6379> get test_key # 15秒
48"test_value"
49127.0.0.1:6379> get test_key # 35秒
50(nil)
51
52# getrange:获取key对应value的子字符串
53127.0.0.1:6379> set name abcdefghijk
54OK
55127.0.0.1:6379> getrange name 0 2
56"abc"
57
58# msetnx :同mset,不存在就设置,如果存在则返回0(设置不属生效)
59127.0.0.1:6379> msetnx age1 21 age2 31
60(integer) 0
61
62# getset:设置key的值,并返回key旧的值
63127.0.0.1:6379> getset age1 100
64"20"
65
66append:给指定key的value追加字符串,并返回新字符串的长度。
67127.0.0.1:6379> append name lmn
68(integer) 14
69127.0.0.1:6379> get name
70"abcdefghijklmn"
Hash
Hash 是一个 String 类型的 field 和 value 之间的映射表,Redis 的 Hash 数据类型的 key(hash 表名称)对应的 value 实际的内部存储结构为一个 HashMap。 [Sting,Map<String,String>]:一个 String 是 Map<String,String> 的名称。运用场景: 如用一个对象来存储用户信息,商品信息,订单信息等等。
- Hash 特别适合存储对象
- 相对于把一个对象的每个属性存储为 String 类型,将整个对象存储在 Hash 类型中会占用更少内存。
- 所存储的成员较少时数据存储为 zipmap,当成员数量增大时会自动转成真正的 HashMap,此时 encoding 为 ht。
1# hset:设置key对应的HashMap中的field的value
2127.0.0.1:6379> hset info name soulboy
3(integer) 1
4127.0.0.1:6379> hset info age 30
5(integer) 1
6
7# hget:获取key对应的HashMap中的指定field的value
8127.0.0.1:6379> hget info name
9"soulboy"
10127.0.0.1:6379> hget info age
11"30"
12
13# hgetall:获取key对应的HashMap中的所有field的value
14127.0.0.1:6379> hgetall info
151) "name"
162) "soulboy"
173) "age"
184) "30"
19
20# hlen:返回key对应的HashMap中的field的数量
21127.0.0.1:6379> hlen info
22(integer) 2
23
24# hmset:批量设置HashMap中的多个field的值。
25127.0.0.1:6379> hmset info gender man salary 100
26OK
27
28# hmget:批量获取HashMap中的多个field的值。
29127.0.0.1:6379> hmget info name age gender salary
301) "soulboy"
312) "30"
323) "man"
334) "100"
34
35# hlen:获取指定HashMap中键值对的组数。
36127.0.0.1:6379> hlen info
37(integer) 4
38
39# hdel:删除HashMap中指定的键值对。
40127.0.0.1:6379> hdel info gender
41(integer) 1
List
1# lpush:在key对应的list的头部(左)添加一个元素
2127.0.0.1:6379> lpush newlist value1 value2 value3
3(integer) 3
4
5# lrange:获取key对应的list的指定下标范围的元素,-1表示获取所有元素
6127.0.0.1:6379> lrange newlist 0 -1
71) "value3"
82) "value2"
93) "value1"
10
11# lpop:从key对应的list的头部删除一个元素,并返回该元素
12127.0.0.1:6379> lpop newlist
13"value3"
14
15# rpush:在key对应的list的尾部添加一个元素
16127.0.0.1:6379> rpush newlist value3
17(integer) 3
18127.0.0.1:6379> lrange newlist 0 -1
191) "value2"
202) "value1"
213) "value3"
22
23# rpop:从key对应的list的尾部删除一个元素,并返回该元素
24127.0.0.1:6379> rpop newlist
25"value3"
26127.0.0.1:6379> lrange newlist 0 -1
271) "value2"
282) "value1"
Set
1# sadd:在key对应的set中添加一个元素
2127.0.0.1:6379> sadd myset new1 new2 new3 new1
3(integer) 3
4
5# smembers:获取key对应的set的所有元素
6127.0.0.1:6379> smembers myset
71) "new1"
82) "new2"
93) "new3"
10
11# spop:随机返回并删除key对应的set中的一个元素
12127.0.0.1:6379> spop myset
13"new1"
14
15# srem:删除集合中一个或多个成员
16127.0.0.1:6379> srem myset new1 new2
17(integer) 2
18
19# suion:求给定key对应的set并集
20127.0.0.1:6379> sunion myset myset2
211) "new1"
222) "new4"
233) "new2"
244) "new3"
25
26# sinter:求给定key对应的set交集
27127.0.0.1:6379> sinter myset myset2
281) "new3"
292) "new4"
30
31# 返回给定所有集合的差集(大的集合在前)
32127.0.0.1:6379> sdiff myset2 myset
331) "new1"
342) "new2"
SortSet
set 的基础增加顺序 score,再根据 score 进行排序。
sortset 使用了 hashmap 作为数据结构,还加了一层跳跃表,跳跃表:相当于双向链表,在其基础上添加前往比当前元素大的跳转链接,可以利用类似【索引】的思想,提取出【链表】中的【部分关键节点】作为索引,从而形成多层次的关键节点的多层索引。
跳跃表的优点:维持【结构平衡】的成本比较低,完全依靠【随机】。
1# zadd:在key对应的zset中添加一个元素
2127.0.0.1:6379> zadd myzset 1 one 2 two 3 three
3(integer) 3
4
5# zrange:获取key对应的zset中指定范围的元素,-1表示获取所有元素
6127.0.0.1:6379> zrange myzset 0 -1
71) "one"
82) "two"
93) "three"
10127.0.0.1:6379> zrange myzset 0 -1 withscores
111) "one"
122) "1"
133) "two"
144) "2"
155) "three"
166) "3"
17
18# zrem——删除key对应的zset中的一个元素
19127.0.0.1:6379> zrem myzset one
20(integer) 1
21127.0.0.1:6379> zrange myzset 0 -1
221) "two"
232) "three"
24
25# zrangebyscore——返回有序集key中,指定分数范围的元素列表(排行榜:分数从100分到1000分的用户的升序)
26127.0.0.1:6379> zrangebyscore myzset 2 9
271) "two"
282) "three"
293) "six"
304) "nine"
31
32# 查看zset中元素的数量
33127.0.0.1:6379> zcard myzset
34(integer) 4
35
36# zrank——返回key对应的zset中指定member的排名。其中member按score值递增(从小到大); 排名以0为底,也就是说,score值最小的成员排名为0,排行榜中运用。
37127.0.0.1:6379> zrank myzset six
38(integer) 2
Publish&Subscribe
发布订阅类似于信息管道,用来进行系统之间消息解耦,类似于 mq,rabbitmq、rocketmq、kafka、activemq 主要有消息发布者和消息订阅者。比如运用于:订单支付成功,会员系统加积分、钱包进行扣钱操作、发货系统(下发商品)等……
- PUBLISH 将信息 message 发送到指定的频道 channel。返回收到消息的客户端数量。
- SUBSCRIBE 订阅给指定频道的信息。
- UNSUBSCRIBE 取消订阅指定的频道,如果不指定,则取消订阅所有的频道。
Redis 的消息订阅发布和 mq 对比
Redis 发布订阅功能比较薄弱但比较轻量级,mq 消息持久化以及数据可靠性比较差,无后台功能,无法使用 msgId、msgKey 进行查询消息。
1# Publish 发布channela主题
2127.0.0.1:6379> PUBLISH channela test
3(integer) 0
4
5# Subscribe 订阅channela主题
6127.0.0.1:6379> PUBLISH channela test
7(integer) 0
8
9# Publish 发送消息
10127.0.0.1:6379> PUBLISH channela test
11(integer) 1
12
13# Subscribe 订阅者收到消息
141) "message"
152) "channela"
163) "test"
TRDB Trasaction
传统关系型数据库事务
一个数据库事务通常包含了一个序列的对数据库的读/写操作。它的存在包含有以下两个目的:
- 为数据库操作序列提供了一个从失败中恢复到正常状态的方法,同时提供了数据库即使在异常状态下仍能保持一致性的方法。
- 当多个应用程序在并发访问数据库时,可以在这些应用程序之间提供一个隔离方法,以防止彼此的操作互相干扰。
事务的 ACID 四大特性
- 原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行
- 一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束
- 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行
- 持久性(Durability):已被提交的事务对数据库的修改应该永久保存在数据库中
事务隔离机制
- 语法:set global transaction isolation level read uncommitted;
- 种类:read uncommitted、read committed、repeatable read、serializable
InnoDB MVCC 实现 Repeatable Read
多版本并发控制机制如下:
row record:行数据 + DATA_TRX_ID + DATA_ROLL_PTR + DELETE BIT
- DATA_TRX_ID :本条记录的 Trasaction_ID,每处理一个事务的时候,其值自动 +1。
- DATA_ROLL_PTR:存储 undo log 的指针,undo log 用来记录事务执行命令的记录。
- DELETE BIT:用来标识这条记录是否被删除,不是真正删除,真正的删除发生在事务 commit 时候。
Redis Trasaction
Redis 事务基本命令
- MULTI 与 EXEC 命令:以 MULTI 开始一个事务,然后将多个命令入队到事务中, 最后由 EXEC 命令触发事务, 一并执行事务中的所有命令
1127.0.0.1:6379> MULTI
2OK
3127.0.0.1:6379> set name soulboy
4QUEUED
5127.0.0.1:6379> get name
6QUEUED
7127.0.0.1:6379> set food bread
8QUEUED
9127.0.0.1:6379> EXEC
101) OK
112) "soulboy"
123) OK
- DISCARD 命令:DISCARD 命令用于取消一个事务, 它清空客户端的整个事务队列, 然后将客户端从事务状态调整回非事务状态, 最后返回字符串 OK 给客户端, 说明事务已被取消。
1127.0.0.1:6379> MULTI
2OK
3127.0.0.1:6379> set class 001
4QUEUED
5127.0.0.1:6379> get class
6QUEUED
7127.0.0.1:6379> set member 007
8QUEUED
9127.0.0.1:6379> DISCARD
10OK
11127.0.0.1:6379> EXEC
12(error) ERR EXEC without MULTI
- WATCH 命令:WATCH 命令用于在事务开始之前监视任意数量的键: 当调用 EXEC 命令执行事务时, 如果任意一个被监视的键已经被其他客户端修改了, 那么整个事务不再执行, 直接返回失败。
1127.0.0.1:6379> WATCH msg
2OK
3127.0.0.1:6379> MULTI
4OK
5127.0.0.1:6379> set msg 1
6QUEUED
7127.0.0.1:6379> get msg #此时另外一终端修改了 msg
8QUEUED
9127.0.0.1:6379> EXEC
10(nil)
Redis 事务 ACID
- 非原子性(Atomicity):单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的。除非一个事务队列中的所有命令都被成功地执行,才可以称这个事务执行成功。
- 一致性(Consistency)
入队错误:在命令入队的过程中,如果客户端向服务器发送了错误的命令,比如命令的参数数量不对,等等, 那么服务器将向客户端返回一个出错信息, 并且将客户端的事务状态设为 REDIS_DIRTY_EXEC ,将会回滚整个入列命令,回滚到 MULTI 命令之前的状态。
1127.0.0.1:6379> MULTI
2OK
3127.0.0.1:6379> set msg 1
4QUEUED
5127.0.0.1:6379> get msg
6QUEUED
7127.0.0.1:6379> EXEC
8(nil)
9127.0.0.1:6379> MULTI
10OK
11127.0.0.1:6379> set username soulboy
12QUEUED
13127.0.0.1:6379> get
14(error) ERR wrong number of arguments for 'get' command
15127.0.0.1:6379> get username
16QUEUED
17127.0.0.1:6379> get
18(error) ERR wrong number of arguments for 'get' command
19127.0.0.1:6379> EXEC
20(error) EXECABORT Transaction discarded because of previous errors.
执行错误:如果命令在事务执行的过程中发生错误,比如说,对一个不同类型的 key 执行了错误的操作, 那么 Redis 只会将错误包含在事务的结果中,这不会引起事务中断或整个失败,不会影响已执行事务命令的结果,也不会影响后面要执行的事务命令,所以它对事务的一致性也没有影响。
- 隔离性(Isolation):WATCH 命令用于在事务开始之前监视任意数量的键: 当调用 EXEC 命令执行事务时, 如果任意一个被监视的键已经被其他客户端修改了, 那么整个事务不再执行, 直接返回失败。
- 持久性(Durability):因为事务不过是用队列包裹起了一组 Redis 命令,并没有提供任何额外的持久性功能,所以事务的持久性由 Redis 所使用的持久化模式决定。Redis 持久化模式包括:rdb(数据内存备份)、aof(append onlyif 增量备份),每个一秒做一个备份。