示例我有一些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行将加载相同的服务器
答案 0 :(得分:4)
数百万行不是问题,这是SQL数据库设计用来处理的, 如果 ,您具有设计良好的架构和良好的索引。
不要将日期和时间存储为单独的字符串,而可以将它们存储为单个datetime
或单独的date
和time
类型。有关使用哪个索引的更多信息,请参见下面的索引。这既紧凑,允许索引编制,排序速度更快,并且无需进行转换即可使用date and time functions。
类似地,请确保对纬度和经度使用适当的numeric type。您可能需要使用numeric
来确保精度。
由于您将要存储数十亿行,因此请确保将bigint
用作主键。常规int最多只能达到20亿。
与其在每一行中存储有关设备的信息,不如将其存储在单独的表中。然后仅将设备的ID存储在日志中。这将减少您的存储空间,并消除由于数据重复而导致的错误。确保将设备ID声明为外键,这将提供referential integrity和索引。
Indexes是使数据库非常非常有效地搜索数百万或数十亿行的工具。确保您经常使用的行上有索引,例如时间戳。
date
和deviceID
上缺少索引可能是您查询如此缓慢的原因。如果没有索引,MySQL必须查看数据库中称为full table scan的每一行。这就是为什么您的查询如此之慢,缺少索引的原因。
您可以通过explain
发现查询是否正在使用索引。
datetime
或time
+ 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
列拆分为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');
或者您可以使用partial indexes like Postgresql切换到数据库。部分索引允许您仅索引值的一部分或函数的结果。而且Postgresql在很多方面都比MySQL更好。这就是我的建议。
例如,如果您想知道每台设备每天有多少个日志条目,而不是拉出所有行并自己计算,则可以使用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,例如sum
,count
,max
,{{1 }}等。
min
如果必须检索86400行,那么简单地从数据库中获取所有数据的成本可能会很高。通过仅获取所需的列,可以大大加快此过程。这意味着使用select *
而不是select only, the, specific, columns, you, need
。
您在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
非常有效。
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
,要么切换为单独的where
和date
类型。
答案 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。