超大数据量表的处理,欢迎达人门来讨论!

shiosank 2009-04-09 04:27:48
公司做的是地理信息系统,如果项目全面上马,以后每天接收到的实时数据可能会达到35000000条,保存一个月的记录。也就是10亿条记录。
目前的数据库结构是将这些数据存入到一张表中。
表结构如下:
CREATE TABLE [dbo].[TrackingData](
[ID] [bigint] IDENTITY(1,1) NOT NULL,
[DevID] [varchar](12) COLLATE Chinese_PRC_CI_AS NULL,
[SendTime] [datetime] NULL,
[Latitude] [float] NULL,
[Longitude] [float] NULL,
[UpPeopleNum] [tinyint] NULL,
[DPeopleNum] [tinyint] NULL,
[Speed] [smallint] NULL,
[Angle] [smallint] NULL,
CONSTRAINT [PK_TrackingData] PRIMARY KEY CLUSTERED

最近一直在学习SQL SERVER 高级知识,了解了SQL SERVER 的存储结构和索引的知识。每个数据行占62个字节,用公式估算了下:10亿条数据大约占60G的空间。

1.SQL SERVER 中一张表能存的下10亿条数据吗?
2.这样的表结构能否适应如此巨大的数据存储量的,是不是应该将这个大表分解成很多张小表?
3.目前表中只有基于ID的簇索引,经常进行的查询语句的查询参数通常是DevID,SendTime,
如此频繁的插入操作如果建立非簇索引,索引的维护开销会很大吧,请给个折中的方案?

欢迎大家来讨论,散分中!!!!!

...全文
146 22 打赏 收藏 转发到动态 举报
写回复
用AI写文章
22 条回复
切换为时间正序
请发表友善的回复…
发表回复
AI1983 2009-04-10
  • 打赏
  • 举报
回复
个人觉得还是分出几张表比较好,对更新和查询的速度都有优化

标记一下继续学习
zsforever 2009-04-10
  • 打赏
  • 举报
回复
如此海量和增长速度,sqlserver有点难吧
orochi_gao 2009-04-10
  • 打赏
  • 举报
回复
这个数据量一个月60G(如果建非聚集索引会更多),这只是一个月的数据,一个月之后呢?那时数据如何处理?
Teng_s2000 2009-04-10
  • 打赏
  • 举报
回复
按照时间进行分区吧

不过这么大的数据量SQL应该是可以对付的,但是个人感觉还是Oracle号些
no_mIss 2009-04-10
  • 打赏
  • 举报
回复
考虑下成本的吧..呵呵
一个月60G而已.一年不过1T,用sql server 足够了
gxg353 2009-04-10
  • 打赏
  • 举报
回复
学习 关注
you_tube 2009-04-10
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 no_mIss 的回复:]
引用 14 楼 jinjazz 的回复:
我的建议是抛弃sqlserver...

那用什么来弄?
[/Quote]
oracle
no_mIss 2009-04-10
  • 打赏
  • 举报
回复
[Quote=引用 14 楼 jinjazz 的回复:]
我的建议是抛弃sqlserver...
[/Quote]
那用什么来弄?
jinjazz 2009-04-10
  • 打赏
  • 举报
回复
我的建议是抛弃sqlserver...
kye_jufei 2009-04-10
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 HEROWANG 的回复:]
SQL code-- 进行演示操作前, 先备份, 以便可以在演示完成后, 恢复到原始状态
USE master
-- 备份
BACKUP DATABASE AdventureWorks
TO DISK = 'AdventureWorks.bak'
WITH FORMAT

---- 恢复
--RESTORE DATABASE AdventureWorks
-- FROM DISK = 'AdventureWorks.bak'
-- WITH REPLACE
GO

--=========================================
-- 转换为分区表
--=========================================…
[/Quote]
up
claro 2009-04-09
  • 打赏
  • 举报
回复
帮顶。
nzperfect 2009-04-09
  • 打赏
  • 举报
回复
[Quote=引用楼主 shiosank 的帖子:]
公司做的是地理信息系统,如果项目全面上马,以后每天接收到的实时数据可能会达到35000000条,保存一个月的记录。也就是10亿条记录。
目前的数据库结构是将这些数据存入到一张表中。
表结构如下:
CREATE TABLE [dbo].[TrackingData](
[ID] [bigint] IDENTITY(1,1) NOT NULL,
[DevID] [varchar](12) COLLATE Chinese_PRC_CI_AS NULL,
[SendTime] [datetime] NULL,
[Latitude] [float] NULL,
[Longitude] [float] NULL,
.....
1.SQL SERVER 中一张表能存的下10亿条数据吗?
2.这样的表结构能否适应如此巨大的数据存储量的,是不是应该将这个大表分解成很多张小表?
3.目前表中只有基于ID的簇索引,经常进行的查询语句的查询参数通常是DevID,SendTime,
如此频繁的插入操作如果建立非簇索引,索引的维护开销会很大吧,请给个折中的方案?
[/Quote]
1.SQL SERVER 中一张表能存的下10亿条数据吗?
当然可以.
2.这样的表结构能否适应如此巨大的数据存储量的,是不是应该将这个大表分解成很多张小表?
如果有条件做最好做成分区表.
3.目前表中只有基于ID的簇索引,经常进行的查询语句的查询参数通常是DevID,SendTime,
如此频繁的插入操作如果建立非簇索引,索引的维护开销会很大吧,请给个折中的方案?
肯定会有影响,这个要看你的硬件的处理能力。
Zoezs 2009-04-09
  • 打赏
  • 举报
回复

  --drop database dbPartitionTest
  --测试数据库
  create database dbPartitionTest
  go
  use
  dbPartitionTest
  go
  --增加分组
  alter database dbPartitionTest ADD FILEGROUP P200801
  alter database dbPartitionTest ADD FILEGROUP P200802
  alter database dbPartitionTest ADD FILEGROUP P200803
  go
  --分区函数
  CREATE PARTITION FUNCTION part_Year(datetime)
  AS RANGE LEFT FOR VALUES
  (
  ’20080131 23:59:59.997’,’20080229 23:59:59.997’,’20080331 23:59:59.997’
  )
  go
  --增加文件组
  ALTER DATABASE dbPartitionTest ADD FILE (NAME = N’P200801’,FILENAME = N’c:tb_P200801.ndf’,SIZE = 1MB,MAXSIZE = 500MB,FILEGROWTH = 1MB)TO FILEGROUP P200801
  ALTER DATABASE dbPartitionTest ADD FILE (NAME = N’P200802’,FILENAME = N’c:tb_P200802.ndf’,SIZE = 1MB,MAXSIZE = 500MB,FILEGROWTH = 1MB)TO FILEGROUP P200802
  ALTER DATABASE dbPartitionTest ADD FILE (NAME = N’P200803’,FILENAME = N’c:tb_P200803.ndf’,SIZE = 1MB,MAXSIZE = 500MB,FILEGROWTH = 1MB)TO FILEGROUP P200803
  go
  --分区架构
  CREATE PARTITION SCHEME part_YearScheme AS PARTITION part_Year  TO (P200801,P200802,P200803,[PRIMARY])
  go
  CREATE TABLE [dbo].t_part
  (name varchar(100) default newid(),date datetime NOT NULL)
  ON part_YearScheme (date)
  go
  --添加测试数据,每天1条
  declare @date datetime
  set @date=’2007-12-31’
  while @date<=’2008-04-0’
  1 begin
  insert into t_part(date)values(@date)
  set @date=@date+1
  end
  go
  --查询数据分布在哪些分区
  select $partition.part_Year(date) as 分区编号,* from t_part order by date
  --查询数据库文件
  go
  sp_helpfile

  • 打赏
  • 举报
回复
查询分区信息:

;WITH
TBINFO AS(
SELECT
SchemaName = S.name,
TableName = TB.name,
PartitionScheme = PS.name,
PartitionFunction = PF.name,
PartitionFunctionRangeType = CASE
WHEN boundary_value_on_right = 0 THEN 'LEFT'
ELSE 'RIGHT' END,
PartitionFunctionFanout = PF.fanout,
SchemaID = S.schema_id,
ObjectID = TB.object_id,
PartitionSchemeID = PS.data_space_id,
PartitionFunctionID = PS.function_id
FROM sys.schemas S
INNER JOIN sys.tables TB
ON S.schema_id = TB.schema_id
INNER JOIN sys.indexes IDX
on TB.object_id = IDX.object_id
AND IDX.index_id < 2
INNER JOIN sys.partition_schemes PS
ON PS.data_space_id = IDX.data_space_id
INNER JOIN sys.partition_functions PF
ON PS.function_id = PF.function_id
),
PF1 AS(
SELECT
PFP.function_id, PFR.boundary_id, PFR.value,
Type = CONVERT(sysname,
CASE T.name
WHEN 'numeric' THEN 'decimal'
WHEN 'real' THEN 'float'
ELSE T.name END
+ CASE
WHEN T.name IN('decimal', 'numeric')
THEN QUOTENAME(RTRIM(PFP.precision)
+ CASE WHEN PFP.scale > 0 THEN ',' + RTRIM(PFP.scale) ELSE '' END, '()')
WHEN T.name IN('float', 'real')
THEN QUOTENAME(PFP.precision, '()')
WHEN T.name LIKE 'n%char'
THEN QUOTENAME(PFP.max_length / 2, '()')
WHEN T.name LIKE '%char' OR T.name LIKE '%binary'
THEN QUOTENAME(PFP.max_length, '()')
ELSE '' END)
FROM sys.partition_parameters PFP
LEFT JOIN sys.partition_range_values PFR
ON PFR.function_id = PFP.function_id
AND PFR.parameter_id = PFP.parameter_id
INNER JOIN sys.types T
ON PFP.system_type_id = T.system_type_id
),
PF2 AS(
SELECT * FROM PF1
UNION ALL
SELECT
function_id, boundary_id = boundary_id - 1, value, type
FROM PF1
WHERE boundary_id = 1
),
PF AS(
SELECT
B.function_id, boundary_id = ISNULL(B.boundary_id + 1, 1),
value = STUFF(
CASE
WHEN A.boundary_id IS NULL THEN ''
ELSE ' AND [partition_column_name] ' + PF.LessThan + ' ' + CONVERT(varchar(max), A.value) END
+ CASE
WHEN A.boundary_id = 1 THEN ''
ELSE ' AND [partition_column_name] ' + PF.MoreThan + ' ' + CONVERT(varchar(max), B.value) END,
1, 5, ''),
B.Type
FROM PF1 A
RIGHT JOIN PF2 B
ON A.function_id = B.function_id
AND (A.boundary_id - 1 = B.boundary_id
OR(A.boundary_id IS NULL AND B.boundary_id IS NULL))
INNER JOIN(
SELECT
function_id,
LessThan = CASE
WHEN boundary_value_on_right = 0 THEN '<='
ELSE '<' END,
MoreThan = CASE
WHEN boundary_value_on_right = 0 THEN '>'
ELSE '>=' END
FROM sys.partition_functions
)PF
ON B.function_id = PF.function_id
),
PS AS(
SELECT
DDS.partition_scheme_id, DDS.destination_id,
FileGroupName = FG.name, IsReadOnly = FG.is_read_only
FROM sys.destination_data_spaces DDS
INNER JOIN sys.filegroups FG
ON DDS.data_space_id = FG.data_space_id
),
PINFO AS(
SELECT
RowID = ROW_NUMBER() OVER(ORDER BY SchemaID, ObjectID, PS.destination_id),
TB.SchemaName, TB.TableName,
TB.PartitionScheme, PS.destination_id, PS.FileGroupName, PS.IsReadOnly,
TB.PartitionFunction, TB.PartitionFunctionRangeType, TB.PartitionFunctionFanout,
PF.boundary_id, PF.Type, PF.value
FROM TBINFO TB
INNER JOIN PS
ON TB.PartitionSchemeID = PS.partition_scheme_id
LEFT JOIN PF
ON TB.PartitionFunctionID = PF.function_id
AND PS.destination_id = PF.boundary_id
)
SELECT
RowID,
SchemaName = CASE destination_id
WHEN 1 THEN SchemaName
ELSE N'' END,
TableName = CASE destination_id
WHEN 1 THEN TableName
ELSE N'' END,
PartitionScheme = CASE destination_id
WHEN 1 THEN PartitionScheme
ELSE N'' END,
destination_id, FileGroupName, IsReadOnly,
PartitionFunction = CASE destination_id
WHEN 1 THEN PartitionFunction
ELSE N'' END,
PartitionFunctionRangeType = CASE destination_id
WHEN 1 THEN PartitionFunctionRangeType
ELSE N'' END,
PartitionFunctionFanout = CASE destination_id
WHEN 1 THEN CONVERT(varchar(20), PartitionFunctionFanout)
ELSE N'' END,
boundary_id = ISNULL(CONVERT(varchar(20), boundary_id), ''),
Type = ISNULL(Type, N''),
value = CASE PartitionFunctionFanout
WHEN 1 THEN '<ALL Data>'
ELSE ISNULL(value, N'<NEXT USED>') END
FROM PINFO
ORDER BY RowID
  • 打赏
  • 举报
回复
-- 进行演示操作前, 先备份, 以便可以在演示完成后, 恢复到原始状态
USE master
-- 备份
BACKUP DATABASE AdventureWorks
TO DISK = 'AdventureWorks.bak'
WITH FORMAT

---- 恢复
--RESTORE DATABASE AdventureWorks
-- FROM DISK = 'AdventureWorks.bak'
-- WITH REPLACE
GO

--=========================================
-- 转换为分区表
--=========================================
USE AdventureWorks
GO

-- 1. 创建分区函数
-- a. 适用于存储历史存档记录的分区表的分区函数
DECLARE @dt datetime
SET @dt = '20020101'
CREATE PARTITION FUNCTION PF_HistoryArchive(datetime)
AS RANGE RIGHT
FOR VALUES(
@dt,
DATEADD(Year, 1, @dt))

-- b. 适用于存储历史记录的分区表的分区函数
--DECLARE @dt datetime
SET @dt = '20030901'
CREATE PARTITION FUNCTION PF_History(datetime)
AS RANGE RIGHT
FOR VALUES(
@dt,
DATEADD(Month, 1, @dt), DATEADD(Month, 2, @dt), DATEADD(Month, 3, @dt),
DATEADD(Month, 4, @dt), DATEADD(Month, 5, @dt), DATEADD(Month, 6, @dt),
DATEADD(Month, 7, @dt), DATEADD(Month, 8, @dt), DATEADD(Month, 9, @dt),
DATEADD(Month, 10, @dt), DATEADD(Month, 11, @dt), DATEADD(Month, 12, @dt))
GO

-- 2. 创建分区架构
-- a. 适用于存储历史存档记录的分区表的分区架构
CREATE PARTITION SCHEME PS_HistoryArchive
AS PARTITION PF_HistoryArchive
TO([PRIMARY], [PRIMARY], [PRIMARY])

-- b. 适用于存储历史记录的分区表的分区架构
CREATE PARTITION SCHEME PS_History
AS PARTITION PF_History
TO([PRIMARY], [PRIMARY],
[PRIMARY], [PRIMARY], [PRIMARY],
[PRIMARY], [PRIMARY], [PRIMARY],
[PRIMARY], [PRIMARY], [PRIMARY],
[PRIMARY], [PRIMARY], [PRIMARY])
GO

-- 3. 删除索引
-- a. 删除存储历史存档记录的表中的索引
DROP INDEX Production.TransactionHistoryArchive.IX_TransactionHistoryArchive_ProductID
DROP INDEX Production.TransactionHistoryArchive.IX_TransactionHistoryArchive_ReferenceOrderID_ReferenceOrderLineID

-- b. 删除存储历史记录的表中的索引
DROP INDEX Production.TransactionHistory.IX_TransactionHistory_ProductID
DROP INDEX Production.TransactionHistory.IX_TransactionHistory_ReferenceOrderID_ReferenceOrderLineID
GO

-- 4. 转换为分区表
-- a. 将存储历史存档记录的表转换为分区表
ALTER TABLE Production.TransactionHistoryArchive
DROP CONSTRAINT PK_TransactionHistoryArchive_TransactionID
WITH(
MOVE TO PS_HistoryArchive(TransactionDate))

-- b.将存储历史记录的表转换为分区表
ALTER TABLE Production.TransactionHistory
DROP CONSTRAINT PK_TransactionHistory_TransactionID
WITH(
MOVE TO PS_History(TransactionDate))
GO

-- 5. 恢复主键
-- a. 恢复存储历史存档记录的分区表的主键
ALTER TABLE Production.TransactionHistoryArchive
ADD CONSTRAINT PK_TransactionHistoryArchive_TransactionID
PRIMARY KEY CLUSTERED(
TransactionID,
TransactionDate)

-- b. 恢复存储历史记录的分区表的主键
ALTER TABLE Production.TransactionHistory
ADD CONSTRAINT PK_TransactionHistory_TransactionID
PRIMARY KEY CLUSTERED(
TransactionID,
TransactionDate)
GO

-- 6. 恢复索引
-- a. 恢复存储历史存档记录的分区表的索引
CREATE INDEX IX_TransactionHistoryArchive_ProductID
ON Production.TransactionHistoryArchive(
ProductID)

CREATE INDEX IX_TransactionHistoryArchive_ReferenceOrderID_ReferenceOrderLineID
ON Production.TransactionHistoryArchive(
ReferenceOrderID,
ReferenceOrderLineID)

-- b. 恢复存储历史记录的分区表的索引
CREATE INDEX IX_TransactionHistory_ProductID
ON Production.TransactionHistory(
ProductID)

CREATE INDEX IX_TransactionHistory_ReferenceOrderID_ReferenceOrderLineID
ON Production.TransactionHistory(
ReferenceOrderID,
ReferenceOrderLineID)
GO

-- 7. 查看分区表的相关信息
SELECT
SchemaName = S.name,
TableName = TB.name,
PartitionScheme = PS.name,
PartitionFunction = PF.name,
PartitionFunctionRangeType = CASE
WHEN boundary_value_on_right = 0 THEN 'LEFT'
ELSE 'RIGHT' END,
PartitionFunctionFanout = PF.fanout,
SchemaID = S.schema_id,
ObjectID = TB.object_id,
PartitionSchemeID = PS.data_space_id,
PartitionFunctionID = PS.function_id
FROM sys.schemas S
INNER JOIN sys.tables TB
ON S.schema_id = TB.schema_id
INNER JOIN sys.indexes IDX
on TB.object_id = IDX.object_id
AND IDX.index_id < 2
INNER JOIN sys.partition_schemes PS
ON PS.data_space_id = IDX.data_space_id
INNER JOIN sys.partition_functions PF
ON PS.function_id = PF.function_id
GO

--=========================================
-- 移动分区表数据
--=========================================
-- 1. 为存储历史存档记录的分区表增加分区, 并接受从历史记录分区表移动过来的数据
-- a. 修改分区架构, 增加用以接受新分区的文件组
ALTER PARTITION SCHEME PS_HistoryArchive
NEXT USED [PRIMARY]

-- b. 修改分区函数, 增加分区用以接受从历史记录分区表移动过来的数据
DECLARE @dt datetime
SET @dt = '20030901'
ALTER PARTITION FUNCTION PF_HistoryArchive()
SPLIT RANGE(@dt)

-- c. 将历史记录表中的过期数据移动到历史存档记录表中
ALTER TABLE Production.TransactionHistory
SWITCH PARTITION 2
TO Production.TransactionHistoryArchive PARTITION $PARTITION.PF_HistoryArchive(@dt)

-- d. 将接受到的数据与原来的分区合并
ALTER PARTITION FUNCTION PF_HistoryArchive()
MERGE RANGE(@dt)
GO

-- 2. 将存储历史记录的分区表中不包含数据的分区删除, 并增加新的分区以接受新数据
-- a. 合并不包含数据的分区
DECLARE @dt datetime
SET @dt = '20030901'
ALTER PARTITION FUNCTION PF_History()
MERGE RANGE(@dt)

-- b. 修改分区架构, 增加用以接受新分区的文件组
ALTER PARTITION SCHEME PS_History
NEXT USED [PRIMARY]

-- c. 修改分区函数, 增加分区用以接受新数据
SET @dt = '20041001'
ALTER PARTITION FUNCTION PF_History()
SPLIT RANGE(@dt)
GO


--=========================================
-- 清除历史存档记录中的过期数据
--=========================================
-- 1. 创建用于保存过期的历史存档数据的表
CREATE TABLE Production.TransactionHistoryArchive_2001_temp(
TransactionID int NOT NULL,
ProductID int NOT NULL,
ReferenceOrderID int NOT NULL,
ReferenceOrderLineID int NOT NULL
DEFAULT ((0)),
TransactionDate datetime NOT NULL
DEFAULT (GETDATE()),
TransactionType nchar(1) NOT NULL,
Quantity int NOT NULL,
ActualCost money NOT NULL,
ModifiedDate datetime NOT NULL
DEFAULT (GETDATE()),
CONSTRAINT PK_TransactionHistoryArchive_2001_temp_TransactionID
PRIMARY KEY CLUSTERED(
TransactionID,
TransactionDate)
)

-- 2. 将数据从历史存档记录分区表移动到第1步创建的表中
ALTER TABLE Production.TransactionHistoryArchive
SWITCH PARTITION 1
TO Production.TransactionHistoryArchive_2001_temp

-- 3. 删除不再包含数据的分区
DECLARE @dt datetime
SET @dt = '20020101'
ALTER PARTITION FUNCTION PF_HistoryArchive()
MERGE RANGE(@dt)

-- 4. 修改分区架构, 增加用以接受新分区的文件组
ALTER PARTITION SCHEME PS_HistoryArchive
NEXT USED [PRIMARY]

-- 5. 修改分区函数, 增加分区用以接受新数据
SET @dt = '20040101'
ALTER PARTITION FUNCTION PF_HistoryArchive()
SPLIT RANGE(@dt)
shiosank 2009-04-09
  • 打赏
  • 举报
回复
谁能给点关于分区表的资料,谢谢!!
taoistong 2009-04-09
  • 打赏
  • 举报
回复
1.SQL SERVER 中一张表能存的下10亿条数据吗?

2.这样的表结构能否适应如此巨大的数据存储量的,是不是应该将这个大表分解成很多张小表?
分区也一样
最好有一张是历史数据,一张是当前数据。查询和插入效果会有很大提高的!
3.目前表中只有基于ID的簇索引,经常进行的查询语句的查询参数通常是DevID,SendTime,
在DevID,SendTime上创建非簇索引。不会很大,如果你的sendtime是依次向下的。不会引起拆分数据页面!

qizhengsheng 2009-04-09
  • 打赏
  • 举报
回复
等星星和钻石
  • 打赏
  • 举报
回复
1、10亿数据,60G,应该能吧。
3、在devid,sendtime上面建立普通索引就行。

如果可以的话,还是分区吧
加载更多回复(2)

22,210

社区成员

发帖
与我相关
我的任务
社区描述
MS-SQL Server 疑难问题
社区管理员
  • 疑难问题社区
  • 尘觉
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧