데이터 베이스 MySQL 튜토리얼 使用FriendFeed来提升MySQL性能的方法_MySQL

使用FriendFeed来提升MySQL性能的方法_MySQL

Jun 01, 2016 pm 12:59 PM
mysql

 背景

我们使用MySQL存储了FriendFeed的所有数据。数据库随着用户基数的增长而增长了很多。现在已经存储了超过2.5亿条记录与一堆涵盖了从评论和“喜欢”到好友列表的其他数据。

随着数据的增长,我们也曾迭代地解决了随着如此迅猛的增长而带来的扩展性问题。我们的尝试很有代表性,例如使用只读mysql从节点和memcache来增加读取吞吐量,对数据库进行分片来提高写入吞吐量。然而,随着业务的增长,添加新功能比扩展既有功能以迎合更多的流量变得更加困难。


特别的,对 schema 做改动或为超过 1000-2000 万行记录的数据库添加索引会将数据库锁住几个小时。删除旧索引也要占用这么多时间,但不删除它们会影响性能;因为数据库要持续地在每个INSERT上读写这些没用的区块,并将重要的区块挤出了内存。为避免这些问题需要采取一些复杂的措施(例如在从节点上设置新的索引,然后将从节点与主节点对调),但这些措施会引发错误并且实施起来比较困难,它们阻碍了需要改动 schema/索引才能实现的新功能。由于数据库的严重分散,MySQL 的关系特性(如join)对我们没用,所以我们决定脱离 RDBMS。


虽然已有许多用于解决灵活 schema 数据存储和运行时构建索引的问题(例如 CouchDB)的项目。但在大站点中却没有足够广泛地用到来说服人们使用。在我们看到和运行的测试中,这些项目要么不稳定,要么缺乏足够的测试(参见这个有点过时的关于 CouchDB 的文章)。MySQL 不错,它不会损坏数据;复制也没问题,我们已经了解了它的局限。我们喜欢将 MySQL 用于存储,仅仅是非关系型的存储。

几经思量,我们决定在 MySQL 上采用一种无模式的存储系统,而不是使用一个完全没接触过的存储系统。本文试图描述这个系统的高级细节。我们很好奇其他大型网站是如何处理这些问题的,另外也希望我们完成的某些设计会对其他开发者有所帮助。

综述

我们在数据库中存储的是无模式的属性集(例如JSON对象或python字典)。存储的记录只需一个名为id的16字节的UUID属性。对数据库而言实体的其他部分是不可见的。我们可以简单地存入新属性来改变schema(可以简单理解为数据表中只有两个字段:id,data;其中data存储的是实体的属性集)。

我们通过保存在不同表中的索引来检索数据。如果想检索每个实体中的三个属性,我们就需要三个数据表-每个表用于检索某一特定属性。如果不想再用某一索引了,我们要在代码中停止该索引对应表的写操作,并可选地删除那个表。如果想添加个新索引,只需要为该索引新建个MySQL表,并启动一个进程异步地为该表添加索引数据(不影响运行中的服务)。

最终,虽然我们的数据表增多了,但添加和删除索引却变得简单了。我们大力改善了添加索引数据的进程(我们称之为“清洁工")使其在快速添加索引的同时不会影响站点。我们可以在一天内完成新属性的保存和索引,并且我们不需要对调主从MySQL数据库,也不需要任何其他可怕的操作。

细节

MySQL 使用表保存我们的实体,一个表就像这样 :
 

CREATE TABLE entities (
  added_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
  id BINARY(16) NOT NULL,
  updated TIMESTAMP NOT NULL,
  body MEDIUMBLOB,
  UNIQUE KEY (id),
  KEY (updated)
) ENGINE=InnoDB;
로그인 후 복사

之所以使用 added_id 个字段是因为 InnoDB 按物理主键顺序存储数据,自增长主键确保新实例在磁盘上按顺序写到老实体之后,这样有助于分区读写(相对老的实体,新实体往往读操作更频繁,因为 FriendFeed 的 pages 是按时间逆序排列)。实体本身经 python 字典序列化后使用 zlib 压缩存储。

索引单独存在一张表里,如果要创建索引,我们创建一张新表存储我们想要索引的数据分片的所有属性。例如,一个 FriendFeed 实体通过看上去是这样的:

{
  "id": "71f0c4d2291844cca2df6f486e96e37c",
  "user_id": "f48b0440ca0c4f66991c4d5f6a078eaf",
  "feed_id": "f48b0440ca0c4f66991c4d5f6a078eaf",
  "title": "We just launched a new backend system for FriendFeed!",
  "link": "http://friendfeed.com/e/71f0c4d2-2918-44cc-a2df-6f486e96e37c",
  "published": 1235697046,
  "updated": 1235697046,
}
로그인 후 복사

我们索引实体的属性 user_id,这样我们可以渲染一个页面,包含一个已提交用户的所有属性。我们的索引表看起来是这样的:

CREATE TABLE index_user_id (
  user_id BINARY(16) NOT NULL,
  entity_id BINARY(16) NOT NULL UNIQUE,
  PRIMARY KEY (user_id, entity_id)
) ENGINE=InnoDB;
로그인 후 복사


我们的数据存储会自动为你维护索引,所以如果你要在我们存储上述结构实体的数据存储里开启一个实例,你可以写一段代码(用 python):

user_id_index = friendfeed.datastore.Index(
  table="index_user_id", properties=["user_id"], shard_on="user_id")
datastore = friendfeed.datastore.DataStore(
  mysql_shards=["127.0.0.1:3306", "127.0.0.1:3307"],
  indexes=[user_id_index])
 
new_entity = {
  "id": binascii.a2b_hex("71f0c4d2291844cca2df6f486e96e37c"),
  "user_id": binascii.a2b_hex("f48b0440ca0c4f66991c4d5f6a078eaf"),
  "feed_id": binascii.a2b_hex("f48b0440ca0c4f66991c4d5f6a078eaf"),
  "title": u"We just launched a new backend system for FriendFeed!",
  "link": u"http://friendfeed.com/e/71f0c4d2-2918-44cc-a2df-6f486e96e37c",
  "published": 1235697046,
  "updated": 1235697046,
}
datastore.put(new_entity)
entity = datastore.get(binascii.a2b_hex("71f0c4d2291844cca2df6f486e96e37c"))
entity = user_id_index.get_all(datastore, user_id=binascii.a2b_hex("f48b0440ca0c4f66991c4d5f6a078eaf"))
로그인 후 복사

上面的 Index 类在所有实体中查找 user_id,自动维护 index_user_id 表的索引。我们的数据库是切分的,参数 shard_on 是用来确定索引是存储在哪个分片上(这种情况下使用 entity["user_id"] % num_shards)。

你可以使用索引实例(见上面的 user_id_index.get_all)查询一个索引,使用 python 写的数据存储代码将表 index_user_id 和表 entities 合并。首先在所有数据库分片中查询表 index_user_id 获取实体 ID 列,然后在 entities 提出数据。

新建一个索引,比如,在属性 link 上,我们可以创建一个新表:

CREATE TABLE index_link (
  link VARCHAR(735) NOT NULL,
  entity_id BINARY(16) NOT NULL UNIQUE,
  PRIMARY KEY (link, entity_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
로그인 후 복사

我们可以修改数据存储的初始化代码以包含我们的新索引:

user_id_index = friendfeed.datastore.Index(
  table="index_user_id", properties=["user_id"], shard_on="user_id")
link_index = friendfeed.datastore.Index(
  table="index_link", properties=["link"], shard_on="link")
datastore = friendfeed.datastore.DataStore(
  mysql_shards=["127.0.0.1:3306", "127.0.0.1:3307"],
  indexes=[user_id_index, link_index])
로그인 후 복사

我可以异步构建索引(特别是实时传输服务):

./rundatastorecleaner.py --index=index_link
로그인 후 복사

一致性与原子性

由于采用分区的数据库,实体的索引可能存储在与实体不同的分区中,这引起了一致性问题。如果进程在写入所有索引表前崩溃了会怎样?

许多有野心的 FriendFeed 工程师倾向于构建一个事务性协议,但我们希望尽可能地保持系统的简洁。我们决定放宽限制:

  •     保存在主实体表中的属性集是规范完整的
  •     索引不会对真实实体值产生影响

因此,往数据库中写入实体时我们采用如下步骤:

  •     使用 InnoDB 的 ACID 属性将实体写入 entities 表。
  •     将索引写入所有分区中的索引表。


我们要记住从索引表中取出的数据可能是不准确的(例如如果写操作没有完成步骤2可能会影响旧属性值)。为确保采用上面的限制能返回正确的实体,我们用索引表来决定要读取哪些实体,但不要相信索引的完整性,要使用查询条件对这些实体进行再过滤:

1.根据查询条件从索引表中取得 entity_id

2.根据 entity_id 从 entities 表中读取实体

3.根据实体的真实属性(用 Python)过滤掉不符合查询条件的实体

为保证索引的持久性和一致性,上文提到的“清洁工”进程要持续运行,写入丢失的索引,清理失效的旧索引。它优先清理最近更新的实体,所以实际上维护索引的一致性非常快(几秒钟).
 
性能

我们对新系统的主索引进行了优化,对结果也很满意。以下是上个月 FriendFeed 页面的加载延时统计图(我们在前几天启动了新的后端,你可以根据延时的显著回落找到那一天)。

201562592337738.png (600×250)

特别地,系统的延时现在也很稳定(哪怕是在午高峰期间)。如下是过去24小时FriendFeed页面加载延时图。

201562592401055.png (600×250)

与上周的某天相比较:

201562592420826.png (600×250)

系统到目前为止使用起来很方便。我们在部署之后也改动了几次索引,并且我们也开始将这种模式应用于 MySQL 中那些较大的表,这样我们在以后可以轻松地改动它们的结构。

본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

Video Face Swap

Video Face Swap

완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

인기 기사

<gum> : Bubble Gum Simulator Infinity- 로얄 키를 얻고 사용하는 방법
3 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌
Nordhold : Fusion System, 설명
3 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌
Mandragora : 마녀 트리의 속삭임 - Grappling Hook 잠금 해제 방법
3 몇 주 전 By 尊渡假赌尊渡假赌尊渡假赌

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 CS6

시각적 웹 개발 도구

SublimeText3 Mac 버전

SublimeText3 Mac 버전

신 수준의 코드 편집 소프트웨어(SublimeText3)

LARAVEL 소개 예 LARAVEL 소개 예 Apr 18, 2025 pm 12:45 PM

Laravel은 웹 응용 프로그램을 쉽게 구축하기위한 PHP 프레임 워크입니다. 설치 : Composer를 사용하여 전 세계적으로 Laravel CLI를 설치하고 프로젝트 디렉토리에서 응용 프로그램을 작성하는 등 다양한 기능을 제공합니다. 라우팅 : Routes/Web.php에서 URL과 핸들러 간의 관계를 정의하십시오. 보기 : 리소스/뷰에서보기를 작성하여 응용 프로그램의 인터페이스를 렌더링합니다. 데이터베이스 통합 : MySQL과 같은 데이터베이스와 상자 외 통합을 제공하고 마이그레이션을 사용하여 테이블을 작성하고 수정합니다. 모델 및 컨트롤러 : 모델은 데이터베이스 엔티티를 나타내고 컨트롤러는 HTTP 요청을 처리합니다.

MySQL 및 Phpmyadmin : 핵심 기능 및 기능 MySQL 및 Phpmyadmin : 핵심 기능 및 기능 Apr 22, 2025 am 12:12 AM

MySQL 및 Phpmyadmin은 강력한 데이터베이스 관리 도구입니다. 1) MySQL은 데이터베이스 및 테이블을 작성하고 DML 및 SQL 쿼리를 실행하는 데 사용됩니다. 2) PHPMYADMIN은 데이터베이스 관리, 테이블 구조 관리, 데이터 운영 및 사용자 권한 관리에 직관적 인 인터페이스를 제공합니다.

MySQL 대 기타 프로그래밍 언어 : 비교 MySQL 대 기타 프로그래밍 언어 : 비교 Apr 19, 2025 am 12:22 AM

다른 프로그래밍 언어와 비교할 때 MySQL은 주로 데이터를 저장하고 관리하는 데 사용되는 반면 Python, Java 및 C와 같은 다른 언어는 논리적 처리 및 응용 프로그램 개발에 사용됩니다. MySQL은 데이터 관리 요구에 적합한 고성능, 확장 성 및 크로스 플랫폼 지원으로 유명하며 다른 언어는 데이터 분석, 엔터프라이즈 애플리케이션 및 시스템 프로그래밍과 같은 해당 분야에서 이점이 있습니다.

데이터베이스 연결 문제 해결 : Minii/DB 라이브러리 사용 실질적인 사례 데이터베이스 연결 문제 해결 : Minii/DB 라이브러리 사용 실질적인 사례 Apr 18, 2025 am 07:09 AM

작은 응용 프로그램을 개발할 때 까다로운 문제가 발생했습니다. 가벼운 데이터베이스 운영 라이브러리를 신속하게 통합해야합니다. 여러 라이브러리를 시도한 후에는 기능이 너무 많거나 호환되지 않는다는 것을 알았습니다. 결국, 나는 내 문제를 완벽하게 해결하는 YII2를 기반으로 단순화 된 버전 인 Minii/DB를 발견했습니다.

Laravel 프레임 워크 설치 방법 Laravel 프레임 워크 설치 방법 Apr 18, 2025 pm 12:54 PM

기사 요약 :이 기사는 Laravel 프레임 워크를 쉽게 설치하는 방법에 대한 독자들을 안내하기위한 자세한 단계별 지침을 제공합니다. Laravel은 웹 애플리케이션의 개발 프로세스를 가속화하는 강력한 PHP 프레임 워크입니다. 이 자습서는 시스템 요구 사항에서 데이터베이스 구성 및 라우팅 설정에 이르기까지 설치 프로세스를 다룹니다. 이러한 단계를 수행함으로써 독자들은 라벨 프로젝트를위한 탄탄한 토대를 빠르고 효율적으로 놓을 수 있습니다.

MySQL 모드 해결 문제 : theliamysqlmodeschecker 모듈 사용 경험 MySQL 모드 해결 문제 : theliamysqlmodeschecker 모듈 사용 경험 Apr 18, 2025 am 08:42 AM

Thelia를 사용하여 전자 상거래 웹 사이트를 개발할 때 까다로운 문제가 발생했습니다. MySQL 모드가 제대로 설정되지 않아 일부 기능이 제대로 작동하지 않습니다. 약간의 탐색 후, 나는 theliamysqlmodeschecker라는 모듈을 발견했습니다.이 모듈은 Thelia가 요구하는 MySQL 패턴을 자동으로 수정하여 내 문제를 완전히 해결할 수 있습니다.

MySQL : 구조화 된 데이터 및 관계형 데이터베이스 MySQL : 구조화 된 데이터 및 관계형 데이터베이스 Apr 18, 2025 am 12:22 AM

MySQL은 테이블 구조 및 SQL 쿼리를 통해 구조화 된 데이터를 효율적으로 관리하고 외래 키를 통해 테이블 ​​간 관계를 구현합니다. 1. 테이블을 만들 때 데이터 형식을 정의하고 입력하십시오. 2. 외래 키를 사용하여 테이블 간의 관계를 설정하십시오. 3. 인덱싱 및 쿼리 최적화를 통해 성능을 향상시킵니다. 4. 데이터 보안 및 성능 최적화를 보장하기 위해 데이터베이스를 정기적으로 백업 및 모니터링합니다.

MySQL : 주요 기능 및 기능이 설명되었습니다 MySQL : 주요 기능 및 기능이 설명되었습니다 Apr 18, 2025 am 12:17 AM

MySQL은 웹 개발에 널리 사용되는 오픈 소스 관계형 데이터베이스 관리 시스템입니다. 주요 기능에는 다음이 포함됩니다. 1. 다른 시나리오에 적합한 InnoDB 및 MyISAM과 같은 여러 스토리지 엔진을 지원합니다. 2.로드 밸런싱 및 데이터 백업을 용이하게하기 위해 마스터 슬레이브 복제 기능을 제공합니다. 3. 쿼리 최적화 및 색인 사용을 통해 쿼리 효율성을 향상시킵니다.

See all articles