具有百万行的数据库表

jxct1oxe  于 2021-06-20  发布在  Mysql
关注(0)|答案(4)|浏览(321)

例如,我有一些gps设备,发送信息到我的数据库每秒钟
因此,1个设备在mysql数据库中用这些列创建1行(8)
id=12341日期=22.02.2018时间=22:40经度=22.236558789经度=78.9654582设备id=24名称=设备名称someinfo=asdadasd
因此,1分钟内它创建60行,24小时内创建864000行,1个月(31天)创建2678400行
因此,1个设备每月在我的db表中创建260万行(每个月都会删除记录)。因此,如果有更多的设备,将有260万*设备数
所以我的问题是这样的:
问题1:如果我从php进行这样的搜索(只针对当前日期和1个设备)

SELECT * FROM TABLE WHERE date='22.02.2018' AND deviceID= '24'

最大可能结果为86400行
会不会让我的服务器过载太多
问题2:限制为5小时(18000行),这是数据库的问题还是像第一个例子那样加载服务器还是更少

SELECT * FROM TABLE WHERE date='22.02.2018' AND deviceID= '24' LIMIT 18000

问题3:如果我只显示一个db的结果,它会使服务器过载吗

SELECT * FROM TABLE WHERE date='22.02.2018' AND deviceID= '24' LIMIT 1

这是否意味着,如果我有数百万行和1000行将加载相同的服务器,如果我只显示一个结果

w80xi6nr

w80xi6nr1#

读你的问题,对我来说答案是
只需为纬度和经度创建一个单独的表,并将id设为外键并将其保存。

jk9hmnmh

jk9hmnmh2#

在不知道您要运行的确切查询的情况下,我只能猜测最佳结构。话虽如此,您应该以使用每行最小字节数的最佳类型为目标。这将使您的查询更快。
例如,您可以使用以下结构:

create table device (
  id int primary key not null,
  name varchar(20),
  someinfo varchar(100)
);

create table location (
  device_id int not null,
  recorded_at timestamp not null,
  latitude double not null, -- instead of varchar; maybe float?
  longitude double not null, -- instead of varchar; maybe float?
  foreign key (device_id) references device (id)
);

create index ix_loc_dev on location (device_id, recorded_at);

如果包含精确的查询(命名列),我们可以为它们创建更好的索引。
因为您的查询选择性可能不好,所以您的查询可能会运行全表扫描。在本例中,我更进一步,为列使用了尽可能小的数据类型,因此速度更快:

create table location (
  device_id tinyint not null,
  recorded_at timestamp not null,
  latitude float not null,
  longitude float not null,
  foreign key (device_id) references device (id)
);

真的想不出比这个小的了。

xdnvmnnf

xdnvmnnf3#

我能向您推荐的最好的方法是使用时间序列数据库来存储和访问时间序列数据。您可以在本地托管任何类型的时间序列数据库引擎,只需在其访问方法的开发中投入更多的资源,或者使用任何专门的数据库来处理这样的远程通信数据。

zi8p0yeb

zi8p0yeb4#

数百万行不是问题,如果您有一个设计良好的模式和良好的索引,这就是sql数据库要处理的。

使用合适的类型

与其将日期和时间存储为单独的字符串,不如将它们存储为单个字符串 datetime 或分开 date 以及 time 类型。请参阅下面的索引,以了解有关使用哪种索引的更多信息。这两者都更紧凑,允许索引,更快的排序,它使可用的日期和时间函数,而不必做转换。
同样,请确保对纬度和经度使用适当的数字类型。你可能想用 numeric 以确保精度。
因为要存储数十亿行,所以一定要使用 bigint 你的主键。一个普通的整数只能达到大约20亿。

将重复的数据移到另一个表中。

与其在每一行中存储有关设备的信息,不如将其存储在单独的表中。然后只在日志中存储设备的id。这将减少存储大小,并消除由于数据重复而导致的错误。确保将设备id声明为外键,这将提供引用完整性和索引。

添加索引

索引使数据库能够非常、非常有效地搜索数百万或数十亿行。确保经常使用的行上有索引,例如时间戳。
上缺少索引 date 以及 deviceID 很可能就是为什么你的查询这么慢。如果没有索引,mysql必须查看数据库中的每一行,称为全表扫描。这就是为什么你的查询如此缓慢,你缺乏索引。
您可以发现您的查询是否使用索引 explain .

日期时间还是时间+日期?

通常情况下,最好将日期和时间存储在一个单独的列中,通常称为 created_at . 那你可以用 date 像这样去约会。

select *
from gps_logs
where date(created_at) = '2018-07-14'

有个问题。问题是索引如何工作。。。或者不要。因为函数调用, where date(created_at) = '2018-07-14' 不会使用索引。mysql将运行 date(created_at) 在每一行。这意味着一个性能破坏的全表扫描。
你可以通过使用 datetime 列。这将使用索引,而且效率很高。

select *
from gps_logs
where '2018-07-14 00:00:00' <= created_at and created_at < '2018-07-15 00:00:00'

或者你可以分开你的单间 datetime 列为 date 以及 time 列,但这会带来新问题。查询跨越日边界的范围变得很困难。就像你想在不同的时区度过一天。用一根柱子很容易。

select *
from gps_logs
where '2018-07-12 10:00:00' <= created_at and created_at < '2018-07-13 10:00:00'

但更多的是涉及到一个单独的 date 以及 time .

select *
from gps_logs
where (created_date = '2018-07-12' and created_time >= '10:00:00')
  or  (created_date = '2018-07-13' and created_time < '10:00:00');

或者可以切换到具有部分索引(如postgresql)的数据库。部分索引只允许索引值的一部分或函数的结果。postgresql比mysql做得更好。这是我的建议。

尽可能多地使用sql。

例如,如果您想知道每个设备每天有多少个日志条目,而不是把所有的行都拉出来自己计算,您可以使用groupby按设备和日期对它们进行分组。

select gps_device_id, count(id) as num_entries, created_at::date as day 
from gps_logs
group by gps_device_id, day;

 gps_device_id | num_entries |    day     
---------------+-------------+------------
             1 |       29310 | 2018-07-12
             2 |       23923 | 2018-07-11
             2 |       23988 | 2018-07-12

有了这么多的数据,您将非常依赖 group by 以及相关的聚合函数 sum , count , max , min 等等。

避免选择*

如果必须检索86400行,那么仅从数据库中获取所有数据的成本可能会很高。只需获取所需的列,就可以大大加快速度。这意味着使用 select only, the, specific, columns, you, need 而不是 select * .

把它们放在一起。

在postgresql中

postgresql中的模式应该如下所示。

create table gps_devices (
    id serial primary key,
    name text not null

    -- any other columns about the devices
);

create table gps_logs (
    id bigserial primary key,
    gps_device_id int references gps_devices(id),
    created_at timestamp not null default current_timestamp,
    latitude numeric(12,9) not null,
    longitude numeric(12,9) not null
);

create index timestamp_and_device on gps_logs(created_at, gps_device_id);
create index date_and_device on gps_logs((created_at::date), gps_device_id);

查询通常只能对每个表使用一个索引。因为你会经常搜索时间戳和设备id timestamp_and_device 结合索引时间戳和设备id。 date_and_device 是一样的,但它只是时间戳日期部分的部分索引。这将使 where created_at::date = '2018-07-12' and gps_device_id = 42 效率很高。

在mysql中

create table gps_devices (
    id int primary key auto_increment,
    name text not null

    -- any other columns about the devices
);

create table gps_logs (
    id bigint primary key auto_increment,
    gps_device_id int references gps_devices(id),
    foreign key (gps_device_id) references gps_devices(id),
    created_at timestamp not null default current_timestamp,
    latitude numeric(12,9) not null,
    longitude numeric(12,9) not null
);

create index timestamp_and_device on gps_logs(created_at, gps_device_id);

非常相似,但没有部分索引。所以你要么就得一直用裸机 created_at 在你的 where 子句,或切换到单独 date 以及 time 类型。

相关问题