事务可以看做是一次大的活动,它由不同的小活动组成,这些活动要么全部成功,要么全部失败。
数据库事务的四大特性 ACID:
主流的数据库例如MySQL、PostgreSQL等,都支持ACID事务,其内部会采用MVCC(多版本并发控制)技术,实现高性能、高并发的本地事务。
在计算机系统中,更多的是通过关系型数据库来控制事务,这是利用数据库本身的事务特性来实现的,因此叫数据 库事务,由于应用主要靠关系数据库来控制事务,而数据库通常和应用在同一个服务器,所以基于关系型数据库的 事务又被称为本地事务。
分布式事务指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上,且属于不同的应用,分布式事务需要保证这些操作要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同数据库的数据一致性。
分布式系统会把一个应用系统拆分为可独立部署的多个服务,因此需要服务与服务之间远程协作才能完成事务操 作,这种分布式系统环境下由不同的服务之间通过网络远程协作完成事务称之为分布式事务,例如用户注册送积分 事务、创建订单减库存事务,银行转账事务等都是分布式事务。
本地事务依赖数据库本身提供的事务特性来实现,因此以下逻辑可以控制本地事务:
begin transaction;
//1.本地数据库操作:张三减少金额
//2.本地数据库操作:李四增加金额
commit transation;
但是在分布式环境下,会变成下边这样:
begin transaction;
//1.本地数据库操作:张三减少金额
//2.远程调用:让李四增加金额
commit transation;
当远程调用让李四增加金额成功了,由于网络问题远程调用并没有返回,此时本地事务提交失败就回滚 了张三减少金额的操作,此时张三和李四的数据就不一致了。
因此在分布式架构的基础上,传统数据库事务就无法使用了,张三和李四的账户不在一个数据库中甚至不在一个应 用系统里,实现转账事务需要通过远程调用,由于网络问题就会导致分布式事务问题。
CAP定理是在 1998年加州大学的计算机科学家 Eric Brewer (埃里克.布鲁尔)提出,分布式系统有三个指标
它们的第一个字母分别是 C、A、P,且这三个指标不可能同时做到,这个结论就叫做 CAP 定理。
分布式系统中,数据一般会存在不同节点的副本中,如果对第一个节点的数据成功进行了更新操作,而第二个节点上的数据却没有得到相应更新,这时候读取第二个节点的数据依然是更新前的数据,即脏数据,这就是分布式系统数据不一致的情况。
在分布式系统中,如果能够做到针对一个数据项的更新操作执行成功后,所有的用户都能读取到最新的值,那么这样的系统就被认为具有强一致性(或严格的一致性)。
请注意CAP中的一致性和ACID中的一致性,虽然单词相同,但实际含义不同,请注意区分
上图中,商品信息的读写要满足一致性就是要实现如下目标:
分布式系统一致性的特点:
在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。在现代的互联网应用中,如果因为服务器宕机等问题,导致服务长期不可用,是不可接受的
上图中,商品信息读取满足可用性就是要实现如下目标:
分布式系统可用性的特点:
以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在 C 和 A 之间做出选择。
提高分区容忍性的办法就是一个数据项复制到多个节点上,那么出现分区之后,这一数据项仍然能在其他区中读取,容忍性就提高了。然而,把数据复制到多个节点,就会带来一致性的问题,就是多个节点上面的数据可能是不一致的。
通常分布式系统的各各结点部署在不同的子网,这就是网络分区,不可避免的会出现由于网络问题而导致结点之间 通信失败,此时仍可对外提供服务,这叫分区容忍性。
上图中,商品信息读写满足分区容忍性就是要实现如下目标:
分区容忍的含义:
如果要实现C则必须保证数据一致性,在数据同步的时候为防止向从数据库查询不一致的数据则需要将从数据库数 据锁定,待同步完成后解锁,如果同步失败从数据库要返回错误信息或超时信息。
如果要实现A则必须保证数据可用性,不管任何时候都可以向从数据查询数据,则不会响应超时或返回错误信息。 通过分析发现在满足P的前提下C和A存在矛盾性。
1)AP:
放弃一致性,追求分区容忍性和可用性。这是很多分布式系统设计时的选择。例如:
上边的商品管理,完全可以实现AP,前提是只要用户可以接受所查询的到数据在一定时间内不是最新的即可。
通常实现AP都会保证最终一致性,后面讲的BASE理论就是根据AP来扩展的,一些业务场景 比如:订单退款,今日退款成功,明日账户到账,只要用户可以接受在一定时间内到账即可。
2)CP:
放弃可用性,追求一致性和分区容错性,我们的zookeeper其实就是追求的强一致,又比如跨行转账,一次转账请 求要等待双方银行系统都完成整个事务才算完成。
3)CA:
放弃分区容忍性,即不进行分区(单体架构),不考虑由于网络不通或结点挂掉的问题,则可以实现一致性和可用性。那么系统 将不是一个标准的分布式系统,我们最常用的关系型数据就满足了CA。
CAP是一个已经被证实的理论:一个分布式系统最多只能同时满足 一致性(Consistency)、可用性(Availability)和分区容忍性(Partition tolerance)这三项中的两项。
它可以作为架构设计、技术选型的考量标准。对于多数大型互联网应用的场景,结点众多、部署分散,而且现在的 集群规模越来越大,所以节点故障、网络故障是常态,而且要保证服务可用性达到N个9(99.99…%),并要达到良好的响应性能来提高用户体验,因此一般都会做出如下选择:保证P和A,舍弃C强一致,保证最终一致性。
BASE:全称:Basically Available(基本可用)
,Soft state(软状态)
和 Eventually consistent(最终一致性)
三个短语的缩写,来自 ebay 的架构师提出。BASE 理论是对 CAP 中一致性和可用性权衡的结果,其来源于对大型互联网分布式实践的总结,是基于 CAP 定理逐步演化而来的。其核心思想是:
即使无法做到强一致性(Strong consistency),但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual consistency)。
通过牺牲强一致性来获得可用性,当出现故障允许部分不可用但要保证核心功能可用,允许数据在一段时间内是不一致的,但最终达到一致状态。满足BASE理论的事务,称之为“柔性事务”。
许多的NoSQL是按照BASE理论进行设计的,典型的例子包括:DynamoDB、Cassandra、CouchDB。
基本可用是指分布式系统在出现不可预知故障的时候,允许损失部分可用性——但请注意,这绝不等价于系统不可用。
分布式系统在出现故障时,允许损失部分可用功能,保证核心功能可用。如,电商网站交易付款出现问题了,商品依然可以正常浏览。
弱状态也称为软状态,和硬状态相对,是指允许系统中的数据存在中间状态,并认为该中间状态的存在不会影响系统的整体可用性,即允许系统在不同节点的数据副本之间进行数据同步的过程存在延时。
由于不要求强一致性,所以BASE允许系统中存在中间状态(也叫软状态),这个状态不影响系统可用性,如订单的"支付中"、“数据同步中”等状态,待数据最终一致后状态改为“成功”状态。
最终一致性强调的是系统中所有的数据副本,在经过一段时间的同步后,最终能够达到一个一致的状态。因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性。
经过一段时间后,所有节点数据都将会达到一致。如订单的"支付中"状态,最终会变 为“支付成功”或者"支付失败",使订单状态与实际交易结果达成一致,但需要一定时间的延迟、等待。
在计算机中部分关系数据库如Oracle、MySQL支持两阶段提交协议,如下图:
1.准备阶段(Prepare phase):事务管理器给每个参与者发送Prepare消息,每个数据库参与者在本地执行事务,并写本地的Undo/Redo日志,此时事务没有提交。
(Undo日志是记录修改前的数据,用于数据库回滚,Redo日志是记录修改后的数据,用于提交事务后写入数 据文件)
2.提交阶段(commit phase):如果事务管理器收到了参与者的执行失败或者超时消息时,直接给每个参与者发送回滚(Rollback)消息;否则,发送提交(Commit)消息;参与者根据事务管理器的指令执行提交或者回滚操 作,并释放事务处理过程中使用的锁资源。注意:必须在最后阶段释放锁资源。
下图展示了2PC的两个阶段,分成功和失败两个情况说明: 成功情况:
失败情况:
缺点
1、Redis默认不是以守护进程的方式运行,可以通过该配置项修改,使用yes启用守护进程
daemonize no
2、当Redis以守护进程方式运行时,Redis默认会把pid写入/var/run/redis.pid文件,可以通过pidfile指定
pidfile /var/run/redis.pid
3、指定Redis监听端口,默认端口为6379;’
port 6379
4、绑定的主机地址
bind 127.0.0.1
5、当客户端限制多长时间后关闭连接,如果指定为0,表示关闭该功能
timeout 300
6、指定日志记录几倍,Redis总共支持四个级别:debug,verbose,notice,warning,默认为verbose
loglevel verbos
7、日志记录方式,默认为标准输出,如果配置Redis为守护进程方式运行,而这里又配置日志记录方式标准输出,则日志将会发送给/dev/null
logfile stdout
8、设置数据库的数量,默认数据库为0,可以使用SELECT<dbid>命令在连接上指定数据库id
databases 16
9、指定在多长时间内,有多少次更新操作,就将数据同步到数据文件,可以多个条件配合
save<seconds><changes>
Redis默认配置文件中提供了三个条件
save 900 1
save 300 10
save 60 10000
分别表示900秒(15分钟)内有1个更改,300秒(5分钟)内有10个更改以及60秒内有10000个更改。
10、指定存储至本地数据库时是否压缩数据,默认为yes,Redis采用LZF(压缩算法)压缩,如果为了节省CPU时间,可以关闭该选项,但会导致数据库文件变的巨大
rdbcompression yes
11、指定本地数据库文件名,默认为dump.rdb
dbfilename dump.rdb
12、指定本地数据库存放目录
dir ./
13、设置当本机为slav服务时,设置master服务的IP地址及端口,在Redis启动时,它会自动从master进行数据同步slaveof<masterip> <masterport>
14、当master服务设置了密码保护时,slav服务连接master的密码
masterauth<master-password>
15、设置Redis连接密码,如果配置了连接密码,客户端在连接Redis是需要通过AUTH <password>命令提供密码,默认关闭
requirepass foobared
16、设置同一时间最大客户端连接数,默认是无限制,Redis可以同时打开的客户端连接数为Redis进程可以打开的最大文件描述符数,如果设置maxclients 0,表示不作限制。当客户端连接数到达限制是,Redis会关闭新的连接并向客户端返回max number of clients reached错误信息
maxclients 128
17、指定Redis最大内存限制,Redis在启动时会把数据加载到内存中,达到最大内存后,Redis会先尝试清除已到期或即将到期的Key,档次方法处理后,仍然达最大内存设置,将无法再进行写入操作,但仍然可以静心读取操作。Rdis新的vm机制,会把key存放内存,Value会存放在swap区
maxmemory <bytes>
18、指定是否每次更新操作后进行日志记录,Redis在默认情况下是异步的把数据写入磁盘,如果不开启,可能会在断电时导致一端时间内的数据丢失。因为 redis 本省同步数据文件是按上面save条件来同步的,所有的数据会在一端时间内只存在于内存中。默认为no
appendonly no
19、指定更新日志文件名,默认为appendonly.aof
appendfulename appendonly.aof
20、指定更新日志条件,共有3个可选值:
no: 表示等操作系统进行数据缓存同步到磁盘(快)
always: 表示每次更新操作后活动调用fsync()将数据写到磁盘(慢,安全)
everysec: 表示每秒同步一个(折中,默认值)
appendfsync everysec
21、指定是否启用虚拟内存机制,默认为no,简单的介绍一下,vm机制将数据分页存放,有Redis将访问量较少的页即冷数据swap到磁盘上,访问多的页面由磁盘自动换出到内存中(在后面的文章会仔细分析Redis的vm机制)
vm-enabled no
22、虚拟内存文件路径,默认值为/tmp/redis.swap,不可多个Redis实例共享
vm-swap-file /tmp/redis.swap
23、将所有大于vm-max-memory的数据存入虚拟内存,无论vm-max-memory设置多小,所有索引数据都是内存存储的(Redis的索引数据 就是keys),也就是说,当vm-max-memory设置为0的时候,其实是所有value都存在于磁盘。默认值为0
vm-page-size 32
24、Redis swap文件分成了很多的page,一个对象可以保存咱几多个page上面,但一个page上不能被多个对象共享,vm-page-size是要根据存储的 数据大小来设定的,作者建议如果村粗很多小对象,page大小最好设置为32或者64bytes;如果存储很大大对象,则可以使用更大的page,如果不确定,就是用默认值
vm-page-size 32
25、设置swap文件中的page数量,由于页表(一种表示页面空闲或是欧诺个的bitmap)是放在内存中的,在磁盘上每8个pages将消耗1byte的内存
vm-pages 134217728
26、设置访问swap文件的线程数,最好不要超过机器的核数,如果设置为0,那么所有对swap文件的操作都是串行的,可能会造成比较长时间的延迟。默认值为 4
vm-max-threads 4
27、设置在向客户端应答时,是否把较小的包含并未一个包发送,默认为开启
glueoutputbuf yes
28、指定在超过一定的数量或者最大的元素超过某一临界值时,采用一种特殊的哈希算法
hash-max-zipmap-entries 64
hash-max-zipmap-value 512
29、指定是否激活重置哈希。默认为开启(后面在介绍Redis的哈希算法时具体介绍)
activerehasing yes
30、指定包含其他的配置文件,可以在同一主机上多个redis实例之间使用同一份配置文件,而同时各个实例又拥有自己的特定配置文件
include /path/to/local.conf
31、maxmemory-policy noeviction # 内存达到上限之后的处理策略
1、volatile-lru : 只对设置了过期时间的key进行LRU(默认值)
2、allkeys-lru : 产出lru算法的key
3、volatile-random : 随机删除即将过期key
4、allkey -random : 随机删除
5、volatile-ttl : 删除即将过期的
6、noeviction : 永不过期,返回错误
1、 keys * :返回满足的所有键,可以模糊匹配,比如 keys abc* :表示以 abc 开头的 key,
通配符 :
* 代表所有
? 表示代表一个字符
2、 exists key : 是否存在指定的key ,存在返回1.不存在返回0
3、 expire key second :设置某个key的过期时间 时间为妙
4、 del key : 删除某个key
5、 ttl key :查看剩余时间,当key不存在是,返回-2;存在但没有设置剩余生存时间时,返回 -1,否则,以秒为单位,返回key 的剩余生存时间。
6、 persist key :取消过去时间
7、 PEXPIRE key millisseconds 修改key 的过期时间为毫秒
8、 select : 选择数据库 数据库为0-15(默认一共16个数据库)
9、 设计成多个数据库实际上是为了数据库安全和备份
10、 move key dbindex : 将当前数据中的key转移到其他数据库
11、 randomkey : 随机返回一个key
12、 rename key key2 : 种命名key
13、 echo : 打印命令
14、 dbsize : 查看数据库的key数量
15、 info : 查看数据库信息
16、 config get * 实时存储收到的请求,返回相关的配置
17、 flushdb : 清除当前数据库
18、 flushall : 清空所有数据库
19、 type key : 查看你的key是什么类型
20、 unlink key: 根据value选择非阻塞删除,仅将keys从keyspace元数据中删除,真正的删除会在后续异步操作。
21、 select: 命令切换数据库
key的命名建议:redis单个key允许存入512M大小
1、赋值语法:
SET KEY_NAME VALUE : (说明:多次设置name会覆盖)(Redis SET 命令用于设置给定 key 的值。如果 key 已经存储值,SET 就要写旧值,且无视类型)。
2、命令:
SETNX key1 value:(not exist) 如果key1不存在,则设置 并返回1。如果key1存在,则不设置并返回0;(解决分布式锁 方案之一,只有在key 不存在时设置 key 的值。setnx (SET if not exits)命令在指定的key不存在时,为key设置指定的值)。
SETEX key1 10 lx :(expired)设置key1的值为lx,过期时间为10秒,10秒后key1清除(key也清除)
SETEX key1 10 lx :(expired) 设置key1的值为lx,过期时间为10秒,10秒后key1清除(key 也清除)
SETRANG STRING range value : 替换字符串
3、取值语法:
GET KEY_NAME : Redis GET 命令用于获取指定 key 的值。如果 key 不存在,返回 nil。如果key存储的值不是字符串类型,返回一个错误。
GETRANGE key start end : 用于获取存储在指定key中字符串的子字符串。字符串的截取范围由 start 和 end 两个偏移量来决定(包括 start 和 end 在内)
GETBIT key offset :对 key 所存储的字符串值,获取指定偏移量上的为(bit);
GETTEST语法 : GETSET KEY_NAME VALUE : GETSET 命令用于设置指定 key 的值,并返回key的旧值。当key不存在是,返回 nil
STRLEN key :返回 key 所存储的字符串值的长度
4、删除语法:
DEL KEY_NAME : 删除指定的key,如果存在,返回数字类型。
5、批量写:
MSET K1 V1 K2 V2 ... (一次性写入多个值)
6、批量读:
MGET K1 K2 K3
7、GETSET NAME VALUE : 一次性设置和读取(返回旧值,写上新值)
8、自增/自减:
INCR KEY_Name : Incr 命令将key中存储的数组值增1。如果 key 不存在,那么key的值会先被初始化为0,然后在执行INCR操作
自增: INCRBY KEY_Name : 增量值Incrby 命令将key中存储的数字加上指定的增量值
自减: DECR KEY_Name 或 DECYBY KEY_NAME 减值:DECR 命令将key中存储的数字减少1
(注意这些key对应的必须是数字类型字符串,否则会出错。)
9、字符串拼接:APPEND KEY_NAME VALUE ,Append 命令用于为指定的key追加至末尾,如果不存在,为其赋值
10、setex (set with expire) #设置过期时间
11、setnx (set if not exist) #不存在设置 在分布式锁中会常常使用!
一、赋值语法:
1、 HSET KEY FIELD VALUE : 为指定的KEY,设定FILD/VALUE
2、 HMSET KEY FIELD VALUE [FIELD1,VALUE]... : 同时将多个 field-value(域-值)对设置到哈希表key中。
二、取值语法:
HGET KEY FIELD :获取存储在HASH中的值,根据FIELD得到VALUE
HMGET KEY FIELD [FIELD1] :获取key所有给定字段的值
HGETALL KEY :返回HASH表中所有的字段和值
HKEYS KEY : 获取所有哈希表中的字段
HLEN KEY : 获取哈希表中字段的数量
三、删除语法:
HDEL KEY FIELD[FIELD2] :删除一个或多个HASH表字段
四、其它语法:
HSETNX KEY FIELD VALUE : 只有在字段field 不存在时,设置哈希表字段的值
HINCRBY KEY FIELD INCREMENT :为哈希key中的指定字段的整数值加上增量 increment。
HINCRBYFLOAT KEY FIELD INCREMENT :为哈希表key 中的指定字段的浮点数值加上增量 increment
HEXISTS KEY FIELD : 查看哈希表中key中,指定的字段是否存在
1、赋值语法:
LPUSH KEY VALUE1 [VALUE2] :将一个或多个值插入到列表头部(从左侧添加)
RPUSH KEY VALUE1 [VALUE2] :在列表中添加一个或多个值(从有侧添加)
LPUSHX KEY VAKUE :将一个值插入到已存在的列表头部。如果列表不在,操作无效
RPUSHX KEY VALUE :一个值插入已经在的列表尾部(最右边)。如果列表不在,操作无效
2、取值语法:
LLEN KEY :获取列表长度
LINDEX KEY INDEX :通过索引获取列表中的元素
LRANGE KEY START STOP :获取列表指定范围内的元素 描述:返回列表中指定区间的元素,区间以偏移量START和END指定。其中0表示列表的第一个元素,1表示列表的第二个元素,以此类推。。。也可以使用负数下标,以-1表示列表的最后一个元素,-2表示列表的倒数第二个元素,一次类推。。。
3、删除语法:
LPOP KEY :移除并获取列表的第一个元素(从左侧删除)
RPOP KEY :移除列表的最后一个元素,返回值为移除的元素(从右侧删除)
BLPOP key1 [key2]timeout :移除并获取列表的第一个元素,如果列表没有元素会阻塞列表知道等待超时或发现可弹出元素为止。
4、修改语法:
LSET KEY INDEX VALUE :通过索引设置列表元素的值
LINSERT KEY BEFORE|AFTER WORIL VALUE :在列表的元素前或者后 插入元素 描述:将值 value 插入到列表key当中,位于值world之前或之后。
5、RPOPLPUSH source destiation : 移除列表的最后一个元素,并将该元素添加到另外一个列表并返回
示例描述:
RPOPLPUSH a1 a2 : a1的最后元素移到a2的左侧
RPOPLPUSH a1 a1 : 循环列表,将最后元素移到最左侧
BRPOPLPUSH source destination timeout :从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它;如果列表没有元素会阻塞列表知道等待超时或发现可弹出的元素为止。
1、复制语法:
SADD KEY member1 [member2]:向集合添加一个或多个成员
2、取值语法:
SCARD KEY :获取集合的成员数
SMEMBERS KEY :返回集合中的所有成员
SISMEMBER KEY MEMBER :判断member元素是否是集合key的成员(开发中:验证是否存在判断)
SRANDMEMBER KEY [COUNT] :返回集合中一个或对个随机数
3、删除语法:
SREM key member1 [member2] : 移除集合中一个或多个成员
SPOP key [count] : 移除并返回集合中的一个随机元素
SMOVE source destination member :将member 元素从Source集合移动到destination集合中
4、差集语言:
SDIFF key1 [key2] :返回给定所有集合的差集
SDIFFSTORE destination key1 [key2] :返回给定所有集合的茶几并存储在destination中
5、交集语言:
SUNION key1 [key2] : 返回所有给定集合的并集
SUNIONSTORE destination key1 [key2] :所有给定集合的并集存储在 destinatiion集合中
1、复制语法:
ZADD KEY score1 member1 【score2 member2】 :向有序集合添加一个或多个成员,或者更新已经存在成员的分数
2、取值语法:
ZCARD key :获取有序结合的成员数
ZCOUNT key min max :计算在有序结合中指定区间分数的成员数
127.0.0.1:6379> ZADD kim 1 tian
(integer) 0
127.0.0.1:6379> zadd kim 2 yuan 3 xing
(integer) 2
127.0.0.1:6379> zcount kim 1 2
(integer) 2
127.0.0.1:6379>
ZRANK key member :返回有序集合中指定成员的所有
ZRANGE KEY START STOP [WITHSCORES]:通过索引区间返回有序集合成指定区间内的成员(低到高)
ZRANGEBYSCORE KEY MIN MAX [WITHSCORES] [LIMIT] :通过分数返回有序集合指定区间内的成员
ZREVRANGE KEY START STOP [WITHSCORES] :返回有序集中是定区间内的成员,通过索引,分数从高到底
ZREVERANGEBYSCORE KEY MAX MIN [WITHSCORES] :返回有序集中指定分数区间的成员,分数从高到低排序
3、删除语法:
DEL KEY : 移除集合
ZREM key member [member...] 移除有序集合中的一个或多个成员
ZREMRANGEBYSCORE KEY MIN MAX :移除有序集合中给定的分数区间的所有成员。
ZREMRANGEBYSCORE KEY MIN MAX :移除有序集合中给定的分数区间的所有成员。
ZINCRBY KEY INCREMENT MEMBER :增加member元素的分数increment,返回值是更改后的分数