国外游戏ui设计网站,wordpress主题yeti1.9.2,简单手机app制作,网站开发怎么做菜菜呀#xff0c;咱们业务BJKJ有个表数据需要做迁移程序员主力 Y总现在有多少数据#xff1f;菜菜大约21亿吧#xff0c;2017年以前的数据没有业务意义了#xff0c;给你半天时间把这个事搞定#xff0c;绩效给你A程序员主力 Y总有绩效奖金吗#xff1f;菜菜钱的事你去问… 菜菜呀咱们业务BJKJ有个表数据需要做迁移程序员主力 Y总现在有多少数据菜菜大约21亿吧2017年以前的数据没有业务意义了给你半天时间把这个事搞定绩效给你A程序员主力 Y总有绩效奖金吗菜菜钱的事你去问X总我当家不管钱程序员主力 Y总...........菜菜问题分析经过几分钟的排查数据库情况如下1. 数据库采用Sqlserver 2008 R2单表数据量21亿2. 无水平或者垂直切分但是采用了分区表。分区表策略是按时间降序分的区将近30个分区。正因为分区表的原因系统才保证了在性能不是太差的情况下坚持至今。3. 此表除聚集索引之外无其他索引无主键主键其实是利用索引来快速查重的。所以在频繁插入新数据的情况下索引调整所耗费的性能比较低。至于聚集索引和非聚集索引等知识请各位移步google或者百度。 至于业务不是太复杂。经过相关人员咨询大约40%的请求为单条Insert大约60%的请求为按class_id 和in_time倒序分页获取数据。Select请求全部命中聚集索引所以性能非常高。这也是聚集索引之所以这样设计的目的。 解决问题 由于单表数据量已经超过21亿并且2017年以前的数据几乎不影响业务所以决定把2017年以前不包括2017年的数据迁移到新表仅供以后特殊业务查询使用。经过查询大约有9亿数据量。数据迁移工作包括三个个步骤1. 从源数据表查询出要迁移的数据2. 把数据插入新表3. 把旧表的数据删除传统做法 这里申明一点就算是传统的做法也需要分页获取源数据因为你的内存一次性装载不下9亿条数据。1. 从源数据表分页获取数据具体分页条数太少则查询原表太频繁太多则查询太慢。SQL语句类似于SELECT * FROM (SELECT *,ROW_NUMBER() OVER(ORDER BY class_id,in_time) p FROM tablexx WHERE in_time 2017.1.1 ) t WHERE t.p BETWEEN 1 AND 1002. 把查询出来的数据插入目标数据表这里强调一点一定不要用单条插入策略必须用批量插入。3. 把数据删除其实这里删除还是有一个小难点表没有标示列。这里不展开因为这不是菜菜要说的重点。 如果你的数据量不大以上方法完全没有问题但是在9亿这个数字前面以上方法显得心有余而力不足。一个字慢太慢非常慢。可以大体算一下假如每秒可以迁移1000条数据大约需要的时间为(单位分)900000000/1000/6015000(分钟)大约需要10天^ V ^改进做法以上的传统做法弊端在哪里呢1. 在9亿数据前查询必须命中索引就算是非聚集索引菜菜也不推荐首推聚集索引。2. 如果你了解索引的原理你应该明白不停的插入新数据的时候索引在不停的更新调整以保持树的平衡等特性。尤其是聚集索引影响甚大因为还需要移动实际的数据。提取以上两点共同的要素那就是聚集索引。相应的解决方案也就应运而生1. 按照聚集索分页引查询数据2. 批量插入数据迎合聚集索引即按照聚集索引的顺序批量插入。3. 按照聚集索引顺序批量删除由于做了表分区如果有一种方式把2017年以前的分区直接在磁盘物理层面从当前表剥离然后挂载到另外一个表可算是神级操作。有谁能指导一下菜菜感激不尽扩展阅读1. 一个表的聚集索引的顺序就是实际数据文件的顺序映射到磁盘上本质上位于同一个磁道上所以操作的时候磁盘的磁头不必跳跃着去操作。2. 存储在硬盘中的每个文件都可分为两部分文件头和存储数据的数据区。文件头用来记录文件名、文件属性、占用簇号等信息文件头保存在一个簇并映射在FAT表文件分配表中。而真实的数据则是保存在数据区当中的。平常所做的删除其实是修改文件头的前2个代码这种修改映射在FAT表中就为文件作了删除标记并将文件所占簇号在FAT表中的登记项清零表示释放空间这也就是平常删除文件后硬盘空间增大的原因。而真正的文件内容仍保存在数据区中并未得以删除。要等到以后的数据写入把此数据区覆盖掉这样才算是彻底把原来的数据删除。如果不被后来保存的数据覆盖它就不会从磁盘上抹掉。NetCore 代码(实际运行代码)1. 第一步由于聚集索引需要class_id 所以宁可花2-4秒时间把要操作的class_id查询出来(ORM为dapper)并且升序排列 DateTime dtMax DateTime.Parse(2017.1.1); var allClassId DBProxy.GeSourcetLstClassId(dtMax)?.OrderBy(ss);2. 按照第一步class_id 列表顺序查询数据每个class_id 分页获取然后插入目标表全部完成然后删除源表相应class_id的数据。(全部命中聚集索引) D int pageIndex 1; //页码 int pageCount 20000;//每页的数据条数 DataTable tempData null; int successCount 0; foreach (var classId in allClassId) { tempData null; pageIndex 1; while (true) { int startIndex (pageIndex - 1) * pageCount1; int endIndex pageIndex * pageCount; tempData DBProxy.GetSourceDataByClassIdTable(dtMax, classId, startIndex, endIndex); if (tempData null || tempData.Rows.Count0) { //最后一页无数据了删除源数据源数据然后跳出 DBProxy.DeleteSourceClassData(dtMax, classId); break; } else { DBProxy.AddTargetData(tempData); } pageIndex; } successCount; Console.WriteLine($班级:{classId} 完成,已经完成{successCount}个); }DBProxy 完整代码class DBProxy { //获取要迁移的数据所有班级id public static IEnumerableint GeSourcetLstClassId(DateTime dtMax) { var connection Config.GetConnection(Config.SourceDBStr); string Sql SELECT class_id FROM tablexx WHERE in_time dtMax GROUP BY class_id ; using (connection) { return connection.Queryint(Sql, new { dtMax dtMax }, commandType: System.Data.CommandType.Text); } } public static DataTable GetSourceDataByClassIdTable(DateTime dtMax, int classId, int startIndex, int endIndex) { var connection Config.GetConnection(Config.SourceDBStr); string Sql SELECT * FROM ( SELECT *,ROW_NUMBER() OVER(ORDER BY in_time desc) p FROM tablexx WHERE in_time dtMax AND class_idclassId ) t WHERE t.p BETWEEN startIndex AND endIndex ; using (connection) { DataTable table new DataTable(MyTable); var reader connection.ExecuteReader(Sql, new { dtMax dtMax, classId classId, startIndex startIndex, endIndex endIndex }, commandType: System.Data.CommandType.Text); table.Load(reader); reader.Dispose(); return table; } } public static int DeleteSourceClassData(DateTime dtMax, int classId) { var connection Config.GetConnection(Config.SourceDBStr); string Sql delete from tablexx WHERE in_time dtMax AND class_idclassId ; using (connection) { return connection.Execute(Sql, new { dtMax dtMax, classId classId }, commandType: System.Data.CommandType.Text); } } //SqlBulkCopy 批量添加数据 public static int AddTargetData(DataTable data) { var connection Config.GetConnection(Config.TargetDBStr); using (var sbc new SqlBulkCopy(connection)) { sbc.DestinationTableName tablexx_2017; sbc.ColumnMappings.Add(class_id, class_id); sbc.ColumnMappings.Add(in_time, in_time); . . . using (connection) { connection.Open(); sbc.WriteToServer(data); } } return 1; } }运行报告: 程序本机运行开vpn连接远程DB服务器运行1分钟迁移的数据数据量为 1915560每秒约3万条数据 1915560 / 6031926 条/秒 cpu情况不高磁盘队列情况不高写在最后在以下情况下速度还将提高 1. 源数据库和目标数据库硬盘为ssd并且分别为不同的服务器 2. 迁移程序和数据库在同一个局域网保障数据传输时候带宽不会成为瓶颈 3. 合理的设置SqlBulkCopy参数 4. 菜菜的场景大多数场景下每次批量插入的数据量达不到设置的值因为有的class_id 对应的数据量就几十条甚至几条而已打开关闭数据库连接也是需要耗时的 5. 单纯的批量添加或者批量删除操作●程序员修仙之路--把用户访问记录优化到极致●程序员修仙之路--把用户访问记录优化到极致●程序员修仙之路--设计一个实用的线程池●程序员修仙之路--数据结构之CXO让我做一个计算器●程序猿修仙之路--数据结构之设计高性能访客记录系统●程序猿修仙之路--算法之快速排序到底有多快●程序猿修仙之路--数据结构之你是否真的懂数组互联网之路菜菜与君一同成长长按识别二维码关注