精华内容
下载资源
问答
  • 新浪微博爬虫分享(一天可抓取 1300 万条数据

    万次阅读 多人点赞 2018-08-06 10:56:24
    From:... 微博爬虫单机每日千万级的数据 微博爬虫总结:https://blog.csdn.net/nghuyong/article/details/81251948 Python爬虫——新浪微博(网页版):https://blog.csdn.net/qq_37267015/ar...

    From:https://blog.csdn.net/bone_ace/article/details/50903178

    微博爬虫单机每日千万级的数据 微博爬虫总结:https://blog.csdn.net/nghuyong/article/details/81251948
    Python 爬虫——新浪微博(网页版):https://blog.csdn.net/qq_37267015/article/details/71512824

    github 地址:https://github.com/LiuXingMing/SinaSpider
    github 地址:https://github.com/CUHKSZ-TQL/WeiboSpider_SentimentAnalysis

    微博访客 cookie 池

    微博爬虫 --- 自动获取访客 Cookie:https://cloud.tencent.com/developer/article/1615151

    # -*- coding: utf-8 -*-
    # @Author  :
    # @Date    :
    # @File    : add_wb_cookie.py
    # @description : XXX
    
    
    import redis
    import json
    import random
    import requests
    import datetime
    import multiprocessing
    from urllib.parse import quote
    from concurrent import futures
    
    #############################################
    REDIS_HOST_local = '127.0.0.1'
    REDIS_PORT = 6379
    REDIS_DB = 0
    COOKIE_POOL = 'weibo:cookie'
    #############################################
    
    proxy_list = [
        'http://127.0.0.1:8888',
        'http://127.0.0.1:8888',
        'http://127.0.0.1:8888',
    ]
    
    
    class WBCookiePool(object):
    
        def __init__(self):
    
            self._redis_conn_pool_local = redis.ConnectionPool(
                host=REDIS_HOST_local,
                port=REDIS_PORT,
                db=REDIS_DB
            )
            self.redis_conn_local = redis.StrictRedis(connection_pool=self._redis_conn_pool_local)
            pass
    
        def __del__(self):
            pass
    
        @staticmethod
        def get_tid():
            """
                获取 tid,c,w
            :return:tid
            """
            tid_url = "https://passport.weibo.com/visitor/genvisitor"
            data = {
                "cb": "gen_callback",
                "fp": {
                    "os": "3",
                    "browser": "Chrome69,0,3497,100",
                    "fonts": "undefined",
                    "screenInfo": "1920*1080*24",
                    "plugins": "Portable Document Format::internal-pdf-viewer::Chrome PDF Plugin|::"
                               "mhjfbmdgcfjbbpaeojofohoefgiehjai::Chrome PDF Viewer|::internal-nacl-plugin::Native Client"
                }
            }
    
            headers = {
                "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 "
                              "(KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36",
            }
    
            proxy_host = random.choice(proxy_list)
            proxies = {
                'http': proxy_host,
                'https': proxy_host,
            }
            req = requests.post(
                url=tid_url, data=data, headers=headers,
                # proxies=proxies
            )
            if req.status_code == 200:
                ret = eval(
                    req.text.replace("window.gen_callback && gen_callback(", "").replace(");", "").replace("true", "1"))
                return ret.get('data').get('tid')
            return None
    
        def get_cookie(self):
            """
                获取完整的 cookie
            :return: cookie
            """
            tid = self.get_tid()
            if not tid:
                return None
            cookies = {
                "tid": tid + "__095"  # + tid_c_w[1]
            }
            url = f"https://passport.weibo.com/visitor/visitor?a=incarnate&t={quote(tid)}" \
                  f"&w=2&c=095&gc=&cb=cross_domain&from=weibo&_rand={random.random()}"
            # print(url)
            headers = {
                "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 "
                              "(KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36",
            }
            proxy_host = random.choice(proxy_list)
            proxies = {
                'http': proxy_host,
                'https': proxy_host,
            }
            req = requests.get(
                url, cookies=cookies, headers=headers,
                # proxies=proxies
            )
            if req.status_code != 200:
                return None
    
            ret = eval(
                req.text.replace("window.cross_domain && cross_domain(", "").replace(");", "").replace("null", "1"))
    
            try:
                sub = ret['data']['sub']
                if sub == 1:
                    return None
                subp = ret['data']['subp']
            except KeyError:
                return None
            return sub, subp
    
        def del_expire_cookie(self, max_time_int=None):
            self.redis_conn_local.zremrangebyscore(COOKIE_POOL, min=0, max=max_time_int)
    
        def add_cookie_2_redis(self):
            count = 0
            while True:
                while True:
                    try:
                        sub, sub_p = self.get_cookie()
                        break
                    except BaseException as be:
                        print(be)
                        continue
    
                count += 1
    
                expire_time = datetime.datetime.now() - datetime.timedelta(minutes=10)
                expire_time_int = int(expire_time.timestamp())
                self.del_expire_cookie(max_time_int=expire_time_int)
    
                headers = {'cookie': f'SUB={sub};SUBP={sub_p}'}
                print(headers)
                # self.redis_conn.lpush(self.cookie_pool, json.dumps(headers, ensure_ascii=False))
    
                timestamp_int = int(datetime.datetime.now().timestamp())
                data_string = json.dumps(headers, ensure_ascii=False)
                self.redis_conn_local.zadd(COOKIE_POOL, timestamp_int, data_string, )
    
    
    def main():
        wb_cookie_pool = WBCookiePool()
    
        cpu_count = multiprocessing.cpu_count()
        pool = futures.ThreadPoolExecutor(max_workers=cpu_count)
        for i in range(cpu_count):
            pool.submit(wb_cookie_pool.add_cookie_2_redis)
    
    
    if __name__ == '__main__':
        main()
    
    

    爬虫功能:

    • 此项目和QQ空间爬虫类似,主要爬取新浪微博用户的个人信息、微博信息、粉丝和关注(详细见此)。
    • 代码获取新浪微博Cookie进行登录,可通过多账号登录来防止新浪的反扒(用来登录的账号可从淘宝购买,一块钱七个)。
    • 项目爬的是新浪微博wap站,结构简单,速度应该会比较快,而且反扒没那么强,缺点是信息量会稍微缺少一些(可见爬虫福利:如何爬wap站)。
    • 爬虫抓取微博的速度可以达到 1300万/天 以上,具体要视网络情况,我使用的是校园网(广工大学城校区),普通的家庭网络可能才一半的速度,甚至都不到。

    环境、架构:

    开发语言:Python2.7 
    开发环境:64位Windows8系统,4G内存,i7-3612QM处理器。 
    数据库:MongoDB 3.2.0 
    (Python编辑器:Pycharm 5.0.4;MongoDB管理工具:MongoBooster 1.1.1)

    • 主要使用 scrapy 爬虫框架。
    • 下载中间件会从Cookie池和User-Agent池中随机抽取一个加入到spider中。
    • start_requests 中根据用户ID启动四个Request,同时对个人信息、微博、关注和粉丝进行爬取。
    • 将新爬下来的关注和粉丝ID加入到待爬队列(先去重)。

    使用说明:

    启动前配置:

    • MongoDB安装好 能启动即可,不需要配置。
    • Python需要安装好scrapy(64位的Python尽量使用64位的依赖模块)
    • 另外用到的python模块还有:pymongo、json、base64、requests。
    • 将你用来登录的微博账号和密码加入到 cookies.py 文件中,里面已经有两个账号作为格式参考了。
    • 另外一些scrapy的设置(如间隔时间、日志级别、Request线程数等)可自行在setting里面调。

    运行截图:

    新浪微博爬虫程序

    新浪微博爬虫数据

    数据库说明:

    SinaSpider主要爬取新浪微博的个人信息、微博数据、关注和粉丝。 
    数据库设置 Information、Tweets、Follows、Fans四张表,此处仅介绍前面两张表的字段。

    Information 表: 
    _id:采用 “用户ID” 作为唯一标识。 
    Birthday:出生日期。 
    City:所在城市。 
    Gender:性别。 
    Marriage:婚姻状况。 
    NickName:微博昵称。 
    Num_Fans:粉丝数量。 
    Num_Follows:关注数量。 
    Num_Tweets:已发微博数量。 
    Province:所在省份。 
    Signature:个性签名。 
    URL:微博的个人首页。

    Tweets 表: 
    _id:采用 “用户ID-微博ID” 的形式作为一条微博的唯一标识。 
    Co_oridinates:发微博时的定位坐标(经纬度),调用地图API可直接查看具体方位,可识别到在哪一栋楼。 
    Comment:微博被评论的数量。 
    Content:微博的内容。 
    ID:用户ID。 
    Like:微博被点赞的数量。 
    PubTime:微博发表时间。 
    Tools:发微博的工具(手机类型或者平台) 
    Transfer:微博被转发的数量。


    转载请注明出处,谢谢!(原文链接:http://blog.csdn.net/bone_ace/article/details/50903178

    微博爬虫,单机每日千万级的数据 && 吐血整理的微博爬虫总结

    前言

    此前我发布了一篇博客微博爬虫,每日百万级数据,并且把代码开源在了Github上,然后就有很多人联系我,也有公众号转载了这篇文章。

    不过对于微博爬虫,我还是心虚的,因为没有解决账号池的问题,所以每天百万级的数据,是有水分的。单单爬好友关系,这种简单数据可以达到百万级,如果爬关键词搜索的微博,或者一个人的全部微博,是达不到百万级数据这个量的。

    不过既然已经埋了坑,就要填!所以自从写了那片文章以后,就一直想构建一个稳定的单机每日千万级的微博抓取系统

    值得庆祝的是,这个问题现在已经全面解决了!也对微博爬虫有了更深层次的认识!

    微博站点分析

    目前微博一共有三个站点,分别是 
    https://weibo.cn 
    这里写图片描述
    https://m.weibo.com 
    这里写图片描述
    https://weibo.com 
    这里写图片描述

    可以看到这三个站点的复杂程度是逐渐提高的,很显然,如果能在最简单的weibo.cn完成的抓取,肯定不去复杂的weibo.com上去抓,但是事实上,有的只能在复杂的抓取!

    那什么任务是weibo.cn完成不了的呢?可以说,抓取一个人的全部微博,抓取好友关系,抓取个人信息,这些都能在weibo.cn这个站点完成。 
    但是,就是有一个任务,weibo.cn实现不了,就是高级搜索

    微博高级搜索

    可能你经常有这样的需要,比如最近疫苗事件兴起,你要抓取7月10号到7月20号这段时间,提及到疫苗这个关键词的微博

    这其实是一个非常刚性的需求,这就要采用微博的高级搜索来完成了。

    对于高级搜索接口,微博三个站点的情况是:

    weibo.cn

    高级搜索入口:https://weibo.cn/search/mblog?advanced=mblog&f=s 
    这里写图片描述
    可以看到这里可以筛选的条件是,类型,用户,时间,注意,这里的时间是以天为单位。

    下面具体搜索一个关键词,疫苗 
    这里写图片描述
    这里写图片描述

    可以看到一页有10条搜索的结果,最多显示100页,也就是1000条结果, 
    所以,一次搜索的结果,最多返回1000条微博, 
    而这个站点的时间单位是,所以比如搜索的时间段是10天,那么最多能抓取到10*1000=10000条数据。

    m.weibo.com

    很遗憾这个站点没有高级搜索接口

    weibo.com

    高级搜索入口:https://s.weibo.com 
    这里写图片描述
    可以看到这里可以筛选的条件是,类型,用户,时间,地区,注意,这里的时间是以小时为单位。

    这里写图片描述

    这个站点一页是20条微博,最多50页,所以一次搜索也是最多返回1000条微博数据 
    但是这个站点的时间单位是小时, 
    所以比如搜索的时间段是10天,那么最多能抓取到10*24*1000=240000条数据。

    总结

    • 对于搜索接口,只能选择weibo.com和weibo.cn
    • weibo.com的筛选条件更加丰富,包括了地区,时间段更细,以小时为单位
    • 所以如果希望抓取尽可能多的关键词搜索结果,需要采用weibo.com

    所以仅仅高级搜索有可能需要用到weibo.com,并且是你需要的搜索结果数据很大,并且筛选条件很细,比如地区,其他所有爬虫需求都可以通过weibo.cn这个站点来抓取,包括比较粗略的高级搜索

    微博抓取经验总结

    wiebo.com 还是传统的验证码,5位数的数字字母组合 
    这里写图片描述
    这种验证码可以通过扫码平台解决。具体的登陆代码参考这里 
    不过,由于你买的小号,可能由于频繁操作,被微博盯上了,登陆进去是账号异常,这就会产生非常恶心的验证码,如下图 
    这里写图片描述
    这种情况,我建议你放弃治疗吧,不要想着破解这种验证码了,所以一般买的小号,不是100%可以用,有一部分是异常账号的!

    构建千万级的爬虫系统

    根据以上这些分析以后,要想构建千万级别的爬虫系统,只要做一件事情构建账号池

    构建账号池的步骤也非常简单: 
    1. 购买大量账号 
    2. 登陆进微博,保存下cookie 
    就这两步,以后每次请求,只要随机从账号池中选择一个账号即可。

    对于weibo.cn和weibo.com这两个站点的cookie是不同的,所以要构建两个账号池,一个cn站点的,一个com站点的。

    这时候,你结合我之前写的项目WeiboSpider就可以轻松达到每日百万级的数据抓取了!

    注意这里实际的抓取速度和你的账号池大小和电脑的带宽有很大关系,如果账号池不大,请求的间隔延迟就需要时间长一点,如果带宽小的话,每次请求的耗时也会长一点

    我的数据是, 
    账号池里230个账号,每次请求延迟为0.1秒,可以达到一天200~300万的抓取结果。

    冲刺千万级

    我一直认为,我上面构建的这个爬虫,已经占满了带宽! 
    有一次,我又启动了一个爬虫程序,发现另一个爬虫,也可以达到一天200~300万的抓取速度,同时之前的爬虫也是在以一天200~300万的抓取速度在运行, 
    所以,仅仅是因为CPU限制了爬虫的抓取量,而不是网络IO!

    所以只需要用多进程优化即可,这里推荐Redis-Scrapy,所有的爬虫共享一个Redis队列,通过Redis统一给爬虫分配URL,这样就是一个分布式的抓取系统了。 
    可以分别部署在不同的机器上(如果一个机器带宽/CPU占用满了),或者就在一个机器上开多个进程即可。

    就这样,我开了5个进程,不敢多开,毕竟账号池还是200多个。

    然后结果就是:

    这里写图片描述

    一分钟可以抓取8000条数据,一天可以达到1100万+

    这个抓取系统目前一直在稳定运行

    所以就此实现了最初的目标,千万级别的微博爬虫系统

    总结

    • 解决微博登陆问题,并构建账号池
    • 通过scrapy-redis框架构建分布式系统
    • 深入分析微博高级搜索需求的解决方案,并编写weibo.cn和weibo.com两个站点的爬虫

    至此,可以说,彻底解决了一切关于微博爬虫的问题!!!

    开源代码在这里,你需要添加自己的账号池.

    展开全文
  • 现在有一个表,表数据百万,需要查询某一天的新增用户数统计 。目前写的是,根据某天的时间查然后在查询之前的,相互比对今天的时间用户不在之前用户里面,用到了 not in 查询结果慢的不行,跪求大神有什么优化...
  • 昨晚遇到个把百万数据导入MySQL的问题,翻遍整个网络,最后找到了如下几种方法,这里先做个汇总! 直接导入 ------此方法极不推荐!费时费力还损电脑! 用Load data infile和临时表导入;------ 推荐 使用存储...

    昨晚遇到一个把百万条数据导入MySQL的问题,翻遍整个网络,最后找到了如下几种方法,这里先做个汇总!

    1. 直接导入 ;
    2. 用Load data infile导入
    3. 使用存储过程批量导入
    4. 更换引擎,让ENGINE=InnoDB为MyISAM,再导入;
    5. 合并单条SQL语句为多条,再导入;
    6. 利用事务进行有序的多次插入。

    现在掌柜再依次对上面的各种方法进行一个介绍以及各种方法会遇到的坑😂。

    • 首先是直接导入,如果需要示例数据可以私掌柜。

    ----------------------------------------2021.07.12更新--------------------------------------------
    因为网盘过期了,所以示例数据如果大家有需要,还是直接私我你的邮箱,我再发给你,谢谢🤝!!!
    ----------------------------------------2021.07.12分割--------------------------------------------

    ----------------------------------------2021.05.18更新--------------------------------------------
    因为最近好些朋友都想要示例数据,所以掌柜干脆直接放网盘,大家可以自取👇:
    链接:https://pan.baidu.com/s/1VTBSZBFGZ5VY9YnCHtrODw
    提取码:1isa
    ----------------------------------------2021.05.18分割--------------------------------------------

    PS:直接导入之前需要更改MySQL的数据导入权限,具体方法可以查看之前掌柜写的比较详细的一篇 ------>MySQL8.0版本以上的文件导入权限问题;此外已经新建表名为user_gender。
    PPS:掌柜这里的MySQL是8.0.15版本!

    不过掌柜一开始使用source直接导入的时候就遇到第一个坑👇报错如下:
    在这里插入图片描述
    有点纳闷?再三检查文件名没有写错啊,咋会打不开外部文件导入呢?
    于是翻阅谷歌,在一个外国小哥的博客里面发现遇到同样的问题,于是往下一看,终于找到原因了:
    在这里插入图片描述
    没有写对文件路径和最后多加了分号导致的。。。

    use your_database;
    SOURCE your_disk:/yourpath/user_gender1.sql
    

    后来按👆上面的步骤和正确写法再次操作就成功导入了:
    在这里插入图片描述
    注意看右下角的开始时间是20:28,结果半个小时过去了,数据还在导入。。。
    在这里插入图片描述
    于是掌柜后来自动掐断,再随手查看这半小时导入了多少数据:
    在这里插入图片描述
    惨不忍睹!半小时才导入3万多条。。。这上百万的数据岂不是要一天???这怎么能忍!于是就有了开头的一番搜索。

    • 下面来到第二种方法👉:使用Load data infile 导入。
      依然一开始要更改导入权限,然后开始执行导入文件操作:
      在这里插入图片描述
      但是突然报错如上!👆,说不正确的整数值(ERROR1366: Incorrect integer value) ???成功get到了
      第二个坑
      👈。奇怪,明明要导入的文件打开就是整数:
      在这里插入图片描述
      后来掌柜再次搜索官方文档发现是由sql_mode这个所导致的,因为MySQL安装的时候是默认使用的严格SQL模式,所以对数据类型的验证要求很严格。
      在这里插入图片描述
      于是解决办法就是:更改sql_mode的值即可。

    如果你不确定MySQL目前有哪些模式可以先查询一下:
    在这里插入图片描述
    上面两条命令都可以查询到目前你的sql包含哪些模式,第一个查询是全局查询;第二个是目前进程的查询。可以发现两个里面都有strict 这个模式存在,所以需要去掉它
    在这里插入图片描述
    或者设置成空字符串也可以:
    在这里插入图片描述
    可以发现设置后再次查询sql_mode已经没有strict 模式了,现在再次用Load导入看看:
    在这里插入图片描述
    终于不再报错Incorrect,但是第三个坑又出现了🕳👆 ------> ERROR 1062(23000):Duplicate entry ‘0’ for key ‘PRIMARY’.
    然后掌柜又去官方文档查看,发现官方说出现这样的情况是因为主键的问题,然后接着官方说如果在插入数据后面加入IGNORE,会产生警示但不报错,并且重复的键值不会插入。
    在这里插入图片描述
    显然掌柜这里并不能使用IGNORE方法,然后掌柜又去翻阅谷歌有同样情况的人是如何解决的,发现有些说用自增主键的方式可以解决但是掌柜再看 这里 要导入的数据集并不符合这个方法。。. (打脸自己😂,今天发现就是在一开始创建表格的时候设置自增主键即可解决这个问题!!!
    在这里插入图片描述
    接着再次导入数据到表格:
    在这里插入图片描述
    终于成功导入了百万条数据,然后今天耗时42秒,比第一种方法确实快了不止100倍!!!!

    (未完待续。。。)

    展开全文
  • 利用布隆处理百万数据

    万次阅读 2018-07-31 20:31:16
    这两遇到了数据处理的问题 是使用Java将三个不定数量(至少百万)的map集合中取得数据的交集 这里可以使用布隆进行数据处理   效果是非常好的.,这里用的是整型 所以速度也比较快,如果是字符串的话,可以...

    这两天遇到了一个数据处理的问题

    是使用Java将三个不定数量(至少百万)的map集合中取得数据的交集

    这里可以使用布隆进行数据处理

     

    效果是非常好的.,这里用的是整型

    所以速度也比较快,如果是字符串的话,可以hash一下

    这是布隆工具类

    class BloomFilter{
    
        private static final int BIT_SIZE = 2 << 28 ;//二进制向量的位数,用作数据存储
    
        private BitSet bits = new BitSet(BIT_SIZE);
    
        /**
         * 像过滤器中添加字符串
         */
        public void addValue(int value)
        {
            //将数据bit上变为1
            bits.set(value, true);
        }
    
        /**
         * 判断字符串是否包含在布隆过滤器中
         */
        public boolean contains(int value)
        {
    
            boolean ret = true;
    
            //将要比较的数据与布隆过滤器比对
            ret = ret && bits.get(value);
            return ret;
        }
    
    }

     

    展开全文
  • MySQL 数百万数据条件查询优化

    千次阅读 2019-05-08 10:36:16
    最近在公司实习做到一个项目,要在一个包含数百万数据表(如果以日期来分类,大概是同一天里又十多万行数据)之中查询出日期在某个日期查询出与之相邻日期的那些行的结果,其中只有日期包含索引,然后还有多个条件...

    MySQL 数百万行数据条件查询优化

      最近在公司实习做到一个项目,要在一个包含数百万行数据表(如果以日期来分类,大概是同一天里又十多万行数据)之中查询出日期在某个日期查询出与之相邻日期的那些行的结果,其中只有日期包含索引,然后还有多个条件查询。在刚开始的时候,需要花费2秒多才能查询出想要的结果,这个速度肯定对于用户查询很不友好,于是我被要求将查询出结果的时间降到300ms之内。然后经过多种方法优化,最终将查询时间减少到200多ms。速度快了仅10倍。

      这篇博客就是基于此背景之下产生的,这里顺便将自己的优化方法记录下来以防以后遗忘。

      MySQL 数百万行以上的数量多条件查询一般都会很浪费时间,这里提供了几种方法及思路进行优化,尽量优化到能够应用的场景。

    首先创建一个百万数据量的数据表

      这里使用了mysql的存储过程来插入数据,我通过改变存储过程CONCAT('2018-06-0',i)中的2018-06-0的月份的值从而得到不同日期的多行数据。

      例子表格建立sql语句

    -- PS: 这个表和存储过程是我从网上随便找的,可能建表方面不太规范,大家忽略,只要看字段值即可
    
    -- 创建MyISAM模式表方便批量跑数据
    CREATE TABLE `logs_info` (
      `id` INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY comment '主键id',
      `dt` VARCHAR(20) NOT NULL comment '记录日期',
      `logtype` VARCHAR(255) DEFAULT NULL comment '日志类型',
      `logurl` VARCHAR(255) DEFAULT NULL comment '日志url',
      `logip` VARCHAR(255) DEFAULT NULL comment 'ip地址',
      `logdz` VARCHAR(255) DEFAULT NULL comment '其他字段',
      `ladduser` VARCHAR(255) DEFAULT NULL comment '其他字段',
      `lfadduser` VARCHAR(255) DEFAULT NULL comment '其他字段',
      `laddtime` DATETIME DEFAULT NULL comment '其他字段',
      `htmlname` VARCHAR(255) DEFAULT NULL comment '页面名称'
    ) ENGINE=MYISAM  AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='日志表';
    -- 创建索引
    CREATE INDEX dtIndex ON cfile.logs_info(dt); 
    -- 添加表字段
    ALTER TABLE cfile.logs_info ADD (ctime  DATETIME DEFAULT '1991-01-01 10:10:10' NOT NULL);
    ALTER TABLE cfile.logs_info ADD ( mtime DATETIME DEFAULT NOW() NOT NULL);
     
    -- 创建存储过程
    
    DROP PROCEDURE IF EXISTS my_insert;
    
    DELIMITER $$
    
    CREATE
        /*[DEFINER = { user | CURRENT_USER }]*/
        PROCEDURE `cfile`.`my_insert`()
        /*LANGUAGE SQL
        | [NOT] DETERMINISTIC
        | { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
        | SQL SECURITY { DEFINER | INVOKER }
        | COMMENT 'string'*/
    	BEGIN
    	
       DECLARE n INT DEFAULT 1;
       DECLARE i INT DEFAULT 1;
            loopname:LOOP
                INSERT INTO `logs_info`(`dt`,`logtype`,`logurl`,`logip`,`logdz`,`ladduser` ,`lfadduser`,`laddtime`,`htmlname`) VALUES ( CONCAT('2018-06-0',i),2+n, '/index', '0:0:0:0:0:0:0:1', n, n+1, 'null', '2018-05-03 14:02:42', '首页');
                SET n=n+1;
    	    IF n%300000=0 THEN 
    	        SET i=i+1; 
                END IF;
            IF n=3000000 THEN
                LEAVE loopname;
            END IF;
            END LOOP loopname;
    
    	END$$
    
    DELIMITER ;
     
    -- 执行存储过程
    CALL my_insert();
     
    -- 数据插入成功后修改表模式InnoDB 时间稍微久点
     ALTER TABLE `logs_info` ENGINE=INNODB;
     
    

    建完表后和自定义一些插入数据后,数据量有两百多万行数据。这里需要说一下,多次统计数量的速度count(*)比较快,之前了解过count(*)会统计每行中特定某个标志行的id来统计的,而且会保留缓存,所以比较比count(id)快一点
    在这里插入图片描述

    方法一,使用in, 查询

    EXPLAIN  
    
    SELECT d.id FROM logs_info d WHERE htmlname='第一页' AND logurl='/index' AND dt IN('2018-05-01','2018-05-03','2018-05-05');
    
    

    执行多次,发现使用时间大约为2.432秒。如果使用explain做分析,可以看出是使用了全表扫描,共扫描两百多万行。
    in 查询结果
    in 查询结果
    in 的执行情况
    in 执行情况

    方法二,使用or, 查询

    EXPLAIN  
    
    SELECT d.id FROM logs_info d  WHERE htmlname='第一页' AND logurl='/index' AND (dt ='2018-05-01' OR dt='2018-05-03' OR dt='2018-05-05');
    

    执行多次,发现使用时间大约为2.44秒。如果使用explain做分析,可以看出也是使用了全表扫描,共扫描两百多万行。
    or 执行结果
    or 执行结果
    or执行情况
    or 执行情况

    方法三,使用union all, 查询

    EXPLAIN  
    
    SELECT d.id FROM logs_info d WHERE d.htmlname='第一页' AND d.logurl='/index' AND d.dt='2018-05-01'
    UNION ALL
    SELECT d.id FROM logs_info d WHERE d.htmlname='第一页' AND d.logurl='/index' AND d.dt='2018-05-03'
    UNION ALL
    SELECT d.id FROM logs_info d WHERE d.htmlname='第一页' AND d.logurl='/index' AND d.dt='2018-05-05'
    

    用union all进行查询,发现所用时间为2.05秒左右,比上两种情况都要快一点,然后通过explain查看其执行情况,可以看出其使用了索引进行查询,然后查询的行数一共是60万*3 =180万行左右,比上两种的全表扫描行数少,所以速度会快一点。

    union all执行结果,
    union 执行结果
    union all 执行情况
    union all 执行情况

    方法四,代码解决,多线程执行再合并

    EXPLAIN 
    
    SELECT d.id FROM logs_info d WHERE d.htmlname='第一页' AND d.logurl='/index' AND d.dt='2018-05-01'
    
    大约1秒左右
    

    这里看github项目中的代码,后面也会贴出代码。

    代码:

        /**
         * 使用方法四,多线程得到数据,
         *
         * @param request 条件实体类
         * @return 日志数据集合
         **/
        public List<LogPO> getData4(LogRequest request) throws InterruptedException, ExecutionException {
            this.commonSetDt(request);
            CountDownLatch latch = new CountDownLatch(3);
            //设置新的条件
            LogRequest[] requests = new LogRequest[2];
            for (int i = 0; i < requests.length; i++) {
                requests[i] = new LogRequest();
                requests[i].setHtmlname(request.getHtmlname());
                requests[i].setLogurl(request.getLogurl());
            }
            requests[0].setDt(getDateStr(request.getDt(), 2));
            requests[1].setDt(getDateStr(request.getDt(), 4));
            //异步执行
            List<LogPO> list = new ArrayList<>(16);
            Future<List<LogPO>> list1 = asyncService.execMapper(request, latch);
            Future<List<LogPO>> list2 = asyncService.execMapper(requests[0], latch);
            Future<List<LogPO>> list3 = asyncService.execMapper(requests[1], latch);
            //等待5秒还没执行完,直接返回
            latch.await(5, TimeUnit.SECONDS);
            list.addAll(list1.get());
            list.addAll(list2.get());
            list.addAll(list3.get());
            return list;
        }
    

    异步方法类

    /**
     * 异步方法操作类
     *
     * @author zhangcanlong
     * @since 2019/5/8 9:22
     **/
    @Component
    public class AsyncService {
        @Autowired
        private LogMapper logMapper;
    
        /**
         * 异步执行mapper
         *
         * @param request 条件
         * @return 执行返回的实体类
         **/
        @Async("taskExecutor")
        public Future<List<LogPO>> execMapper(LogRequest request, CountDownLatch latch) {
            Long time1 = System.currentTimeMillis();
            AsyncResult<List<LogPO>> asyncResult = new AsyncResult<>(this.logMapper.getLogsByCondition4(request));
            System.out.println("线程名为:" + Thread.currentThread().getName());
            System.out.println("花费时间为:" + (System.currentTimeMillis() - time1));
            latch.countDown();
            return asyncResult;
        }
    }
    
    

    方法五,建立复合索引

    暂时还没测试,速度会快很多,但是要修改数据库表,这里就不测试了

    总结

    建议如果能修改数据库的话,使用方法五,如果不能使用方法四,然后可以经常用一下explain做一下分析,再确定使用哪一种方法。

    遇到的问题:

    1. 刚刚开始写的时候,忘记在spring boot的启动类上加上@EnableAsync注解了,导致多线程刚开始的时候并没有启作用,后来还测试了很久,一直奇怪为什么多线程还能比其他晚,果然写代码需要仔细。

    2. 调用的异步方法,不能为同一个类的方法,简单来说,因为Spring在启动扫描时会为其创建一个代理类,而同类调用时,还是调用本身的代理类的,所以和平常调用是一样的。其他的注解如@Cache等也是一样的道理,说白了,就是Spring的代理机制造成的。参考:https://blog.lqdev.cn/2018/08/17/springboot/chapter-twenty-one/

    3. 在默认情况下,未设置TaskExecutor时,默认是使用SimpleAsyncTaskExecutor这个线程池,但此线程不是真正意义上的线程池,因为线程不重用,每次调用都会创建一个新的线程。可通过控制台日志输出可以看出,每次输出线程名都是递增的。参考:https://blog.lqdev.cn/2018/08/17/springboot/chapter-twenty-one/

    项目GitHub地址:
    https://github.com/KANLON/practice/tree/master/data-optimize

    展开全文
  • 百万数据连表查询优化

    千次阅读 2019-03-25 14:18:34
    交代一下背景,这算是次项目经验吧,属于公司个已上线平台的功能,这算是离职人员挖下的坑,随着数据越来越多,原本的SQL查询变得越来越慢,用户体验特别差,因此SQL优化任务交到了我手上。 这个SQL查询关联两个...
  • 百万数据的对账优化

    千次阅读 2018-06-30 08:15:40
    原来的流程系统订单量不大的情况下可以这样子处理没有什么异常问题,随着系统订单的增长,对账时间不断增长,远远超过我们能忍的极限,比如说:到早上还没有对帐完,影响第二出结算单的时间,不能做到财务T+1结算...
  • mysql随机生成百万条测试数据

    千次阅读 2018-08-08 10:54:50
    MySQL的随机生成百万条测试数据 ...然后执行一天的SQL,如下: INSERT INTO grzxmxb(orgname,cxlsh,czyh,checkname,checkorg,checkorgcode,username,certtype,usercertno,checktime,...
  • 刚开始的情况是条sql语句将数据库中的所有数据一次全部查询出来,每条放到个map中,最后放在list中,刚开始写的代码由于数据不多,没出现问题,后来数据达到百万后,客服在导出数据的时候直接内存溢出,于是...
  • 前几老大在群里抛出这个问题说:问个问题,千万级的表查询分页,怎么优化查询 select*fromuserlimit10000000,10 select*fromuserwherename="a"limit10000000,10 2.实验 经过验证发现以上语句在数据库中执行 ...
  • PHP百万数据导出方案(多csv文件压缩)

    万次阅读 多人点赞 2017-08-25 16:49:36
    概述:最近公司项目要求把数据除了页面输出也希望有导出功能,虽然之前也做过几个导出功能,但这次数据量相对比较大,差不多一天数据就20W条,要求导7天或者30天,那么数据量就轻松破百万了甚至破千万,因此开发的...
  • MYSQL百万数据分页查询优化实战

    万次阅读 2020-04-07 15:47:58
    最近项目中,需要将公司老的订单日志数据迁移到新的ElasticSearch统一日志存储,我们老日志数据是分库分表存储在mysql数据库中(按分表),单表数据量在500w左右,本人就写了个小程序负责mysql到es的数据迁移,...
  • 微信公众号文章数据是互联网最有价值的数据,各大小厂商都在与腾讯微信开发组斗智斗勇,爬取和反爬技术在这几年不断登上新台阶。本大神于试过了GITHUB上面前5的微信公众号API,结果不如人意,开源的毕竟还是弱势...
  • 这个问题够写篇分析报告了。 首先每天三百万数据,这张表再大,最后都会撑不住,横向分表是自然的事。估计最先想到的是按时间来分吧。 查询速度的问题可使用的技术方案有很多,但先要做的是对业务查询的分析。找...
  • JAVA 处理百万或者千万数据的方法

    千次阅读 2019-08-17 11:37:52
    参考地址:https://blog.csdn.net/qq_34594123/article/details/78866297
  • sql优化问题(百万数据优化方案)   .sql数据库优化方案 1、索引 2、分库分表分区 3、数据库引擎  4、预处理  5、读写分离 1、索引,建立索引是数据库优化各种方案之中成本最低,见效最快的解决方案,...
  • 往ES插入百万数据的耗时统计

    千次阅读 2019-05-13 10:29:26
    上线前,需要测试系统,其中造假数据百万条,看接口的检索性能如何 2、操作 新建了个备份索引,同时增加了个测试入库接口,用到线程池并发入库 我的电脑是4核的喔创建了5个线程,10个任务执行插入操作, ...
  • mysql在百万数据量下查询慢的问题

    万次阅读 热门讨论 2015-11-25 10:44:37
    这两,越来越觉得自己做的玩家历史表,查询速度很慢,开始还以为是网络的问题,然后持续了很快pass了这个想法。很可能是自己的查询速度慢,于是进入数据库看了一下,发现历史记录已经达到了600多万条了。...
  • 数据量很大大概有几百万条,有重复 数据分别为 用户名,密码,性别,年龄 保存在个txt中(五十多个...怎样才能快速插入,我用传统的方法,程序运行了几了才插入了不到十分之. 插入后要数据无重复,已username列为准
  • 分分钟百万数据的微博爬虫分析

    千次阅读 2020-04-20 15:52:07
    经过上篇的分析我们无意中发现了微博的接口,并且不做任何的账号、流量限制可以直接获取最完整的微博数据。接下来我们就通过分析微博粉丝接口查看其中的数据结构顺便看看能不能找到其他api。 分析微博粉丝接口 由...
  • Oracle 百万数据查询优化

    千次阅读 2017-07-04 11:36:01
    14.并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,如表中有字段sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起...
  • 、数据库访问优化的五个法则  在实际开发,我们主要是需要对SQL语句进行优化,我们需要快速定位能性的瓶颈点,也就是说快速找到我们SQL主要的开销在哪里?根据木桶原理可以知道,最慢的设备往往是性能瓶颈。...
  • 这几年数据分析迅速发展,我们也做了个微数据分析工具。该产品已成功运行三年,满足日活百万的企业。产品结构很简单,用世上最简单的语言php,最普遍的数据库mysql,服务器可以选择apache也可以选择nginx,一切看...
  • 最近在开发个销售数据统计的网站时,客户提供的数据量在百万级以上,这些数据作为基础数据,从中提取,组合出各种类型的字段进行计算汇总,之前没有遇到过这么大的数据量。开始只是用最普通的方法来查询,当测试...
  • 如果是百万数据,我还可以甩锅为服务器性能问题,十万数据查不动,要是汇报让领导升级服务器,估计领导直接让我去跟人事结算工资了。 我是这么设想的: 我:“查不动是服务器性能问题。”领导:“现在服务器...
  • 这两接到个任务,大概目标是要将现有的客户提供的数据(Access数据库)全部转存到我们本地的mysql中,然后后期写个定期检查原表是否更新,如果更新,则更新本地数据库。 我开始写了个小的代码,是利用数组...
  • 前几在项目中,无意中发现sqlserver某张表中存着5000多万条数据,这些数据都没啥用,想着把它们都删了,于是用了delete from table;结果删了个钟sql语句还在执行;通过查阅资料,发现用 truncate table ...
  • 开始觉得100w的数据量不大,于是就插啊插,吃了个饭,回来看,在插入了50多w条数据后,每秒就只能插10条了。。觉得很奇怪,为啥越插越慢呢? 于是就开始分析插入的时间损耗,想到了如下的解决方案:(mysql使用...
  • ...最近的项目需要导入大量的数据,插入的...开始觉得100w的数据量不大,于是就插啊插,吃了个饭,回来看,在插入了50多w条数据后,每秒就只能插10条了。。觉得很奇怪,为啥越插越慢呢? 于是就开始分析插

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 55,680
精华内容 22,272
关键字:

一天百万数据