Timescale 代码快速入门指南旨在帮助您将 Timescale 集成到您自己的程序中。它们使用您喜欢的编程语言来解释如何连接到 Timescale 数据库、创建和管理超表以及摄取和查询数据。
本快速入门指南将向您展示如何
在开始之前,请确保您已拥有
- 创建了 Timescale 服务。有关更多信息,请参阅启动文档。记下您创建的 Timescale 服务中的
Service URL
、Password
和Port
。 - 安装了 Rails。
- 安装了 psql 以连接到 Timescale 服务。
在本节中,您将通过 Ruby on Rails 应用程序创建与 Timescale 服务的连接。
创建一个新的 Rails 应用程序,配置为使用 PostgreSQL 作为数据库。您的 Timescale 服务作为 PostgreSQL 扩展工作。
rails new my_app -d=postgresqlRails 创建并捆绑您的应用程序,并在该过程中安装所有必需的 Gems。
使用 Timescale 服务的
<PORT>
更新位于my_app/config
目录中的database.yml
中的port
。将
DATABASE_URL
的环境变量设置为服务的<SERVICE_URL>
。例如,在ZSH
shell 中,使用以下内容编辑~/.zshrc
文件export DATABASE_URL="<SERVICE_URL>"保存
~/.zshrc
文件并使用以下命令加载环境变量source ~/.zshrc将 Timescale 添加到您的 Rails 迁移
rails generate migration add_timescale一个新的迁移文件
<migration-datetime>_add_timescale.rb
在my_app/db/migrate
目录中创建。使用 Rails 连接到您的服务
echo "\dx" | rails dbconsole在密码提示符中输入 Timescale 服务的
tsdbadmin
密码。结果看起来像
List of installed extensionsName | Version | Schema | Description---------------------+---------+------------+---------------------------------------------------------------------------------------pg_stat_statements | 1.10 | public | track planning and execution statistics of all SQL statements executedplpgsql | 1.0 | pg_catalog | PL/pgSQL procedural languagetimescaledb | 2.9.3 | public | Enables scalable inserts and complex queries for time-series datatimescaledb_toolkit | 1.13.1 | public | Library of analytical hyperfunctions, time-series pipelining, and other SQL utilities(4 rows)重要
为了确保您的测试成功运行,请在
config/environments/test.rb
文件中添加config.active_record.verify_foreign_keys_for_fixtures = false
。否则,您会收到错误,因为 Timescale 使用内部外键。
在本节中,您将创建一个表来存储用户代理或浏览器以及访问者加载页面时的时间。您可以轻松扩展这个简单的示例,以存储您感兴趣的许多其他 Web 分析。
生成 Rails 脚手架以在表中表示用户代理信息
rails generate scaffold PageLoads user_agent:string一个新的迁移文件
<migration-datetime>_create_page_loads.rb
在my_app/db/migrate
目录中创建。Timescale 要求表上的任何UNIQUE
或PRIMARY KEY
索引都包含所有分区列,在本例中为时间列。新的 Rails 模型默认包含 id 的PRIMARY KEY
索引,因此您需要删除该列或确保索引包含时间作为“复合键”的一部分。Rails 本身不支持复合键,但如果您出于某种原因需要保留
id
列,则可以使用composite_primary_keys
gem 添加对它们的支持。将位于
my_app/db/migrate
目录的<migration-datetime>_create_page_loads.rb
文件中的迁移代码更改为class CreatePageLoads < ActiveRecord::Migration[7.0]def changecreate_table :page_loads, id: false do |t|t.string :user_agentt.timestampsendendendRails 生成所有辅助文件和数据库迁移。
在数据库中创建表
rails db:migrate使用以下命令确认表是否存在,并使用以下命令确认模型已正确映射
rails runner 'p PageLoad.count'0在
rails dbconsole
输出中查看page_loads
表的结构echo "\d page_loads" | rails dbconsole结果类似于
Table "public.page_loads"Column | Type | Collation | Nullable | Default------------+--------------------------------+-----------+----------+---------user_agent | character varying | | |created_at | timestamp(6) without time zone | | not null |updated_at | timestamp(6) without time zone | | not null |
创建关系表后,您可以创建超表。创建表和索引、更改表、插入数据、选择数据以及大多数其他任务都在超表上执行。
创建迁移以修改
page_loads
数据库并创建超表rails generate migration add_hypertable一个新的迁移文件
<migration-datetime>_add_hypertable.rb
在my_app/db/migrate
目录中创建。将位于
my_app/db/migrate
目录的<migration-datetime>_add_hypertable.rb
文件中的迁移代码更改为class AddHypertable < ActiveRecord::Migration[7.0]def changeexecute "SELECT create_hypertable('page_loads', by_range('created_at'));"endend注意
by_range
和by_hash
维度构建器是 TimescaleDB 2.13 的新增功能。生成超表
rails db:migrate查看超表
echo "\d page_loads" | rails dbconsole结果类似于
Table "public.page_loads"Column | Type | Collation | Nullable | Default------------+--------------------------------+-----------+----------+---------user_agent | character varying | | |created_at | timestamp(6) without time zone | | not null |updated_at | timestamp(6) without time zone | | not null |Indexes:"page_loads_created_at_idx" btree (created_at DESC)Triggers:ts_insert_blocker BEFORE INSERT ON page_loads FOR EACH ROW EXECUTE FUNCTION _timescaledb_functions.insert_blocker()
您可以通过几种不同的方式将数据插入到超表中。创建一个新的视图和控制器,以便您可以将值插入到数据库中,将用户代理和时间存储在数据库中,检索站点访问者的浏览器用户代理。然后,您可以创建一个 PageLoad
对象,存储用户代理信息和时间,并将该对象保存到 Timescale 服务。
创建一个新的视图和控制器,以便您可以将值插入到数据库中
rails generate controller static_pages home这将为网站上名为
/static_pages/home
的页面生成视图和控制器文件。static_pages_controller.rb
文件位于/my_app/app/controllers
目录中。将此行添加到
static_pages_controller.rb
文件中,以检索站点访问者的浏览器用户代理。class StaticPagesController < ApplicationControllerdef home@agent = request.user_agentendend将您创建的
@agent
变量打印到位于/my_app/app/views/static_pages/
的home.html.erb
文件中。<h1>StaticPages#home</h1><p>Find me in app/views/static_pages/home.html.erb</p><p>Request: <&= @agent &></p>启动 Rails 服务器
rails s转到
http://localhost:3000/static_pages/home
。您应该看到浏览器用户代理的打印输出。更新
static_pages_controller.rb
控制器文件以创建PageLoad
对象,存储用户代理信息和时间,并将该对象保存到 Timescaletsdb
数据库class StaticPagesController < ApplicationControllerdef homePageLoad.create(user_agent: request.user_agent)endend当您转到浏览器并多次刷新页面时。在 Rails 控制台窗口中,将出现提交消息
Started GET "/static_pages/home" for ::1 at 2023-02-22 07:02:16 +0530Processing by StaticPagesController#home as HTMLTRANSACTION (268.7ms) BEGIN↳ app/controllers/static_pages_controller.rb:3:in 'home'PageLoad Create (207.8ms) INSERT INTO "page_loads" ("user_agent", "created_at", "updated_at") VALUES ($1, $2, $3) [["user_agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36"], ["created_at", "2023-02-22 01:32:16.465709"], ["updated_at", "2023-02-22 01:32:16.465709"]]↳ app/controllers/static_pages_controller.rb:3:in 'home'TRANSACTION (206.5ms) COMMIT↳ app/controllers/static_pages_controller.rb:3:in 'home'Rendering layout layouts/application.html.erbRendering static_pages/home.html.erb within layouts/applicationRendered static_pages/home.html.erb within layouts/application (Duration: 0.1ms | Allocations: 7)Rendered layout layouts/application.html.erb (Duration: 9.4ms | Allocations: 2389)Completed 200 OK in 917ms (Views: 10.4ms | ActiveRecord: 682.9ms | Allocations: 4542)使用 psql 连接到
tsdb
数据库psql -x <SERVICE_URL>查看 Timescale
tsdb
数据库中的条目SELECT * FROM page_loads ORDER BY created_at DESC;结果类似于
-[ RECORD 1 ]---------------------------------------------------------------------------------------------------------------------user_agent | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/202.0.0.0 Safari/537.36created_at | 2023-02-22 01:32:53.935198updated_at | 2023-02-22 01:32:53.935198-[ RECORD 2 ]---------------------------------------------------------------------------------------------------------------------user_agent | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/202.0.0.0 Safari/537.36created_at | 2023-02-22 01:32:45.146997updated_at | 2023-02-22 01:32:45.146997
本节介绍如何针对数据库执行查询。您可以检索您插入的数据并查看它。
在
static_pages_controller.rb
文件中,修改home
方法以使用 Active Record 查询page_load
数据库中的所有项目,并将它们存储在数组中class StaticPagesController < ApplicationControllerdef homePageLoad.create(:user_agent => request.user_agent)endend修改
home.html.erb
视图以迭代数组并显示每个项目<h1>Static Pages requests: <%= PageLoad.count &></h1>现在,每次刷新页面时,您都可以看到记录正在插入到
tsdb
Timescale 数据库中,并且页面上的计数器会递增。您需要有大量的页面加载才能研究和探索 time_bucket 函数。您可以使用 Apache Bench 又名
ab
来请求 50,000 次,并行 10 次。ab -n 50000 -c 10 http://localhost:3000/static_pages/homeApache Bench 在超表中创建数千条记录。您可以计算 Rails 支持多少“空请求”。
在
ab
命令开始运行后,您可以启动 rails 控制台并尝试使用 time_bucket 函数进行一些查询。rails console查看每分钟的请求数
PageLoad.select("time_bucket('1 minute', created_at) as time, count(1) as total").group('time').order('time').map {|result| [result.time, result.total]}结果类似于
PageLoad Load (357.7ms) SELECT time_bucket('1 minute', created_at) as time, count(1) as total FROM "page_loads" GROUP BY time ORDER BY time=>[2023-02-22 01:32:00 UTC, 6],[2023-02-22 05:57:00 UTC, 3],[2023-02-22 05:59:00 UTC, 75],
作用域对于将复杂的 SQL 语句分解为 Ruby 对象非常有用。它还允许您根据需要引入参数和重用查询。创建一些有用的作用域,这些作用域可以帮助汇总和轻松访问 time_bucket
函数
在位于
my_app/app/models
目录的page_load.rb
文件中,添加这些作用域class PageLoad < ApplicationRecordscope :last_month, -> { where('created_at > ?', 1.month.ago) }scope :last_week, -> { where('created_at > ?', 1.week.ago) }scope :last_hour, -> { where('created_at > ?', 1.hour.ago) }scope :yesterday, -> { where('DATE(created_at) = ?', 1.day.ago.to_date) }scope :today, -> { where('DATE(created_at) = ?', Date.today) }end在新的 Ruby 控制台中,您可以运行这些命令来获取各种请求的视图
PageLoad.last_week.count # Total of requests from last weekPageLoad.last_hour.first # First request from last hourPageLoad.last_hour.all # All requests from last hourPageLoad.last_hour.limit(10) # 10 requests from last hour您还可以将作用域与其他 ActiveRecord 方法结合使用,例如
# Count chrome users from last hourPageLoad.last_hour.where("user_agent like '%Chrome%'").count在
page_load.rb
文件中添加一个新的作用域,用于计算每分钟的维度class PageLoad < ApplicationRecordscope :counts_per, -> (time_dimension) {select("time_bucket('#{time_dimension}', created_at) as time, count(1) as total").group(:time).order(:time).map {|result| [result.time, result.total]}}end在 Ruby 控制台中探索其他时间范围
PageLoad.counts_per('1 hour')结果类似于
PageLoad Load (299.7ms) SELECT time_bucket('1 hour', created_at) as time, count(1) as total FROM "page_loads" GROUP BY "time" ORDER BY "time" ASC=>[2023-02-22 01:00:00 UTC, 6],[2023-02-22 05:00:00 UTC, 78],[2023-02-22 06:00:00 UTC, 13063],[2023-02-22 07:00:00 UTC, 4114],
为了更深入地了解请求,请移动示例以监视所有服务器请求并存储端点路径和返回响应所需的时间。
使用 rails 迁移向数据库添加列
rails g migration add_performance_to_page_load path:string performance:floatRails 生成器理解迁移的命名约定和额外的参数,以在
my_app/db/migrate
目录中创建一个新的迁移文件<migration-datetime>_add_performance_to_page_load.rb
要在数据库中添加两列,请运行
rails db:migrate
。结果类似于
== 20230226173116 AddPerformanceToPageLoad: migrating =========================-- add_column(:page_loads, :path, :string)-> 0.6050s-- add_column(:page_loads, :performance, :float)-> 0.3076s== 20230226173116 AddPerformanceToPageLoad: migrated (0.9129s) ================要使用 around_action 钩子钩住应用程序控制器,请在位于
my_app/app/controllers
目录的application_controller.rb
文件中添加这些class ApplicationController < ActionController::Basearound_action do |controller, action|performance = Benchmark.measure(&action.method(:call))PageLoad.create(path: request.path,performance: performance.real,user_agent: request.user_agent)endend这将为系统中发生的任何请求创建 PageLoad 记录。
要查看最新记录,请在 Rails 控制台中运行:
PageLoad.order(:created_at).last
结果类似于
PageLoad Load (318.2ms) SELECT "page_loads".* FROM "page_loads" ORDER BY "page_loads"."created_at" DESC LIMIT $1 [["LIMIT", 1]]=>#<PageLoad:0x000000010950a410user_agent:"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36",created_at: Sun, 26 Feb 2023 15:49:35.186955000 UTC +00:00,updated_at: Sun, 26 Feb 2023 15:49:35.186955000 UTC +00:00,path: "/static_pages/home",performance: 1.094204000197351>本示例仅使用来自 benchmark 的真实性能,但您可以收集其他指标以查看有关您的系统的更多详细信息。
既然您知道存在哪些页面,您就可以探索结果。您可以逐页浏览,或一起浏览所有页面,并按路径分组或不分组
在位于
my_app/app/models
目录的page_load.rb
文件中,添加这些作用域,用于平均响应时间、min
和max
请求,并从页面加载中收集唯一路径class PageLoad < ApplicationRecordscope :per_minute, -> { time_bucket('1 minute') }scope :per_hour, -> { time_bucket('1 hour') }scope :per_day, -> { time_bucket('1 day') }scope :per_week, -> { time_bucket('1 week') }scope :per_month, -> { time_bucket('1 month') }scope :average_response_time_per_minute, -> { time_bucket('1 minute', value: 'avg(performance)') }scope :average_response_time_per_hour, -> { time_bucket('1 hour', value: 'avg(performance)') }scope :worst_response_time_last_minute, -> { time_bucket('1 minute', value: 'max(performance)') }scope :worst_response_time_last_hour, -> { time_bucket('1 hour', value: 'max(performance)') }scope :best_response_time_last_hour, -> { time_bucket('1 hour', value: 'min(performance)') }scope :paths, -> { distinct.pluck(:path) }scope :time_bucket, -> (time_dimension, value: 'count(1)') {select(<<~SQL)time_bucket('#{time_dimension}', created_at) as time, path,#{value} as valueSQL.group('time, path').order('path, time')}end在 Rails 控制台中,从页面加载中收集唯一路径
PageLoad.paths # => ["/page_loads/new", "/static_pages/home"]结果类似于
PageLoad Pluck (276.1ms) SELECT DISTINCT "page_loads"."path" FROM "page_loads"=> [nil, "/static_pages/home"]在 Ruby 控制台中,要获取为响应时间生成的实际指标,请使用包含
response_time
的方法进行过滤PageLoad.methods.grep /response_time/结果类似于
PageLoad.methods.grep /response_time/# => [:average_response_time_per_hour,# :average_response_time_per_minute,# :worst_response_time_last_hour,# :worst_response_time_last_minute,# :best_response_time_last_hour]要基于每个页面构建摘要,并递归导航到所有页面并为每个页面构建摘要,请将以下内容添加到
my_app/app/models/
文件夹中的page_load.rb
中def self.resume_for(path)filter = where(path: path)get = -> (scope_name) { filter.send(scope_name).first&.value}metrics.each_with_object({}) do |metric, resume|resume[metric] = get[metric]endenddef self.metricsmethods.grep /response_time/enddef self.statisticspaths.each_with_object({}) do |path, resume|resume[path] = resume_for(path)endend在 Rails 控制台中,要查看基于每个页面的摘要,请运行
PageLoad.resume_for("/page_loads/new")
。结果类似于
=> {:average_response_time_per_minute=>0.10862650000490248,:average_response_time_per_hour=>0.060067999991588295,:worst_response_time_last_minute=>0.20734900003299117,:worst_response_time_last_hour=>0.20734900003299117,:best_response_time_last_hour=>0.009765000082552433},在 Rails 控制台中,递归导航到所有页面并为每个页面构建摘要
结果类似于
"/page_loads/new"=>{:average_response_time_per_minute=>0.10862650000490248,:average_response_time_per_hour=>0.060067999991588295,:worst_response_time_last_minute=>0.20734900003299117,:worst_response_time_last_hour=>0.20734900003299117,:best_response_time_last_hour=>0.009765000082552433},"/static_pages/home"=>{:average_response_time_per_minute=>1.214221078382038,:average_response_time_per_hour=>4.556298695798993,:worst_response_time_last_minute=>2.2735520000569522,:worst_response_time_last_hour=>1867.2145019997843,:best_response_time_last_hour=>1.032415000256151}}
关键词
在此页面上发现问题?报告问题 或 在 GitHub 上编辑此页面。