首页 > 数据库 > 数据库开发技术 > HANA数据库大表数据生成及性能体验
HANA数据库功能的强大自不必说。有一张大表,SMP_CLIENT_LOGS:
CREATE [COLUMN] TABLE "SMP_CLIENT_LOGS"("CORRELATIONID" NVARCHAR(64) CS_STRING NOT NULL,"GUID" NVARCHAR(64) CS_STRING NOT NULL,"APPLICATIONCONNECTIONID" NVARCHAR(64) CS_STRING,"APPLICATIONID" NVARCHAR(64) CS_STRING,"DCCOMPONENT" NVARCHAR(256) CS_STRING,"LOCATION" NVARCHAR(256) CS_STRING,"MESSAGETEXT" NCLOB MEMORY THRESHOLD 1000,"MESSAGECODE" NVARCHAR(16) CS_STRING,"ROOTCONTEXTID" NVARCHAR(64) CS_STRING,"SEVERITY" INT CS_INT,"SOURCENAME" NVARCHAR(1280) CS_STRING,"TIMESTAMP" LONGDATE CS_LONGDATE,"TRANSACTIONID" NVARCHAR(64) CS_STRING,"USERNAME" NVARCHAR(64) CS_STRING,"SERIAL" INT CS_INT,PRIMARY KEY ("CORRELATIONID","GUID"));
1. 生成数据.
简单的应该就是不断的执行
INSERT INTO SMP_CLIENT_LOGS SELECT * FROM SMP_CLIENT_LOGS, 但是对于GUID字段之类的,需要一些函数支持。
这里可以使用压测工具JMeter,使用它自带的一些函数,然后多线程并行插入,很快能生成上百万条随机数据。
INSERT 语句如下:
INSERT INTO SMP_CLIENT_LOGS VALUES ('${__UUID}','${__UUID}','${__RandomString(63, abcdefghij0123456789)}','${__RandomString(63, abcdefghij0123456789)}','${__RandomString(${__Random(32,200)}, abcdefghij0123456789)}','${__RandomString(${__Random(32,200)}, abcdefghij0123456789)}','${__RandomString(${__Random(100,2048)}, abcdefghij0123456789)}','${__RandomString(15, abcdefghij0123456789)}','${__RandomString(${__Random(2,63)}, abcdefghij0123456789)}',${__Random(1,6)},'${__RandomString(${__Random(100,1200)}, abcdefghij0123456789)}',now(),'${__RandomString(${__Random(2,63)}, abcdefghij0123456789)}','${__RandomString(${__Random(2,63)}, abcdefghij0123456789)}',${__Random(1,4)})
这样生成的数据,随机性确实可以得到保证。
当然也可以从已有历史数据当中导出, 再导入:
GRANT EXPORT TO GOMOBILE;EXPORT SMP_CLIENT_LOGS_COLUMN AS BINARY INTO '/home/sean/smp/hana/clientlogs_all';
62017141行数据,只要119秒就导出完成,速度确实很快。
2. 现实中的行、列表方式转换及相关性能影响
在现实当中,可能你没有权限export, 仅能通过有限的脚本并且在系统不崩(运行的服务程序依然能提供服务)的情况下完成平衡转换, 该如何有效完成?
备选方法:
1) ALTER TABLE SMP_CLIENT_LOGS TYPE COLUMN;
2) RENAME SMP_CLIENT_LOGS to SMP_CLIENT_LOGS_ORIG
CREATE COLUMN TABLE SMP_CLIENT_LOGS ....
INSERT INTO SMP_CLIENT_LOGS SELECT * FROM SMP_CLIENT_LOGS_ORIG
看起来似乎两个都可以。可是实际上执行起来,发现都无法正常完成。
原因有二:1) 数据量太大,时间上也基本上无法忍受 2) 意外发现,居然会引起内存问题: transaction rolled back by an internal error: Allocation failed ; $size$=32; $name$=libhdbrskernel.so; $type$=pool; $inuse_count$=****; $allocated_size$=****; $failure_type$=GLOBAL_ALLOCATION_LIMIT
出乎我的意料之外。
一般来说, INSERT INTO这类操作,速度应该很快,不太会引起内存问题。
碰到这种问题,该怎么办?
能想到的,最后成功实验成功的是, 将表中的数据按条件分批段INSERT INTO, 使用的脚本如下:
-- DROP TABLE TMP_MIGRATE_RESULT;DOBEGINDECLARE STARTTIME LONGDATE = '2019-08-18 00:00:00';CREATE TABLE TMP_MIGRATE_RESULT(col1 BIGINT, col2 LONGDATE CS_LONGDATE);WHILE ($STARTTIME < CURRENT_TIMESTAMP)DOINSERT INTO SMP_CLIENT_LOGS SELECT * FROM SMP_CLIENT_LOGS_ORIG WHERE TIMESTAMP <= $STARTTIME;DELETE FROM SMP_CLIENT_LOGS_ORIG WHERE TIMESTAMP <= $STARTTIME;-- SELECT ::ROWCOUNT FROM DUMMY;-- Just save the affected row count into the result table-- Don't forget to drop the result table after migrationINSERT INTO TMP_MIGRATE_RESULT VALUES(::ROWCOUNT, $STARTTIME);COMMIT;STARTTIME = ADD_SECONDS($STARTTIME, 3600);END WHILE;END;
上表相当于按每个小时的数据量依次插入,经考查,按小时分段的数据量,最多也就不到2000000条。这样系统的负载会大大减轻。最终6000多万条数据在60来分钟内完成转换。
同样,删除清理数据,可以使用下边的脚本来完成:
DOBEGINDECLARE STARTTIME LONGDATE = '2019-08-15 20:00:23';DECLARE ENDTIME LONGDATE = '2019-08-27 00:00:00';CREATE TABLE TMP_MIGRATE_RESULT(col1 BIGINT, col2 LONGDATE CS_LONGDATE);WHILE ($STARTTIME < $ENDTIME)DODELETE FROM SMP_CLIENT_LOGS WHERE TIMESTAMP <= $STARTTIME;-- SELECT ::ROWCOUNT FROM DUMMY;-- Just save the affected row count into the result table-- Don't forget to drop the result table after migrationINSERT INTO TMP_MIGRATE_RESULT VALUES(::ROWCOUNT, $STARTTIME);COMMIT;STARTTIME = ADD_SECONDS($STARTTIME, 3600);END WHILE;-- last timeDELETE FROM SMP_CLIENT_LOGS WHERE TIMESTAMP <= $ENDTIME;-- SELECT ::ROWCOUNT FROM DUMMY;-- Just save the affected row count into the result table-- Don't forget to drop the result table after migrationINSERT INTO TMP_MIGRATE_RESULT VALUES(::ROWCOUNT, $ENDTIME);COMMIT;END;
虽然有些诡异,但比较安全。
3. 相关系统开销的监控:
1) 磁盘空间开销:
SELECT SCHEMA_NAME,TABLE_NAME,DISK_SIZE FROM PUBLIC.M_TABLE_PERSISTENCE_STATISTICS ORDER BY DISK_SIZE DESC2) 列式表开销:
select SCHEMA_NAME as "Schema", round(sum(MEMORY_SIZE_IN_TOTAL) /1024/1024) as "MB Used" from M_CS_TABLES GROUP by SCHEMA_NAME order by "MB Used" DESC
3) 行式表开销:
select SCHEMA_NAME as "Schema", round(sum(USED_FIXED_PART_SIZE + USED_VARIABLE_PART_SIZE) /1024/1024) as "MB Used" from M_RS_TABLES GROUP by SCHEMA_NAME order by "MB Used" desc
4) 当前相关查询及连接的开销: (没发现有多好用)
selectc.host, c.user_name, c.connection_status, c.transaction_id, s.last_executed_time,round(s.allocated_memory_size/1024,2) as "Alloc Mem (KB)",round(s.used_memory_size/1024,2) as "Used Mem (KB)", s.statement_stringfromm_connections c, m_prepared_statements swheres.connection_id = c.connection_id and c.connection_status != 'IDLE'order bys.allocated_memory_size desc;
4. 关于分区表:
仔细查检了相关官方文档, HANA虽然支持的分区方式蛮多,但是分区表达式支持的并不太多。
可以基于timestamp的YEAR, MONTH, 但是没有基于HOUR的和 dayname dayname的。想想也对,hour的话,前一天的数据和当天的数据有可能就在同一个分区里头了。上边的这个表,数据量确实过于庞大,两三天的数据就达到6000来万条。一个月的量可以达到9亿多条。这6000来万条是17G数据量,一个月可以达到300G左右。
不过,可以做一个冗余列来把数据分散到多个分区里头:
CREATE COLUMN TABLE "SMP_CLIENT_LOGS2"("CORRELATIONID" NVARCHAR(64) CS_STRING NOT NULL,"GUID" NVARCHAR(64) CS_STRING NOT NULL,"TIMESTAMP" LONGDATE CS_LONGDATE,"TRANSACTIONID" NVARCHAR(64) CS_STRING,"USERNAME" NVARCHAR(64) CS_STRING,"SERIAL" INT CS_INT,"DAYV" INT CS_INT,PRIMARY KEY ("CORRELATIONID","GUID","DAYV")) PARTITION BY RANGE(DAYV) (PARTITION VALUES=0,PARTITION VALUES=1,PARTITION VALUES=2,PARTITION VALUES=3,PARTITION VALUES=4,PARTITION VALUES=5,PARTITION VALUES=6) ;
上边是按照一周的第几天来分的。假如系统始终只保留6天的数据,那就比较好玩了。比如,现在是周6, 那就可以把partition value = 0的数据全部archive掉 (archive的功能可以单独实现),非常简洁。
5. 关于SELECT 语句引起的内存问题
总结以后,发现还是跟返回的数据量有些关系。
SELECT * FROM SMP_CLIENT_LOGSSELECT * FROM SMP_CLIENT_LOGS ORDER BY timestampSELECT * FROM SMP_CLIENT_LOGS WHERE SECONDS_BETWEEN(timestamp, '2019-08-26 05:00:00') BETWEEN 0 AND 7200"SELECT * FROM SMP_CLIENT_LOGS WHERE TO_VARCHAR(timestamp, 'YYYY-MM-DD HH') = '2019-08-25 05'
结果发现,前边两种都会挂:
Exception in thread "main" com.sap.db.jdbc.exceptions.jdbc40.SQLTransactionRollbackException: [129]: transaction rolled back by an internal error: Allocation failed ; $size$=32; $name$=libhdbrskernel.so; $type$=pool; $inuse_count$=11223397; $allocated_size$=565437665; $failure_type$=GLOBAL_ALLOCATION_LIMITat com.sap.db.jdbc.exceptions.jdbc40.SQLTransactionRollbackException.createException(SQLTransactionRollbackException.java:40)at com.sap.db.jdbc.exceptions.SQLExceptionSapDB.createException(SQLExceptionSapDB.java:304)at com.sap.db.jdbc.exceptions.SQLExceptionSapDB.generateDatabaseException(SQLExceptionSapDB.java:174)at com.sap.db.jdbc.packet.ReplyPacket.buildExceptionChain(ReplyPacket.java:104)at com.sap.db.jdbc.ConnectionSapDB.execute(ConnectionSapDB.java:1142)at com.sap.db.jdbc.ConnectionSapDB.execute(ConnectionSapDB.java:886)at com.sap.db.jdbc.FetchInfo.executeFetch(FetchInfo.java:162)at com.sap.db.jdbc.FetchInfo.executeFetchNext(FetchInfo.java:157)at com.sap.db.jdbc.ResultSetSapDB.fetchNextChunk(ResultSetSapDB.java:2424)at com.sap.db.jdbc.ResultSetSapDB.next(ResultSetSapDB.java:419)at com.sap.db.jdbc.trace.ResultSet.next(ResultSet.java:269)at com.foo.db.ClientLogMemory.testSQL(ClientLogMemory.java:69)at com.foo.db.ClientLogMemory.main(ClientLogMemory.java:107)
原文作者:数据库杂记;如有侵权,请联系管理员删除。