Redis跳跃表

跳跃表

  • 跳跃表(skiplist)是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快捷访问节点的目的。

    在大多数情况先,跳跃表的效率可以与平衡树媲美,并且因为跳跃表的实现比平衡树更为简单,所以有不少程序都使用跳跃表来代替平衡树。

    Redis使用跳跃表作为有序集合键的底层实现之一。

    I、跳跃表实现

    Redis的跳跃表由zskiplistNode和zskiplist两个结构定义,其中zskiplistNode结构用于表示跳跃表节点,而zskiplist结构用于保存跳跃表节点的相关信息(如节点数量,表头指针,表尾指针等)。

    下图为一个跳跃表示意图:

![1561695969873](Redis跳跃表/1561695969873.png)



位于最左边的是zskiplist结构,该结构包含以下属性:
 ·header:指向跳跃表的表头节点。
 ·tail:指向跳跃表的表尾节点。
 ·level:表示目前跳跃表内,层数最大的节点的层数。
 ·length:记录跳跃表长度,即跳跃表包含的节点数量(不包含头结点)。

位于zskiplist结构右方的是四个zskiplistNode结构,其属性如下:
 ·层:用L1、L2、L3等表示节点的各个层,每个层都有两个属性:**前进指针**和**跨度**。前进指针用于访问位于表尾方向的其他节点,跨度记录了前进指针指向节点和当前节点的距离。当程序从表头向表尾进行遍历时,访问会沿着层的前进指针进行。
 ·**后退指针**:节点中用BW表示后退指针,后退指针在程序从表尾向表头遍历时使用。
 · 分值:各个节点中的1.0、2.0是节点保存的分值。**在跳跃表中,节点按照各自所保存的分值从小到大排列**。
 · 成员对象: 各个节点中o1、o2是节点保存的成员对象。

值的注意的是:表头节点和其他节点是不一样的,表头节点也有后退指针、分值和成员对象,但是表头节点的这些属性都不会被用到,所以图中忽略了这一部分。

##### 1.1 跳跃表节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
typedef struct zskiplistNode {

// 后退指针
struct zskiplistNode *backward;

// 分值
double score;

// 成员对象
robj *obj;

// 层
struct zskiplistLevel {

// 前进指针
struct zskiplistNode *forward;

// 跨度
unsigned int span;

} level[];

} zskiplistNode;
**层:** 每次创建一个新跳跃表节点的时候,程序都会**随机生成一个介于1和32之间的值作为level数组的大小**,这个大小就是层的“高度”。
  • 1561696052378

前进指针:
每个层都有一个指向表尾方向的前进指针。下图用虚线表示了从表头遍历到表尾的路径:

1561696088361

跨度:
层的跨度用于记录两个节点之间的距离:
初看上去,很容易以为跨度与遍历操作有关,但其实跨度是用来计算排位的,在查找某个节点的过程中,将沿途访问过的所有层的跨度累积起来,得到的结果就是目标节点在跳跃表中的排位。

后退指针:
与每个节点有多个前进指针不同,每个节点只有一个后退指针,所以每次只能后退至前一个节点。

分值和成员:
节点的分值用来对所有节点从小到大排序。
节点的成员是一个指针,其指向一个字符串对象,而字符串对象保存着一个SDS值。

在同一个跳跃表中,各个节点保存的成员对象必须是唯一的,但是多个节点保存的分值却可以是相同的:分值相同的节点将按照成员对象在字典序中的大小进行排序。

1.2 跳跃表

Redis使用一个zskiplist结构来持有zskiplistNode节点,程序可以更方便的对整个跳跃表进行处理,比如,快速访问表头节点和表尾节点,或者快速获取表中节点数量。

1
2
3
4
5
6
7
8
9
10
11
12
 typedef struct zskiplist {

// 表头节点和表尾节点
struct zskiplistNode *header, *tail;

// 表中节点的数量
unsigned long length;

// 表中层数最大的节点的层数
int level;

} zskiplist;

II、跳跃表API

1561696136110

一天一个Linux命令(whoami命令)

语法

whoami(选项)

参数

1
2
--help:在线帮助;
--version:显示版本信息。

3、实例

whoami显示当前用户名, whoami –version 显示当前Linux内核

[fenglangjuxu@localhost ~]$ 
[fenglangjuxu@localhost ~]$ 
[fenglangjuxu@localhost ~]$ whoami
fenglangjuxu
[fenglangjuxu@localhost ~]$ whoami version
whoami: 额外的操作数 "version"
Try 'whoami --help' for more information.
[fenglangjuxu@localhost ~]$ whoami --version
whoami (GNU coreutils) 8.22
Copyright (C) 2013 Free Software Foundation, Inc.
许可证:GPLv3+:GNU 通用公共许可证第3 版或更新版本<http://gnu.org/licenses/gpl.html>。
本软件是自由软件:您可以自由修改和重新发布它。
在法律范围内没有其他保证。

由Richard Mlynarik 编写。
[fenglangjuxu@localhost ~]$ 

1561641280242

一天一个Linux命令(watch命令)

watch命令

  • watch命令以周期性的方式执行给定的指令,指令输出以全屏方式显示。watch是一个非常实用的命令,基本所有的Linux发行版都带有这个小工具,如同名字一样,watch可以帮你监测一个命令的运行结果,省得你一遍遍的手动运行。

语法

watch(选项)(参数)

选项:

1
2
3
-n:指定指令执行的间隔时间(秒);
-d:高亮显示指令输出信息不同之处;
-t:不显示标题。

参数:

指令:需要周期性执行的指令。

实例

uptime命令用来显示系统运行时间信息, 而watch uptime命令执行后会展示uptime信息:

当前时间 系统连续运行时间 当前用户连接数 系统平均负载(最近2分钟、9分钟、XX分钟)

1
2
3
4
5
6
7
8
9
10
[fenglangjuxu@localhost ~]$ 
[fenglangjuxu@localhost ~]$
[fenglangjuxu@localhost ~]$ watch uptime
[fenglangjuxu@localhost ~]$



Every 2.0s: uptime Thu Jun 27 07:26:42 2019

07:26:42 up 34 min, 2 users, load average: 0.25, 0.19, 0.47

一天一个Linux命令(第6天touch命令)

touch命令

  • touch命令有两个功能:一是用于把已存在文件的时间标签更新为系统当前的时间(默认方式),它们的数据将原封不动地保留下来;二是用来创建新的空文件。

    当然了touch主要的功能是用来创建空文件,至少我常用来这么做。

语法

touch(选项)(参数)

选项:

-a:或--time=atime或--time=access或--time=use  只更改存取时间;
-c:或--no-create  不建立任何文件;
-d:<时间日期> 使用指定的日期时间,而非现在的时间;
-f:此参数将忽略不予处理,仅负责解决BSD版本touch指令的兼容性问题;
-m:或--time=mtime或--time=modify  只更该变动时间;
-r:<参考文件或目录>  把指定文件或目录的日期时间,统统设成和参考文件或目录的日期时间相同;
-t:<日期时间>  使用指定的日期时间,而非现在的时间;
--help:在线帮助;
--version:显示版本信息。

实例

创建文件夹dongpengju,可以看到文件夹大小为0,是为空的:

[fenglangjuxu@localhost usr]$ touch dongpengju
touch: 无法创建"dongpengju": 权限不够
[fenglangjuxu@localhost usr]$ su root
密码:
[root@localhost usr]# pwd
/usr
[root@localhost usr]# touch dongpengju
[root@localhost usr]# ls
bin         etc    include  lib64    local  share  tmp
dongpengju  games  lib      libexec  sbin   src
[root@localhost usr]# ll
总用量 264
dr-xr-xr-x.   2 root root 49152 6月  22 04:37 bin
-rw-r--r--.   1 root root     0 6月  25 06:55 dongpengju
drwxr-xr-x.   2 root root     6 4月  11 2018 etc
drwxr-xr-x.   2 root root     6 4月  11 2018 games
drwxr-xr-x.   9 root root  4096 6月  22 04:34 include
dr-xr-xr-x.  42 root root  4096 6月  22 04:36 lib
dr-xr-xr-x. 142 root root 81920 6月  22 04:39 lib64
drwxr-xr-x.  49 root root 12288 6月  22 04:36 libexec
drwxr-xr-x.  12 root root   131 6月  22 04:07 local
dr-xr-xr-x.   2 root root 20480 6月  22 04:37 sbin
drwxr-xr-x. 235 root root  8192 6月  22 04:37 share
drwxr-xr-x.   4 root root    34 6月  22 04:07 src
lrwxrwxrwx.   1 root root    10 6月  22 04:07 tmp -> ../var/tmp
[root@localhost usr]# 

一天一个Linux命令(第12天which命令)

which命令

  • which命令用于查找并显示给定命令的绝对路径,环境变量PATH中保存了查找命令时需要遍历的目录。which指令会在环境变量$PATH设置的目录里查找符合条件的文件。也就是说,使用which命令,就可以看到某个系统命令是否存在,以及执行的到底是哪一个位置的命令。

语法:

which(选项)(参数)

选项:

1
2
3
4
-n<文件名长度>:制定文件名长度,指定的长度必须大于或等于所有文件中最长的文件名;
-p<文件名长度>:与-n参数相同,但此处的<文件名长度>包含了文件的路径;
-w:指定输出时栏位的宽度;
-V:显示版本信息。

参数

指令名:指令名列表。

实例:

查找文件、显示命令路径:

zhiruo@luoyang MINGW64 /e/zhiruoblog
$

zhiruo@luoyang MINGW64 /e/zhiruoblog
$ which ls
/usr/bin/ls

zhiruo@luoyang MINGW64 /e/zhiruoblog
$ which chmod
/usr/bin/chmod

zhiruo@luoyang MINGW64 /e/zhiruoblog
$ which pwd
/usr/bin/pwd

如图:

1561124360712

说明:which是根据使用者所配置的 PATH 变量内的目录去搜寻可运行档的!所以,不同的 PATH 配置内容所找到的命令当然不一样的!

Event-Driven Data Management for Microservices 用于微服务的事件驱动数据管理

Editor – This seven‑part series of articles is now complete:

编辑-此七部分系列文章现已完成:

  1. Introduction to Microservices 微服务构建微服务简介:
  2. Building Microservices: Using an API Gateway 使用api网关构建微服务
  3. Building Microservices: Inter-Process Communication in a Microservices Architecture 微服务体系结构中的进程间通信
  4. Service Discovery in a Microservices Architecture 微服务体系结构中的服务发现
  5. Event‑Driven Data Management for Microservices (this article) 事件驱动的微服务数据管理(本文)
  6. Choosing a Microservices Deployment Strategy 选择一种微服务部署策略
  7. Refactoring a Monolith into Microservices 将单个微服务重构为微服务。

You can also download the complete set of articles, plus information about implementing microservices using NGINX Plus, as an ebook – Microservices: From Design to Deployment. And see our series on the Microservices Reference Architecture and the Microservices Solutions page.

您还可以下载完整的文章集,以及有关使用nginx+实现微服务的信息,作为电子书-microservices:从设计到部署。并在微服务参考体系结构和微服务解决方案页面上查看我们的系列文章。

This is the fifth article in a series about building applications with microservices. The first article introduces the Microservices Architecture pattern and discusses the benefits and drawbacks of using microservices. The second and third articles in the series describe different aspects of communication within a microservices architecture. The fourth article explores the closely related problem of service discovery. In this article, we change gears and look at the distributed data management problems that arise in a microservices architecture.

这是关于使用微服务构建应用程序的系列文章的第五篇。第一篇文章介绍了微服务体系结构模式,讨论了使用微服务的优缺点。本系列的第二和第三篇文章描述了微服务体系结构中通信的不同方面。第四篇文章探讨了与服务发现密切相关的问题。在本文中,我们改变了方向,并研究了微服务体系结构中出现的分布式数据管理问题。

Microservices and the Problem of Distributed Data Management 微服务与分布式数据管理问题

A monolithic application typically has a single relational database. A key benefit of using a relational database is that your application can use ACID transactions, which provide some important guarantees:

单块应用程序通常有一个关系数据库。使用关系数据库的一个主要好处是应用程序可以使用ACID事务,这提供了一些重要的保证:

  • Atomicity – Changes are made atomically 原子性-原子化改变
  • Consistency – The state of the database is always consistent 一致性-数据库的状态始终一致
  • Isolation – Even though transactions are executed concurrently it appears they are executed serially 隔离-即使事务是并发执行的,它们似乎都是串行执行的。
  • Durability – Once a transaction has committed it is not undone 持久性-事务一旦提交,就不会撤消。

As a result, your application can simply begin a transaction, change (insert, update, and delete) multiple rows, and commit the transaction.

因此,您的应用程序可以简单地开始事务、更改(插入、更新和删除)多行,并提交事务

Another great benefit of using a relational database is that it provides SQL, which is a rich, declarative, and standardized query language. You can easily write a query that combines data from multiple tables. The RDBMS query planner then determines the most optimal way to execute the query. You don’t have to worry about low‑level details such as how to access the database. And, because all of your application’s data is in one database, it is easy to query.

使用关系数据库的另一个好处是它提供了SQL,它是一个丰富的、声明性的和标准化的查询语言。您可以轻松编写将数据与多个表组合的查询。RDBMS查询规划器然后确定执行查询的最佳方式。您不必担心低级详细信息,例如如何访问数据库。而且,由于所有应用程序的数据都在一个数据库中,因此很容易查询。

Unfortunately, data access becomes much more complex when we move to a microservices architecture. That is because the data owned by each microservice is private to that microservice and can only be accessed via its API. Encapsulating the data ensures that the microservices are loosely coupled and can evolve independently of one another. If multiple services access the same data, schema updates require time‑consuming, coordinated updates to all of the services.

不幸的是,当我们转向微服务体系结构时,数据访问变得更加复杂。这是因为每个微服务拥有的数据都是该微服务的私有数据,并且只能通过其API访问。封装数据可以确保微服务是松散耦合的,并且可以相互独立地进化。如果多个服务访问相同的数据,架构更新需要对所有服务进行耗时、协调的更新。

To make matters worse, different microservices often use different kinds of databases. Modern applications store and process diverse kinds of data and a relational database is not always the best choice. For some use cases, a particular NoSQL database might have a more convenient data model and offer much better performance and scalability. For example, it makes sense for a service that stores and queries text to use a text search engine such as Elasticsearch. Similarly, a service that stores social graph data should probably use a graph database, such as Neo4j. Consequently, microservices‑based applications often use a mixture of SQL and NoSQL databases, the so‑called polyglot persistence approach.

更糟糕的是,不同的微服务经常使用不同类型的数据库。现代应用程序存储和处理各种数据和关系数据库并不总是最佳选择。对于某些用例,特定的NoSQL数据库可能具有更方便的数据模型,并提供更好的性能和可伸缩性。例如,存储和查询文本的服务使用文本搜索引擎(如ElasticSearch)是有意义的。类似地,存储社交图形数据的服务可能应该使用图形数据库,如ne4j。因此,基于微服务的应用程序通常使用SQL和NoSQL数据库的混合,即所谓的Polyglot持久化方法。

A partitioned, polyglot‑persistent architecture for data storage has many benefits, including loosely coupled services and better performance and scalability. However, it does introduce some distributed data management challenges.

用于数据存储的分区的、多标记的持久性体系结构有许多好处,包括松散耦合的服务以及更好的性能和可伸缩性。然而,它确实带来了一些分布式数据管理方面的挑战。

The first challenge is how to implement business transactions that maintain consistency across multiple services. To see why this is a problem, let’s take a look at an example of an online B2B store. The Customer Service maintains information about customers, including their credit lines. The Order Service manages orders and must verify that a new order doesn’t exceed the customer’s credit limit. In the monolithic version of this application, the Order Service can simply use an ACID transaction to check the available credit and create the order.

第一个挑战是如何实现跨多个服务保持一致性的业务事务。为了了解为什么这是一个问题,让我们来看看一个在线B2B商店的例子。客户服务维护有关客户的信息,包括他们的信用额度。订单服务管理订单,并必须验证新订单不超过客户的信用限额。在这个应用程序的单块版本中,订单服务可以简单地使用ACID事务来检查可用的信用并创建订单。

In contrast, in a microservices architecture the ORDER and CUSTOMER tables are private to their respective services, as shown in the following diagram.

相反,在微服务体系结构中,Order表和Customer表对于各自的服务是私有的,如下图所示。

Each service in a microservices architecture maintains a private database table

The Order Service cannot access the CUSTOMER table directly. It can only use the API provided by the Customer Service. The Order Service could potentially use distributed transactions, also known as two‑phase commit (2PC). However, 2PC is usually not a viable option in modern applications. The CAP theorem requires you to choose between availability and ACID‑style consistency, and availability is usually the better choice. Moreover, many modern technologies, such as most NoSQL databases, do not support 2PC. Maintaining data consistency across services and databases is essential, so we need another solution.

订单服务无法直接访问客户表。它只能使用客户服务提供的API。订单服务可能会使用分布式事务处理,也称为两相提交(2PC)。然而,在现代应用中,2PC通常不是可行的选择。CAP定理要求您在可用性和酸样式一致性之间进行选择,可用性通常是更好的选择。此外,许多现代技术(如大多数NOSQL数据库)不支持2PC。保持跨服务和数据库的数据一致性是至关重要的,因此我们需要另一个解决方案。

The second challenge is how to implement queries that retrieve data from multiple services. For example, let’s imagine that the application needs to display a customer and his recent orders. If the Order Service provides an API for retrieving a customer’s orders then you can retrieve this data using an application‑side join. The application retrieves the customer from the Customer Service and the customer’s orders from the Order Service. Suppose, however, that the Order Service only supports the lookup of orders by their primary key (perhaps it uses a NoSQL database that only supports primary key‑based retrievals). In this situation, there is no obvious way to retrieve the needed data.

第二个挑战是如何实现从多个服务检索数据的查询。例如,假设应用程序需要显示客户和他最近的订单。如果订单服务提供了用于检索客户订单的API,那么您可以使用应用程序端连接来检索这些数据。应用程序从客户服务中检索客户,从订单服务检索客户的订单。但是,假设Order服务只支持通过主键查找订单(也许它使用的是只支持基于主键的检索的NoSQL数据库)。在这种情况下,没有明显的方法来检索所需的数据

Event‑Driven Architecture 事件驱动体系结构

For many applications, the solution is to use an event‑driven architecture. In this architecture, a microservice publishes an event when something notable happens, such as when it updates a business entity. Other microservices subscribe to those events. When a microservice receives an event it can update its own business entities, which might lead to more events being published.

对于许多应用程序,解决方案是使用事件驱动的体系结构。在此体系结构中,微服务在发生一些值得注意的事情时发布事件,例如更新业务实体时。其他微服务订阅了这些事件。当微服务接收到事件时,它可以更新自己的业务实体,这可能会导致更多事件被发布。

You can use events to implement business transactions that span multiple services. A transaction consists of a series of steps. Each step consists of a microservice updating a business entity and publishing an event that triggers the next step. The following sequence of diagrams shows how you can use an event‑driven approach to checking for available credit when creating an order. The microservices exchange events via a Message Broker.

您可以使用事件来实现跨多个服务的业务事务。事务由一系列步骤组成。每个步骤都包含一个微服务,用于更新业务实体并发布触发下一步的事件。以下图表序列显示如何在创建订单时使用事件驱动方法检查可用信用。微服务通过消息代理交换事件。

  1. The Order Service creates an Order with status NEW and publishes an Order Created event. Order服务创建状态为New的订单,并发布订单创建事件。

    In step 1 of a credit check in a microservices architecture, the Order Service publishes an 'Order Created' event

  2. The Customer Service consumes the Order Created event, reserves credit for the order, and publishes a Credit Reserved event. 客户服务使用订单创建事件,为订单保留信用,并发布信用保留事件。

    In a microservices architecture, the second step in a credit check is for the Customer Service to generate a 'Credit Reserved' event

  3. The Order Service consumes the Credit Reserved event, and changes the status of the order to OPEN. 订单服务使用信用保留事件,并将订单的状态更改为打开。

    In a microservices architecture, the third step in a credit check is for the Order Service to set the order status to 'Open'

A more complex scenario could involve additional steps, such as reserving inventory at the same time the customer’s credit is checked.

更复杂的场景可能涉及其他步骤,例如在检查客户信用时保留库存。

Provided that (a) each service atomically updates the database and publishes an event – more on that later – and (b) the Message Broker guarantees that events are delivered at least once, then you can implement business transactions that span multiple services. It is important to note that these are not ACID transactions. They offer much weaker guarantees such as eventual consistency. This transaction model has been referred to as the BASE model.

只要(A)每个服务原子地更新数据库并发布一个事件(稍后会有更多的消息)和(B)MessageBroker保证事件至少交付一次,那么您就可以实现跨多个服务的业务事务。必须指出的是,这些不是ACID交易。它们提供的担保要弱得多,比如最终的一致性。这个事务模型被称为基本模型。

You can also use events to maintain materialized views that pre‑join data owned by multiple microservices. The service that maintains the view subscribes to the relevant events and updates the view. For example, the Customer Order View Updater Service that maintains a Customer Orders view subscribes to the events published by the Customer Service and Order Service.

您还可以使用事件来维护由多个微服务拥有的预联接数据的物化视图。维护视图的服务订阅相关事件并更新视图。例如,维护CustomerOrders视图的CustomerOrder视图更新服务订阅由客户服务和订单服务发布的事件。

In a microservices architecture, a service can subscribe to event notifications published by other services as triggers for action

When the Customer Order View Updater Service receives a Customer or Order event, it updates the Customer Order View datastore. You could implement the Customer Order View using a document database such as MongoDB and store one document for each Customer. The Customer Order View Query Service handles requests for a customer and recent orders by querying the Customer Order View datastore.

当客户订单视图更新服务接收到客户或订单事件时,它将更新客户订单视图数据存储。您可以使用文档数据库(如MongoDB)实现CustomerOrder视图,并为每个客户存储一个文档。客户订单视图查询服务通过查询Customer Order视图数据存储来处理对客户和最近订单的请求。

An event‑driven architecture has several benefits and drawbacks. It enables the implementation of transactions that span multiple services and provide eventual consistency. Another benefit is that it also enables an application to maintain materialized views. One drawback is that the programming model is more complex than when using ACID transactions. Often you must implement compensating transactions to recover from application‑level failures; for example, you must cancel an order if the credit check fails. Also, applications must deal with inconsistent data. That is because changes made by in‑flight transactions are visible. The application can also see inconsistencies if it reads from a materialized view that is not yet updated. Another drawback is that subscribers must detect and ignore duplicate events.

事件驱动的体系结构有几个优点和缺点。它实现跨多个服务并提供最终一致性的事务。另一个优点是它还使应用程序能够维护实例化视图。一个缺点是编程模型比使用酸事务时更复杂。通常,您必须实施补偿事务处理才能从应用程序级故障中恢复;例如,如果信用检查失败,则必须取消订单。此外,应用程序必须处理不一致的数据。这是因为飞行中事务所做的更改是可见的。如果应用程序从尚未更新的实体化视图中读取,也可以看到不一致。另一个缺点是订户必须检测和忽略重复事件。

Achieving Atomicity 实现原子性

In an event‑driven architecture there is also the problem of atomically updating the database and publishing an event. For example, the Order Service must insert a row into the ORDER table and publish an Order Created event. It is essential that these two operations are done atomically. If the service crashes after updating the database but before publishing the event, the system becomes inconsistent. The standard way to ensure atomicity is to use a distributed transaction involving the database and the Message Broker. However, for the reasons described above, such as the CAP theorem, this is exactly what we do not want to do.

在事件驱动的体系结构中,也存在着原子更新数据库和发布事件的问题。例如,Order服务必须向Order表中插入一行并发布创建的订单事件。这两个操作必须以原子方式完成。如果服务在更新数据库后但在发布事件之前崩溃,则系统将变得不一致。确保原子性的标准方法是使用涉及数据库和消息代理的分布式事务。然而,由于上述原因,如上限定理,这正是我们不想做的。

Publishing Events Using Local Transactions 使用本地事务发布事件

One way to achieve atomicity is for the application to publish events using a multi‑step process involving only local transactions. The trick is to have an EVENT table, which functions as a message queue, in the database that stores the state of the business entities. The application begins a (local) database transaction, updates the state of the business entities, inserts an event into the EVENT table, and commits the transaction. A separate application thread or process queries the EVENT table, publishes the events to the Message Broker, and then uses a local transaction to mark the events as published. The following diagram shows the design.

实现原子性的一种方法是应用程序使用只涉及本地事务的多步骤进程发布事件。关键是在存储业务实体状态的数据库中具有一个作为消息队列的事件表。应用程序开始(本地)数据库事务,更新业务实体的状态,将事件插入到事件表中,并提交该事务。单独的应用程序线程或进程查询事件表,将事件发布到消息代理,然后使用本地事务将事件标记为已发布。下图显示了设计。

In a microservices architecture, achieve atomicity by using only local transactions to publish events

The Order Service inserts a row into the ORDER table and inserts an Order Created event into the EVENT table. The Event Publisher thread or process queries the EVENT table for unpublished events, publishes the events, and then updates the EVENT table to mark the events as published.

Order服务将一行插入Order表,并将Order创建的事件插入事件表。事件发布线程或进程查询未发布事件的事件表,发布事件,然后更新事件表,将事件标记为已发布的事件。

This approach has several benefits and drawbacks. One benefit is that it guarantees an event is published for each update without relying on 2PC. Also, the application publishes business‑level events, which eliminates the need to infer them. One drawback of this approach is that it is potentially error‑prone since the developer must remember to publish events. A limitation of this approach is that it is challenging to implement when using some NoSQL databases because of their limited transaction and query capabilities.

这种方法有几个优点和缺点。一个好处是,它可以保证为每个更新发布一个事件,而不依赖于2pc。此外,应用程序发布业务级别的事件,这消除了推断它们的需要。这种方法的一个缺点是它可能容易出错,因为开发人员必须记住发布事件。这种方法的一个限制是在使用某些NoSQL数据库时很难实现,因为它们的事务和查询功能有限。

This approach eliminates the need for 2PC by having the application use local transactions to update state and publish events. Let’s now look at an approach that achieves atomicity by having the application simply update state.

这种方法通过让应用程序使用本地事务来更新状态和发布事件,从而消除了对2PC的需求。现在让我们来看看一种通过让应用程序简单地更新状态来实现原子性的方法。

Mining a Database Transaction Log 挖掘数据库事务日志

Another way to achieve atomicity without 2PC is for the events to be published by a thread or process that mines the database’s transaction or commit log. The application updates the database, which results in changes being recorded in the database’s transaction log. The Transaction Log Miner thread or process reads the transaction log and publishes events to the Message Broker. The following diagram shows the design.

另一种在没有2PC的情况下实现原子性的方法是,事件由挖掘数据库事务或提交日志的线程或进程发布。应用程序更新数据库,从而在数据库的事务日志中记录更改。事务日志挖掘程序线程或进程读取事务日志并将事件发布到消息代理。下图显示了设计。

In a microservices architecture, achieve atomicity by mining the transaction log for events

A example of this approach is the open source LinkedIn Databus project. Databus mines the Oracle transaction log and publishes events corresponding to the changes. LinkedIn uses Databus to keep various derived data stores consistent with the system of record.

此方法的一个示例是开源LinkedInDatabus项目。数据库挖掘Oracle事务日志并发布与更改相对应的事件。LinkedIn使用数据库来保存与记录系统一致的各种衍生数据存储。

Another example is the streams mechanism in AWS DynamoDB, which is a managed NoSQL database. A DynamoDB stream contains the time‑ordered sequence of changes (create, update, and delete operations) made to the items in a DynamoDB table in the last 24 hours. An application can read those changes from the stream and, for example, publish them as events.

另一个例子是AWS DynamoDB中的Streams机制,它是一个托管的NoSQL数据库。DynamoDB流包含过去24小时内对DynamoDB表中的项进行的按时间顺序的更改序列(创建、更新和删除操作)。应用程序可以从流中读取这些更改,例如,将它们发布为事件。

Transaction log mining has various benefits and drawbacks. One benefit is that it guarantees that an event is published for each update without using 2PC. Transaction log mining can also simplify the application by separating event publishing from the application’s business logic. A major drawback is that the format of the transaction log is proprietary to each database and can even change between database versions. Also, it can be difficult to reverse engineer the high‑level business events from the low‑level updates recorded in the transaction log.

事务日志挖掘有不同的优缺点。一个好处是它可以保证在不使用2pc的情况下为每个更新发布一个事件。事务日志挖掘还可以通过将事件发布与应用程序的业务逻辑分离来简化应用程序。一个主要的缺点是事务日志的格式对每个数据库都是专有的,甚至可以在不同的数据库版本之间进行更改。此外,很难从事务日志中记录的低级更新中反向工程高级业务事件。

Transaction log mining eliminates the need for 2PC by having the application do one thing: update the database. Let’s now look at a different approach that eliminates the updates and relies solely on events.

事务日志挖掘通过让应用程序执行以下操作消除了2pc的需求:更新数据库。现在,让我们看看消除更新并仅依赖于事件的其他方法。

Using Event Sourcing 使用事件源

Event sourcing achieves atomicity without 2PC by using a radically different, event‑centric approach to persisting business entities. Rather than store the current state of an entity, the application stores a sequence of state‑changing events. The application reconstructs an entity’s current state by replaying the events. Whenever the state of a business entity changes, a new event is appended to the list of events. Since saving an event is a single operation, it is inherently atomic.

通过使用一种完全不同的、以事件为中心的方法来持久化业务实体,事件源可以在没有2PC的情况下实现原子性。应用程序不是存储实体的当前状态,而是存储一系列状态更改事件。应用程序通过重放事件重新构造实体的当前状态。每当业务实体的状态发生变化时,都会将新事件追加到事件列表中。因为保存一个事件是一个单一的操作,所以它本质上是原子的。

To see how event sourcing works, consider the Order entity as an example. In a traditional approach, each order maps to a row in an ORDER table and to rows in, for example, an ORDER_LINE_ITEM table. But when using event sourcing, the Order Service stores an Order in the form of its state‑changing events: Created, Approved, Shipped, Cancelled. Each event contains sufficient data to reconstruct the Order’s state.

要查看事件源是如何工作的,请以Order实体为例。在传统方法中,每个订单映射到Order表中的一行和行,例如Order_line_Item表中的行。但是当使用事件源时,Order服务以其状态更改事件的形式存储订单:创建、批准、发送、取消。每个事件包含足够的数据来重建订单的状态。

In a microservices architecture, achieve atomicity with event sourcing

Events persist in an Event Store, which is a database of events. The store has an API for adding and retrieving an entity’s events. The Event Store also behaves like the Message Broker in the architectures we described previously. It provides an API that enables services to subscribe to events. The Event Store delivers all events to all interested subscribers. The Event Store is the backbone of an event‑driven microservices architecture.

事件持久化在事件存储区中,该存储库是一个事件数据库。存储有一个API,用于添加和检索实体的事件。事件存储也与我们前面描述的体系结构中的MessageBroker类似。它提供了一个API,使服务能够订阅事件。事件存储将所有事件传递给所有感兴趣的订阅者。事件存储是事件驱动的微服务体系结构的主干。

Event sourcing has several benefits. It solves one of the key problems in implementing an event‑driven architecture and makes it possible to reliably publish events whenever state changes. As a result, it solves data consistency issues in a microservices architecture. Also, because it persists events rather than domain objects, it mostly avoids the object‑relational impedance mismatch problem. Event sourcing also provides a 100% reliable audit log of the changes made to a business entity, and makes it possible to implement temporal queries that determine the state of an entity at any point in time. Another major benefit of event sourcing is that your business logic consists of loosely coupled business entities that exchange events. This makes it a lot easier to migrate from a monolithic application to a microservices architecture.

事件来源补充有几个好处。它解决了实现事件驱动架构的关键问题之一,并可以在状态更改时可靠地发布事件。因此,它解决了微服务体系结构中的数据一致性问题。此外,因为它仍然存在事件而不是域对象,所以它主要避免了对象关系的阻抗失配问题。事件来源补充还提供对业务实体所做的更改的100%可靠的审核日志,并可以实现在任何时间点确定实体状态的时间查询。事件来源补充的另一个主要好处是业务逻辑由交换事件的松散耦合的业务实体组成。这使得从整体应用迁移到微服务体系结构更容易。

Event sourcing also has some drawbacks. It is a different and unfamiliar style of programming and so there is a learning curve. The event store only directly supports the lookup of business entities by primary key. You must use Command Query Responsibility Segregation (CQRS) to implement queries. As a result, applications must handle eventually consistent data.

事件源也有一些缺点。这是一种不同的和不熟悉的编程风格,因此有一个学习曲线。事件存储仅直接支持通过主键查找业务实体。必须使用命令查询责任隔离(Cqrs)来实现查询。因此,应用程序必须最终处理一致的数据。

Summary 概要

In a microservices architecture, each microservice has its own private datastore. Different microservices might use different SQL and NoSQL databases. While this database architecture has significant benefits, it creates some distributed data management challenges. The first challenge is how to implement business transactions that maintain consistency across multiple services. The second challenge is how to implement queries that retrieve data from multiple services.

在微服务体系结构中,每个微服务都有自己的私有数据存储。不同的微服务可能使用不同的SQL和NoSQL数据库。虽然这种数据库架构有很大的好处,但它也带来了一些分布式数据管理方面的挑战。第一个挑战是如何实现跨多个服务保持一致性的业务事务。第二个挑战是如何实现从多个服务检索数据的查询。

For many applications, the solution is to use an event‑driven architecture. One challenge with implementing an event‑driven architecture is how to atomically update state and how to publish events. There are a few ways to accomplish this, including using the database as a message queue, transaction log mining, and event sourcing.

对于许多应用程序,解决方案是使用事件驱动的体系结构。实现事件驱动体系结构的一个挑战是如何原子地更新状态和如何发布事件。有几种方法可以实现这一点,包括将数据库用作消息队列、事务日志挖掘和事件源。

In future blog posts, we’ll continue to dive into other aspects of microservices.

在未来的博客文章中,我们将继续深入到微观服务的其他方面。

Editor – This seven‑part series of articles is now complete:

编辑-此七部分系列文章现已完成:

  1. Introduction to Microservices 微型服务简介
  2. Building Microservices: Using an API Gateway 构建微服务:使用API网关
  3. Building Microservices: Inter-Process Communication in a Microservices Architecture 构建微服务:微服务体系结构中的进程间通信
  4. Service Discovery in a Microservices Architecture 微服务体系结构中的服务发现。
  5. Event-Driven Data Management for Microservices (this article) 事件驱动的微服务数据管理(本文)
  6. Choosing a Microservices Deployment Strategy 选择微服务部署策略
  7. Refactoring a Monolith into Microservices 将单核重构为微服务

You can also download the complete set of articles, plus information about implementing microservices using NGINX Plus, as an ebook – Microservices: From Design to Deployment. And see our series on the Microservices Reference Architecture and the Microservices Solutions page.

您还可以下载完整的文章集,以及有关使用nginx+实现微服务的信息,作为电子书-microservices:从设计到部署。并在微服务参考体系结构和微服务解决方案页面上查看我们的系列文章。

Guest blogger Chris Richardson is the founder of the original CloudFoundry.com, an early Java PaaS (Platform as a Service) for Amazon EC2. He now consults with organizations to improve how they develop and deploy applications. He also blogs regularly about microservices at http://microservices.io.

嘉宾博客作者ChrisRichardson是最初的CloudFoundry.com的创始人,该网站是AmazonEC 2早期的java PaaS(平台即服务)。他现在与组织协商,以改进他们开发和部署应用程序的方式。他还定期在http://microservices.io.上发表关于微服务的博客。

Microservices: From Design to Deployment

微服务:从设计到部署

The complete guide to microservices development

微型服务开发完整指南
DOWNLOAD NOW

现在下载

一天一个Linux命令(第10天more命令)

MV命令

  • more命令是一个基于vi编辑器文本过滤器,它以全屏幕的方式按页显示文本文件的内容,支持vi中的关键字定位操作。more名单中内置了若干快捷键,常用的有H(获得帮助信息),Enter(向下翻滚一行),空格(向下滚动一屏),Q(退出命令)。

    该命令一次显示一屏文本,满屏后停下来,并且在屏幕的底部出现一个提示信息,给出至今己显示的该文件的百分比:–More–(XX%)可以用下列不同的方法对提示做出回答:

    • 按Space键:显示文本的下一屏内容。
    • 按Enier键:只显示文本的下一行内容。
    • 按斜线符|:接着输入一个模式,可以在文本中寻找下一个相匹配的模式。
    • 按H键:显示帮助屏,该屏上有相关的帮助信息。
    • 按B键:显示上一屏内容。
    • 按Q键:退出rnore命令。

语法:

more(语法)(参数)

选项:

1
2
3
4
5
6
-<数字>:指定每屏显示的行数;
-d:显示“[press space to continue,'q' to quit.]”和“[Press 'h' for instructions]”;
-c:不进行滚屏操作。每次刷新这个屏幕;
-s:将多个空行压缩成一行显示;
-u:禁止下划线;
+<数字>:从指定数字的行开始显示。

参数

文件:指定分页显示内容的文件。

实例:

显示文件file的内容,但在显示之前先清屏,并且在屏幕的最下方显示完核的百分比。

[work@aisp-communitycms-1-ptest aisp-community-cms]$ more -dc gc.log

1561122816796

显示文件file的内容,每10行显示一次,而且在显示之前先清屏。

[work@aisp-communitycms-1-ptest aisp-community-cms]$ more -c -10 aisp-community-cms.log

1561122853240

test

1561122872815

Redis简单动态字符串( simple dynamic string, SDS)

1、简单动态字符串(SDS)

  • Redis没有使用C语言传统的字符串表示(以空字符结尾的字符数组),而是自己构建了一种名为简单动态字符串的抽象类型,并将SDS用作Redis的默认字符串表示。
在Redis里面,C字符串只会作为字符串字面量用在一些无需对字符串值进行修改的地方,比如打印日志。


当Redis需要的不仅仅是一个字符串字面量,而是一个可以被修改的字符串值时,Redis就会使用SDS来表示字符串值,比如在Redis的数据库里面,包含字符串值的键值对在底层都是由SDS实现的。

举个例子,如果客户端执行命令:

​ ​
redis> SET msg “hello world”
OK
那么Redis将在数据中创建一个新的键值对,其中:

* 键值对的键是一个字符串对象,对象的底层实现是一个保存着字符串 “msg” 的SDS。
* 键值对的值也是一个字符串对象,对象的底层实现是一个保存着字符串“hello world”的SDS。

又比如, 如果客户端执行命令: 

    redis> RPUSH fruits "apple" "bannan" "cherry"
    (integer) 3
那么Redis将在数据库中创建一个新的键值对,其中:
  • 键值对的键是一个字符串对象,对象的底层实现是一个保存了字符串“fruits”的SDS。

  • 键值对的值也是一个字符串对象,列表对象包含了三个字符串对象,这三个字符串对象分别有三个SDS实现,第一个SDS保存着字符串apple, 第二个SDS保存着bannan,第三个SDS保存着cherry。

    除了用来保存数据库中的字符串值之外,SDS还被用作缓存区(buffer)

缓冲区

​ C语言给字符串开辟一个存储空间,如果对此存储空间的使用超过开辟的空间,会导致内存溢出。例如使用字符串拼接等方式时,就很容易出现此问题。而如果每次拼接之前都要计算每个字符串的长度,时间上又要耗费很久。

​ redis的SDS中内置一个sdscat函数,也是用于字符串的拼接。但是在执行操作之前,其会先检查空间是否足够,通过比较当前字符串的free与即将拼接字符串的len的大小,就知道是否可以拼接。如果free的值不够,会再申请内存空间,避免溢出。

修改字符串时的内存重分配

​ C语言的字符串长度和底层数组之间存在关联,因此字符串长度增加时需要再分配存储空间,避免溢出;字符串长度减少时,需要释放存储空间,避免内存泄漏。

由于redis中,字符串频繁修改是很经常发生的事情,redis的一个应用场景就是变量频繁修改的场景。为了避免C语言的不断重分配空间,redis进行了改进。

​ redis的sds,主要是通过free字段,来进行判断。通过未使用空间大小,实现了空间预分配和惰性空间释放。

​ 1)空间预分配

空间预分配用于优化字符串的增长操作,实现方式为:当需要增长字符串时,sds不仅会分配足够的空间用于增长,还会预分配未使用空间。

分配的规则是,如果增长字符串后,新的字符串比1MB小,则额外申请字符串当前所占空间的大小作为free值;如果增长后,字符串长度超过1MB,则额外申请1MB大小。

例如,字符串增长后,大小是50kb,则额外申请50kb作为未使用空间。如果字符串增长后的大小是20mb,则额外申请1mb作为未使用空间。以上两种情况都为将\0计算在内,因此,实际上还会需要1字节作为\0存放的空间。

上述机制,避免了redis字符串增长情况下频繁申请空间的情况。每次字符串增长之前,sds会先检查空间是否足够,如果足够则直接使用预分配的空间,否则按照上述机制申请使用空间。该机制,使得字符串增长n次,需要申请空间的次数,从必定为n次的情况,降为最多n次的情况。

2)懒惰空间释放

懒惰空间释放用于优化sds字符串缩短的操作,实现方式为:当需要缩短sds的长度时,并不立即释放空间,而是使用free来保存剩余可用长度,并等待将来使用。当有剩余空间,而有有增长字符串操作时,则又会调用空间预分配机制。

当redis内存空间不足时,会自动释放sds中未使用的空间,因此也不需要担心内存泄漏问题。

二进制安全

​ C语言的字符必须符合某种编码,例如ascii,且字符串除了末尾之外,不能有空格,否则会被当作是另一个字符串。这些限制使得c语言的字符串只能保存不含空格的文本,不能保存图片、视频等二进制数据,也不能保存包含空格的文本。

而保存图片、大段文本等内容,也是redis的常用场景。因此,redis也对此进行优化。因此,sds是二进制安全的,写入的是什么内容,返回的也是什么内容,并没有限制。

​ redis的sds,用buf保存字符串,保存的就是一系列的二进制数据。因为,sds考虑字符串长度,是通过len属性,而不是通过\0来判断。

C语言字符串函数

​ redis兼容c语言对于字符串末尾采用\0进行处理,这样使得其可以复用部分c语言字符串函数的代码,实现代码的精简性。

总结——C语言字符串和SDS字符串比较
C字符串 Redis SDS
获取字符串长度时间复杂的O(n) 获取字符串长度时间复杂的O(1)
字符串长度增加可能造成缓冲区溢出 字符串长度增加不会造成缓冲区溢出
修改字符串长度n次,必然n次内存重分配 修改字符串长度n次,最多n次内存重分配
只保存不含空格文本 保存任意二进制数据和文本数据
可以使用<string.h>库所有函数 可以使用部分<string.h>库的函数