具有百万行的数据库表

时间:2018-07-13 01:24:59

标签: mysql sql database

示例我有一些gps设备每秒钟将信息发送到我的数据库

因此1台设备在mysql数据库中使用这些列(8)创建 1行

  

id = 12341 日期 = 22.02.2018 时间 = 22:40   语言 = 22.236558789 经度 = 78.9654582 设备ID = 24 名称 =设备名称 someinfo = asdadadasd

所以在1分钟内创建60行,在24小时内创建864000行 并持续1个月(31天) 2678400 ROWS

因此,有1台设备每月在我的数据库表中创建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的1个结果,它将使服务器超载

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

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

4 个答案:

答案 0 :(得分:4)

数百万行不是问题,这是SQL数据库设计用来处理的, 如果 ,您具有设计良好的架构和良好的索引。

使用正确的类型

不要将日期和时间存储为单独的字符串,而可以将它们存储为单个datetime或单独的datetime类型。有关使用哪个索引的更多信息,请参见下面的索引。这既紧凑,允许索引编制,排序速度更快,并且无需进行转换即可使用date and time functions

类似地,请确保对纬度和经度使用适当的numeric type。您可能需要使用numeric来确保精度。

由于您将要存储数十亿行,因此请确保将bigint用作主键。常规int最多只能达到20亿。

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

与其在每一行中存储有关设备的信息,不如将其存储在单独的表中。然后仅将设备的ID存储在日志中。这将减少您的存储空间,并消除由于数据重复而导致的错误。确保将设备ID声明为外键,这将提供referential integrity和索引。

添加索引

Indexes是使数据库非常非常有效地搜索数百万或数十亿行的工具。确保您经常使用的行上有索引,例如时间戳。

datedeviceID上缺少索引可能是您查询如此缓慢的原因。如果没有索引,MySQL必须查看数据库中称为full table scan的每一行。这就是为什么您的查询如此之慢,缺少索引的原因。

您可以通过explain发现查询是否正在使用索引。

datetimetime + date

通常,最好将日期和时间存储在通常称为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列拆分为datetime列,但这会带来新的问题。查询跨越一天边界的范围变得困难。也许您想要在其他时区度过一天。单列即可轻松实现。

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

但是它与单独的datetime有关。

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');

或者您可以使用partial indexes like Postgresql切换到数据库。部分索引允许您仅索引值的一部分或函数的结果。而且Postgresql在很多方面都比MySQL更好。这就是我的建议。

在SQL中做尽可能多的工作。

例如,如果您想知道每台设备每天有多少个日志条目,而不是拉出所有行并自己计算,则可以使用group by按设备和日期分组

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和相关的aggregate functions,例如sumcountmax,{{1 }}等。

避免使用min

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

将它们放在一起。

在PostgreSQL中

您在PostgreSQL中的架构应如下所示。

select *

查询通常只能在每个表中使用一个索引。由于您将一起搜索时间戳和设备ID,因此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是同一件事,但是它只是时间戳的日期部分的部分索引。这将使date_and_device非常有效。

在MySQL中

where created_at::date = '2018-07-12' and gps_device_id = 42

非常相似,但没有部分索引。因此,您要么需要始终在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,要么切换为单独的wheredate类型。

答案 1 :(得分:1)

只需阅读您的问题,对我来说答案是

只需为纬度和经度创建一个单独的表,然后将您的ID外键保存下来即可。

答案 2 :(得分:1)

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

例如,您可以使用以下结构:

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)
);

真的想不出比这更小的东西。

答案 3 :(得分:0)

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