精华内容
下载资源
问答
  • 2022-04-07 22:32:27

    android冷启动优化方案汇总

    背景

    所在项目组app的冷启动一直是个无人看护优化的状态,机缘巧合之下,领导弄了个专项让我去优化,陆续优化了几个月,效果还不错,分享一些通用的优化方案给大家。

    原理与大纲

    在文章前面先把优化的思路都列出来,方便大家按需查看

    1. 异步inflate布局
    2. 减少binder调用
    3. 启动时ViewPager+Fragment加载的优化
    4. SharedPreference替换为mmkv实现
    5. 确保系统已经完成dex2oat的优化
    6. 锁粒度的优化
    7. 非页面展示的message往后移
    8. 减功能和删代码
    9. bytex

    方案列举

    在文章前面先把优化的思路都列出来,方便大家按需查看
    1. 异步inflate布局
    inflate这个过程就是把R.layout.xxx这一串布局数字,加载为对应的View类型。方法签名为

    LayoutInflater.from(Context context).inflate(@LayoutRes int resource, ViewGroup root, boolean attachToRoot);
    

    所谓的异步inflate布局,实际上就是提前在子线程加载出VIew,在setContentView或者onCreateView等用到View时直接取出,减少了主线程的等待。

    官方已经提供了一个实现,AsyncLayoutInflater,原理很简单,代码也很少,但使用后发现有不少问题,比如View中使用looper会报错,无法提前或者root ViewGroup, LayoutParams设置失效等

    基于此,我换了个思路,把inflate过程拆建并实现部分异步。

    inflate可以分为两个步骤,
    a) 加载R.layout.xxx对应的xml文件,加载到内存中,这是一个IO操作
    b) 把xml文件解析并构建View
    这两个步骤是可以分开执行的,android也提供了单独执行b步骤的接口

    public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot)
    

    我们可以开子线程把a步骤做好了,等需要使用VIew时,在主线程把b做了,虽然不是全部异步,但效果也比较明显,且没有官方实现这么多的问题。
    具体实现是因为有接口可以加载layoutid为XmlResourceParser

    context.getResources().getLayout(layoutId)
    

    2. 减少binder调用
    binder是android提供的IPC的方式。android许多系统服务都是运行在system_server进程而非app进程,比如判断网络,获取电量,加密等,这导致会有很多的binder通信,而IPC是耗时操作,需要尽量减少调用。
    要想知道启动时候执行了多少此binder通信,可以通过抓systrace时添加binder_driver参数查看。
    而要减少binder调用,我的建议有以下两点

    a) 缓存从serviceManager进程获取到的客户端binderProxy实例。android采取服务注册的方式提供服务,app需要获取系统服务,需要先跟serviceManager binder通信一次,获取到具体服务的binderProxy实例,然后再用binderProxy实例与具体服务做binder通信,这里需要做两次binder通信,通过缓存binderProxy实例可以减少一次binder
    b) 轮询获取改为监听回调获取,最典型的就是网络状态的获取,可以用监听网络状态的接口来实现,把网络状态用volatile的变量缓存起来,接口直接返回变量,只在回调中赋值
    

    3. 启动时ViewPager+Fragment加载的优化
    这个比较长,用独立一篇文章介绍 ViewPager+Fragment的加载优化
    4. SharedPreference替换为mmkv实现
    这个就是简单粗暴了,因为SharePreference加载数据的过程是:new一个线程来加载全部数据为map,而调用线程则会一直等待直到加载完成。而mmkv在读写方面都有更好的性能,直接替换完事。
    5. 确保系统已经完成dex2oat的优化
    dex2oat是android提供的提升dex加载速度和运行速度的工具,在安装apk后就会开启进程独立运行。安装apk后进adb shell,ps查看进程,就会看到dex2oat进程。而优化是否成功,则会在logcat中会有体现,但具体我忘记了。
    这个优化点是由一个bug诱发的,某个开发在apk中新加了一个支付相关的依赖,这个依赖跟android framework层用了同一个jar包,这导致apk安装后执行dex2oat时,程序判断有重复的代码,直接终止了oat优化,导致app启动慢了几百毫秒,这个也是耗费了很多时间才定位出来的问题,所以才有了这条建议:确保系统已经完成dex2oat的优化

    6. 锁粒度的优化
    因为java的synchronized封装得十分好,需要开发在需要加锁时,会直接使用synchronized,但因为synchronized是排他锁,其实可以根据业务场景使用读写锁等非排他锁。
    7. 非页面展示的message往后移
    activity的oncreate和onresume是由同一条message–ActivityThread中定义的159消息触发的,而在onResume后,理应跟着的是收到vsync后向主进程插入的doFrame消息。
    如果通过打印looper消息发现onResume和doFrame之间有其他插入的消息,可以考虑把这些消息往后移,等待doFrame完成。
    这个我目前只知道原理,具体技术还未验证。
    8. 减功能和删代码
    通过梳理业务和与产品沟通,把部分不再有用的功能给删掉,是最简单粗暴的优化方法。
    删代码的方法主要就是复用基础工具类提供的能力,避免重复造轮子
    9. bytex
    bytex是字节提供的一套利用transform做字节码修改的框架,其中有个插件可以把R.java中的变量方法内联化,进而减少方法数和优化dex数目。项目上使用后,dex文件数目由两位数降到了个位数,apk体积有十分明显的优化。而且由于app启动时bindApplication需要去加载dex,所以对启动性能的优化也是有一定帮助。
    另一个插件可以直接在字节码层面删除某些方法的调用,理论上也可以优化冷启动时间,比如日志打印,可以在release版本直接删掉。

    总结

    冷启动时长作为一个app的门面,是很重要的性能和体验指标,网上冷启动优化的方案林林总总,各有千秋,如果大家有其他优化方案,可以留言一起研究。
    做冷启动优化的过程中,我最大的感受就是,监控手段比优化手段更重要,优化总是在恶化一段时间后才能完成,而通过监控手段及时检测到冷启动的恶化,则更容易在火苗还小的时候就把火给灭了。防患于未然,总比积重难返要好。怎么监控冷启动时长,这个后续需要持续研究和探索。

    更多相关内容
  • 产品结构树(BOM)设计优化方案.pptx
  • 数据库大数据量的优化方案

    千次阅读 2022-03-04 21:36:40
    对于数据库的的优化此处给出三种优化方案: 1.优化现有mysql数据库 优点:不影响现有业务,源程序不需要修改代码,成本最低 缺点:有优化瓶颈,数据量过亿就无法继续支撑相应的业务 2.升级数据库类型,换一种100%...

    在系统开发的初期以及使用的初期,一般不会太过于在意数据库的设计以及sql语句的优化,这就会导致系统有可能在日积月累的海量数据下越来越慢直至崩溃,所以以后在系统 数据库设计之初完备的数据库模型的设计是必须的。

    优化数据库方案

    对于数据库的的优化此处给出三种优化方案:

    • 1.优化现有mysql数据库
      优点:不影响现有业务,源程序不需要修改代码,成本最低
      缺点:有优化瓶颈,数据量过亿就无法继续支撑相应的业务
    • 2.升级数据库类型,换一种100%兼容mysql的数据库
      优点:不影响现有业务,源程序不需要修改代码,你几乎不需要做任何操作就能提升数据库性能
      缺点:多花钱
    • 3.一步到位,大数据解决方案,更换newsql/nosql数据库
      优点:扩展性强,成本低,没有数据容量瓶颈
      缺点:需要修改源程序代码

    1.方案一:优化现有数据库

    • 1.数据库设计和表创建时就要考虑性能
    • 2.sql的编写需要注意优化
    • 3.分区
    • 4.分表
    • 5.分库

    1.1 数据库设计和表创建时就要考虑性能

    mysql数据库本身高度灵活,造成性能不足,严重依赖开发人员能力。也就是说开发人员能力高,则mysql性能高。这也是众多关系型数据库的通病

    所以我们在设计表时应当注意:

    • 1.表字段避免null值出现,null值很难查询优化且占用额外的索引空间,推荐默认数字0代替null。
    • 2.尽量使用INT而非BIGINT,如果非负则加上UNSIGNED(这样数值容量会扩大一倍),当然能使用TINYINT、SMALLINT、MEDIUM_INT更好。
    • 3.使用枚举或整数代替字符串类型
    • 4.尽量使用TIMESTAMP而非DATETIME
    • 5.单表不要有太多字段,建议在20以内
    • 6.用整型来存IP
    • 7.数据行的长度不要超过8020字节,如果超过这个长度的话在物理页中这条数据会占用两行从而造成存储碎片,降低查询效率。
    • 8.能够用数字类型的字段尽量选择数字类型而不用字符串类型的(电话号码),这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接回逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。
    • 9.对于不可变字符类型char和可变字符类型varchar 都是8000字节,char查询快,但是耗存储空间,varchar查询相对慢一些但是节省存储空间。在设计字段的时候可以灵活选择,例如用户名、密码等长度变化不大的字段可以选择CHAR,对于评论等长度变化大的字段可以选择VARCHAR。
    • 10.字段的长度在最大限度的满足可能的需要的前提下,应该尽可能的设得短一些,这样可以提高查询的效率,而且在建立索引的时候也可以减少资源的消耗。

    1.2 查询优化

    • 1.保证在实现功能的基础上,尽量减少对数据库的访问次数;
    • 2.通过搜索参数,尽量减少对表的访问行数,最小化结果集,从而减轻网络负担;
    • 能够分开的操作尽量分开处理,提高每次的响应速度;
    • 3.在数据窗口使用SQL时,尽量把使用的索引放在选择的首列;
    • 4.算法的结构尽量简单;
    • 5.在查询时,不要过多地使用通配符如SELECT * FROM T1语句,要用到几列就选择几列
    • 6.在可能的情况下尽量限制尽量结果集行数
    • 7.在查询时,不要过多地使用通配符如SELECT * FROM T1语句,要用到几列就选择几列;
      在没有建索引的情况下,数据库查找某一条数据,就必须进行全表扫描了,对所有数据进行一次遍历,查找出符合条件的记录。在数据量比较小的情况下,也许看不出明显的差别,但是当数据量大的情况下,这种情况就是极为糟糕的了。
    • 8.尽量使语句符合查询优化器的规则避免全表扫描而使用索引查询

    下面我重点讲解一下第8点

    比如select * from table1 where name=‘zhangsan’ and tID > 10000
    和执行:
    select * from table1 where tID > 10000 and name=‘zhangsan’
    一些人不知道以上两条语句的执行效率是否一样,因为如果简单的从语句先后上看,这两个语句的确是不一样,如果tID是一个聚合索引,那么后一句仅仅从表的 10000条以后的记录中查找就行了;而前一句则要先从全表中查找看有几个name='zhangsan’的,而后再根据限制条件条件tID> 10000来提出查询结果。但是实际上在实际查询时SQL SERVER中有一个“查询分析优化器”,它可以计算出where子句中的搜索条件并确定哪个索引能缩小表扫描的搜索空间,也就是说,它能实现自动优化。虽然查询优化器可以根据where子句自动的进行查询优化,但有时查询优化器就会不按照您的本意进行快速查询。
    在查询分析阶段,查询优化器查看查询的每个阶段并决定限制需要扫描的数据量是否有用。如果一个阶段可以被用作一个扫描参数(SARG),那么就称之为可优化的,并且可以利用索引快速获得所需数据。
    SARG的定义:用于限制搜索的一个操作,因为它通常是指一个特定的匹配,一个值的范围内的匹配或者两个以上条件的AND连接。
    列名可以出现在操作符的一边,而常数或变量出现在操作符的另一边。如:
    Name=’张三’
    价格>5000
    5000<价格
    Name=’张三’ and 价格>5000
    如果一个表达式不能满足SARG的形式,那它就无法限制搜索的范围了,也就是SQL SERVER必须对每一行都判断它是否满足WHERE子句中的所有条件。所以一个索引对于不满足SARG形式的表达式来说是无用的。
    所以,优化查询最重要的就是,尽量使语句符合查询优化器的规则避免全表扫描而使用索引查询。

    接下来通过查询优化规则接续看看在具体编写sql语句的时候应该需要注意哪些内容 :


    查询语句中对于where的优化:

    • 9.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描
      如:select id from t where num is null
      可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:select id from t where num=0

    • 10.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。优化器将无法通过索引来确定将要命中的行数,因此需要搜索该表的所有行。

    • 11.应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描
      如:select id from t where num=10 or num=20
      可以这样查询:
      select id from t where num=10
      union all
      select id from t where num=20

    • 12.in 和 not in 也要慎用,因为IN会使系统无法使用索引,而只能直接搜索表中的数据。
      如:select id from t where num in(1,2,3)
      对于连续的数值,能用 between 就不要用 in 了
      select id from t where num between 1 and 3

    • 13.尽量避免在索引过的字符数据中,使用非打头字母搜索。这也使得引擎无法利用索引。
      见如下例子:
      SELECT * FROM T1 WHERE NAME LIKE ‘%L%’
      SELECT * FROM T1 WHERE SUBSTING(NAME,2,1)=’L’
      SELECT * FROM T1 WHERE NAME LIKE ‘L%’
      即使NAME字段建有索引,前两个查询依然无法利用索引完成加快操作,引擎不得不对全表所有数据逐条操作来完成任务。而第三个查询能够使用索引来加快操作。

    • 14.必要时强制查询优化器使用某个索引,如在 where 子句中使用参数,也会导致全表扫描。因为SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。
      如下面语句将进行全表扫描:
      select id from t where num=@num
      可以改为强制查询使用索引:
      select id from t with(index(索引名)) where num=@num

    • 15.应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。
      如:

      • SELECT * FROM T1 WHERE F1/2=100
        应改为:SELECT * FROM T1 WHERE F1=100*2
      • SELECT * FROM RECORD WHERE SUBSTRING(CARD_NO,1,4)=’5378’
        应改为:SELECT * FROM RECORD WHERE CARD_NO LIKE ‘5378%’
      • SELECT member_number, first_name, last_name FROM members WHERE DATEDIFF(yy,datofbirth,GETDATE()) > 21
        应改为:SELECT member_number, first_name, last_name FROM members WHERE dateofbirth < DATEADD(yy,-21,GETDATE())

      即:任何对列的操作都将导致表扫描,它包括数据库函数、计算表达式等等,查询时要尽可能将操作移至等号右边。

    • 16.不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。

    • 17.在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。

    • 18.很多时候用 exists是一个好的选择:
      elect num from a where num in(select num from b)
      用下面的语句替换:select num from a where exists(select 1 from b where num=a.num)

    😗 where的优化就这么多啦


    接下来看看临时表的优化

    临时表的优化

      1. 尽量使用表变量来代替临时表。如果表变量包含大量数据,请注意索引非常有限(只有主键索引)。
    • 20.避免频繁创建和删除临时表,以减少系统表资源的消耗。
    • 21.临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型表或常用表中的某个数据集时。但是,对于一次性事件,最好使用导出表。
    • 22.在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;
      如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。
    • 23.如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。

    • 24.尽量避免大事务操作,提高系统并发能力

    • 25.尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。

    • 26.避免使用不兼容的数据类型。例如float和int、char和varchar、binary和varbinary是不兼容的。
      数据类型的不兼容可能使优化器无法执行一些本来可以进行的优化操作。

      例如:SELECT name FROM employee WHERE salary > 60000
      在这条语句中,如salary字段是money型的,则优化器很难对其进行优化,因为60000是个整型数。我们应当在编程时将整型转化成为钱币型,而不要等到运行时转化。

    • 27.充分利用连接条件,在某种情况下,两个表之间可能不只一个的连接条件,这时在 WHERE 子句中将连接条件完整的写上,有可能大大提高查询速度。
      例如 :

      1.SELECT SUM(A.AMOUNT) FROM ACCOUNT A,CARD B WHERE A.CARD_NO = B.CARD_NO
      2.SELECT SUM(A.AMOUNT) FROM ACCOUNT A,CARD B WHERE A.CARD_NO = B.CARD_NO AND A.ACCOUNT_NO=B.ACCOUNT_NO
      第二句将比第一句执行快得多。

    • 28.使用视图加速查询
      把表的一个子集进行排序并创建视图,有时能加速查询。它有助于避免多重排序 操作,而且在其他方面还能简化优化器的工作。
      例如:

      SELECT cust.name,rcvbles.balance,……other columns
      FROM cust,rcvbles
      WHERE cust.customer_id = rcvlbes.customer_id
      AND rcvblls.balance>0
      AND cust.postcode>“98000”
      ORDER BY cust.name

      如果这个查询要被执行多次而不止一次,可以把所有未付款的客户找出来放在一个视图中,并按客户的名字进行排序:

      CREATE VIEW DBO.V_CUST_RCVLBES AS
      SELECT cust.name,rcvbles.balance,……other columns
      FROM cust,rcvbles
      WHERE cust.customer_id = rcvlbes.customer_id
      AND rcvblls.balance>0
      ORDER BY cust.name

      然后以下面的方式在视图中查询:

      SELECT * FROM V_CUST_RCVLBES
      WHERE postcode>“98000”

      视图中的行要比主表中的行少,而且物理顺序就是所要求的顺序,减少了磁盘I/O,所以查询工作量可以得到大幅减少。

    • 29.能用DISTINCT的就不用GROUP BY

      SELECT OrderID FROM Details
      WHERE UnitPrice > 10 GROUP BY OrderID

      可改为:

      SELECT DISTINCT OrderID FROM Details
      WHERE UnitPrice > 10

    • 30.能用UNION ALL就不要用UNION
      UNION ALL不执行SELECT DISTINCT函数,这样就会减少很多不必要的资源

    • 31.尽量不要用SELECT INTO语句。
      SELECT INTO 语句会导致表锁定,阻止其他用户访问该表。

    以上就是常见的一些查询优化方案,但是在我们实际开发中如果数据量比较少,此时是比较不出来的,这时可以用查看执行计划,即:

    把实现相同功能的多条SQL语句考到查询分析器,按CTRL+L看查所利用的索引,表扫描次数(这两个对性能影响最大),总体上看询成本百分比即可。

    1.3 算法优化

    尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。
    使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。
    与临时表一样,游标并不是不可使用。对小型数据集使用 FAST_FORWARD 游标通常要优于其他逐行处理方法,尤其是在必须引用几个表才能获得所需的数据时。在结果集中包括“合计”的例程通常要比使用游标执行的速度快。如果开发时间允许,基于游标的方法和基于集的方法都可以尝试一下,看哪一种方法的效果更好。
    游标提供了对特定集合中逐行扫描的手段,一般使用游标逐行遍历数据,根据取出的数据不同条件进行不同的操作。尤其对多表和大表定义的游标(大的数据集合)循环很容易使程序进入一个漫长的等特甚至死机。
    在有些场合,有时也非得使用游标,此时也可考虑将符合条件的数据行转入临时表中,再对临时表定义游标进行操作,可时性能得到明显提高。

    1.4 建立高效的索引

    创建索引一般有以下两个目的:

    • 维护被索引列的唯一性
    • 提供快速访问表中数据的策略

    大型数据库一般有两种索引:

    • 聚集索引
    • 非聚集索引

    一个没有簇索引的表是按堆结构存储数据,所有的数据均添加在表的尾部
    建立了簇索引的表,其数据在物理上会按照簇索引键的顺序存储,一个表只允许有一个簇索引,因此,根据B树结构,可以理解添加任何一种索引均能提高按索引列查询的速度,但会降低插入、更新、删除操作的性能,尤其是当填充因子(Fill Factor)较大时。
    所以对索引较多的表进行频繁的插入、更新、删除操作,建表和索引时因设置较小的填充因子,以便在各数据页中留下较多的自由空间,减少页分割及重新组织的工作。
    同时通过上面的定义我们也可以看出索引并不是越多越好

    聚集索引和非聚集的区别

    在需要了解两者的区别之前我们先弄清楚这两个索引都是干嘛的
    实际上,可以把索引理解为一种特殊的目录

    • 聚集索引

      我们的汉语字典的正文本身就是一个聚集索引。
      比如,我们要查“安”字,因为“安”的拼音是“an”,而按照拼音排序汉字的字典是以英文字母“a”开头并以“z”结尾的,那么“安”字就自然地排在字典的前部。如果您翻完了所有以“a”开头的部分仍然找不到这个字,那么就说明您的字典中没有这个字。也就是说,字典的正文部分本身就是一个目录,您不需要再去查其他目录来找到您需要找的内容。
      我们把这种正文内容本身就是一种按照一定规则排列的目录称为“聚集索引”。

    • 非聚集索引

      如果遇到不认识的字,不知道它的发音,这时候,需要去根据“偏旁部首”查到您要找的字,然后根据这个字后的页码直接翻到某页来找到您要找的字。
      但您结合“部首目录”和“检字表”而查到的字的排序并不是真正的正文的排序方法,比如您查“张”字,我们可以看到在查部首之后的检字表中“张”的页码是672页,检字表中“张”的上面是“驰”字,但页码却是63页,“张”的下面是“弩”字,页面是390页。很显然,这些字并不是真正的分别位于“张”字的上下方,现在您看到的连续的“驰、张、弩”三字实际上就是他们在非聚集索引中的排序,是字典正文中的字在非聚集索引中的映射。我们可以通过这种方式来找到您所需要的字,但它需要两个过程,先找到目录中的结果,然后再翻到您所需要的页码。
      我们把这种目录纯粹是目录,正文纯粹是正文的排序方式称为“非聚集索引”。

    于是我们做以下总结:

    • 聚集索引一个表只能有一个
    • 非聚集索引一个表可以存在多个

    • 聚集索引存储记录是物理上连续存在
    • 非聚集索引是逻辑上的连续,物理存储并不连续

    • 聚集索引:物理存储按照索引排序;聚集索引是一种索引组织形式,索引的键值逻辑顺序决定了表数据行的物理存储顺序。
    • 非聚集索引:物理存储不按照索引排序;非聚集索引则就是普通索引了,仅仅只是对数据列创建相应的索引,不影响整个表的物理存储顺序。

    • 索引是通过二叉树的数据结构来描述的,我们可以这么理解聚簇索引:索引的叶节点就是数据节点。
    • 非聚簇索引的叶节点仍然是索引节点,只不过有一个指针指向对应的数据块。

    聚集索引的优势与缺点:

    聚集索引插入数据时速度要慢(时间花费在“物理存储的排序”上,也就是首先要找到位置然后插入),但是查询数据比非聚集数据的速度快。

    对于以上内容我们再继续深入总结一下:

    • 聚集索引的缺点就是对表的修改较慢,这是为了保持表中的记录的物理顺序与存储顺序一致,而把记录插入数据页的相应的位置,就必须在数据页中进行重排,导致了执行速度的降低。
    • 非聚集索引指定了表中记录的逻辑顺序,但表中的记录的物理顺序与索引顺序不一致,两种索引的存储结构均是B+树,但非聚集索引的叶子节点并不与实际的数据页重叠,而是采用叶子层包含一个指向表中的记录在数据页中指针的方式。同时非聚集索引比聚集索引的层次多,添加记录不会引起数组顺序的重组。

    何时使用聚集索引或非聚集索引

    在这里插入图片描述

    事实上,我们可以通过前面聚集索引和非聚集索引的定义的例子来理解上表。
    如:返回某范围内的数据一项。

    比如您的某个表有一个时间列,恰好您把聚合索引建立在了该列,这时您查询2004年1月1日至2004年10月1日之间的全部数据时,这个速度就将是很快的,因为您的这本字典正文是按日期进行排序的,聚类索引只需要找到要检索的所有数据中的开头和结尾数据即可;
    而不像非聚集索引,必须先查到目录中查到每一项数据对应的页码,然后再根据页码查到具体内容。

    索引的使用误区

    • 1、主键就是聚集索引
      这种想法是极端错误的,是对聚集索引的一种浪费。
      虽然SQL SERVER默认是在主键上建立聚集索引的。
      通常,我们会在每个表中都建立一个ID列,以区分每条数据,并且这个ID列是自动增大的,步长一般为1。我们的这个办公自动化的实例中的列Gid就是如此。此时,如果我们将这个列设为主键,SQL SERVER会将此列默认为聚集索引。这样做有好处,就是可以让您的数据在数据库中按照ID进行物理排序,但这样做意义不大。
      显而易见,聚集索引的优势是很明显的,而每个表中只能有一个聚集索引的规则,这使得聚集索引变得更加珍贵。
      从我们前面谈到的聚集索引的定义我们可以看出,使用聚集索引的最大好处就是能够根据查询要求,迅速缩小查询范围,避免全表扫描。在实际应用中,因为ID号是自动生成的,我们并不知道每条记录的ID号,所以我们很难在实践中用ID号来进行查询。这就使让ID号这个主键作为聚集索引成为一种资源浪费。其次,让每个ID号都不同的字段作为聚集索引也不符合“大数目的不同值情况下不应建立聚合索引”规则;当然,这种情况只是针对用户经常修改记录内容,特别是索引项的时候会负作用,但对于查询速度并没有影响。

    • 2、只要建立索引就能显著提高查询速度
      事实上,我们可以发现上面的例子中,第2、3条语句完全相同,且建立索引的字段也相同;不同的仅是前者在fariqi字段上建立的是非聚合索引,后者在此字段上建立的是聚合索引,但查询速度却有着天壤之别。所以,并非是在任何字段上简单地建立索引就能提高查询速度。
      从建表的语句中,我们可以看到这个有着1000万数据的表中fariqi字段有5003个不同记录。在此字段上建立聚合索引是再合适不过了。在现实中,我们每天都会发几个文件,这几个文件的发文日期就相同,这完全符合建立聚集索引要求的:“既不能绝大多数都相同,又不能只有极少数相同”的规则。由此看来,我们建立“适当”的聚合索引对于我们提高查询速度是非常重要的。

    • 3、把所有需要提高查询速度的字段都加进聚集索引,以提高查询速度
      上面已经谈到:在进行数据查询时都离不开字段的是“日期”还有用户本身的“用户名”。既然这两个字段都是如此的重要,我们可以把他们合并起来,建立一个复合索引(compound index)。
      很多人认为只要把任何字段加进聚集索引,就能提高查询速度,也有人感到迷惑:如果把复合的聚集索引字段分开查询,那么查询速度会减慢吗?
      如果仅用聚集索引的起始列作为查询条件和同时用到复合聚集索引的全部列的查询速度是几乎一样的,甚至比用上全部的复合索引列还要略快(在查询结果集数目一样的情况下);
      而如果仅用复合聚集索引的非起始列作为查询条件的话,这个索引是不起任何作用的。
      如果复合索引的所有列都用上,而且查询结果少的话,这样就会形成“索引覆盖”,因而性能可以达到最优。同时,请记住:无论您是否经常使用聚合索引的其他列,但其前导列一定要是使用最频繁的列。

    “水可载舟,亦可覆舟”,索引也一样。索引有助于提高检索性能,但过多或不当的索引也会导致系统低效。因为用户在表中每加进一个索引,数据库就要做更多的工作。过多的索引甚至会导致索引碎片
    所以说,我们要建立一个“适当”的索引体系,特别是对聚合索引的创建,更应精益求精,以使您的数据库能得到高性能的发挥。

    1.5.引擎

    目前广泛使用的是MyISAM和InnoDB两种引擎:

    MyISAM

    MyISAM引擎是MySQL 5.1及之前版本的默认引擎,它的特点是:

    • 1.不支持行锁,读取时对需要读到的所有表加锁,写入时则对表加排它锁
    • 2.不支持事务
    • 3.不支持外键
    • 4.不支持崩溃后的安全恢复
    • 5.在表有读取查询的同时,支持往表中插入新纪录
    • 6.支持BLOB和TEXT的前500个字符索引,支持全文索引
    • 7.支持延迟更新索引,极大提升写入性能
    • 8.对于不会进行修改的表,支持压缩表,极大减少磁盘空间占用

    InnoDB

    InnoDB在MySQL 5.5后成为默认索引,它的特点是:

    • 1.支持行锁,采用MVCC来支持高并发
    • 2.支持事务
    • 3.支持外键
    • 4.支持崩溃后的安全恢复
    • 5.不支持全文索引

    总体来讲,MyISAM适合SELECT密集型的表,而InnoDB适合INSERT和UPDATE密集型的表

    MyISAM速度可能超快,占用存储空间也小,但是程序如果要求事务支持,故InnoDB是必须的,则该方案无法执行!

    1.6 分区

    MySQL在5.1版引入的分区是一种简单的水平拆分,用户需要在建表的时候加上分区参数,对应用是透明的无需修改代码。

    对用户来说,分区表是一个独立的逻辑表,但是底层由多个物理子表组成,实现分区的代码实际上是通过对一组底层表的对象封装,但对SQL层来说是一个完全封装底层的黑盒子。MySQL实现分区的方式也意味着索引也是按照分区的子表定义,没有全局索引。

    用户的SQL语句是需要针对分区表做优化,SQL条件中要带上分区条件的列,从而使查询定位到少量的分区上,否则就会扫描全部分区,可以通过EXPLAIN PARTITIONS来查看某条SQL语句会落在那些分区上,从而进行SQL优化,我测试,查询时不带分区条件的列,也会提高速度,故该措施值得一试。

    分区的好处是:

    • 1.可以让单表存储更多的数据
    • 2.分区表的数据更容易维护,可以通过清楚整个分区批量删除大量数据,也可以增加新的分区来支持新插入的数据。另外,还可以对一个独立分区进行优化、检查、修复等操作
    • 3.部分查询能够从查询条件确定只落在少数分区上,速度会很快
    • 4.分区表的数据还可以分布在不同的物理设备上,从而搞笑利用多个硬件设备
    • 5.可以使用分区表赖避免某些特殊瓶颈,例如InnoDB单个索引的互斥访问、ext3文件系统的inode锁竞争
    • 6.可以备份和恢复单个分区

    分区的限制和缺点:

    • 1.一个表最多只能有1024个分区
    • 2.如果分区字段中有主键或者唯一索引的列,那么所有主键列和唯一索引列都必须包含进来
    • 3.分区表无法使用外键约束
    • 4.NULL值会使分区过滤无效
    • 5.所有分区必须使用相同的存储引擎

    分区的类型:

    • 1.RANGE分区:基于属于一个给定连续区间的列值,把多行分配给分区
    • 2.LIST分区:类似于按RANGE分区,区别在于LIST分区是基于列值匹配一个离散值集合中的某个值来进行选择
    • 3.HASH分区:基于用户定义的表达式的返回值来进行选择的分区,该表达式使用将要插入到表中的这些行的列值进行计算。这个函数可以包含MySQL中有效的、产生非负整数值的任何表达式
    • 4.KEY分区:类似于按HASH分区,区别在于KEY分区只支持计算一列或多列,且MySQL服务器提供其自身的哈希函数。必须有一列或多列包含整数值

    1.7 分表

    分表就是把一张大表,按照如上过程都优化了,还是查询卡死,那就把这个表分成多张表,把一次查询分成多次查询,然后把结果组合返回给用户。

    分表分为垂直拆分和水平拆分,通常以某个字段做拆分项。比如以id字段拆分为100张表: 表名为 tableName_id%100

    但:分表需要修改源程序代码,会给开发带来大量工作,极大的增加了开发成本,故:只适合在开发初期就考虑到了大量数据存在,做好了分表处理,不适合应用上线了再做修改,成本太高!!!

    1.8 分库

    如果是已经设计好的只有一个数据库需要分成多个,也会带来大量的开发成本,得不偿失!如果是分布式环境下可以按照模块以及业务的划分在设计之初进行分库,同时建议做个读写分离。

    方案2:升级数据库,换一个100%兼容mysql的数据库

    mysql性能不行,那就换个。为保证源程序代码不修改,保证现有业务平稳迁移,故需要换一个100%兼容mysql的数据库。

    开源选择:

    1.tiDB https://github.com/pingcap/tidb

    2.Cubrid https://www.cubrid.org/

    开源数据库会带来大量的运维成本且其工业品质和MySQL尚有差距,有很多坑要踩,如果你公司要求必须自建数据库,那么选择该类型产品。

    云数据选择

    1.阿里云POLARDB

    淘宝使用的,扛得住双十一,性能卓著

    2.阿里云HybridDB for MySQL (原PetaData)

    官方介绍:云数据库HybridDB for MySQL (原名PetaData)是同时支持海量数据在线事务(OLTP)和在线分析(OLAP)的HTAP(Hybrid Transaction/Analytical Processing)关系型数据库。

    方案三:去掉mysql,换大数据引擎处理数据

    数据量过亿了,没得选了,只能上大数据了。

    开源解决方案

    hadoop家族。hbase/hive怼上就是了。但是有很高的运维成本,一般公司是玩不起的,没十万投入是不会有很好的产出的!

    云解决方案

    这个就比较多了,也是一种未来趋势,大数据由专业的公司提供专业的服务,小公司或个人购买服务,大数据就像水/电等公共设施一样,存在于社会的方方面面。

    国内做的最好的当属阿里云。

    MaxCompute可以理解为开源的Hive,提供sql/mapreduce/ai算法/python脚本/shell脚本等方式操作数据,数据以表格的形式展现,以分布式方式存储,采用定时任务和批处理的方式处理数据。
    DataWorks提供了一种工作流的方式管理你的数据处理任务和调度监控。

    展开全文
  • 微信小程序性能优化方案

    千次阅读 2020-12-17 18:44:21
    微信小程序性能优化方案 提高加载性能 小程序代码包准备(下载代码包) 开发者代码注入 页面渲染优化 提升渲染性能 setData工作原理 优化方法

    微信小程序性能优化方案

    微信小程序如果想要优化性能,有关键性的两点:

    1. 提高加载性能
    2. 提高渲染性能

    提高加载性能

    首先,我们需要了解,当用户点击小程序后小程序启动的流程,具体详细的流程在微信官方文档 启动性能 中有描述,以下的篇章我们主要着重于我们能够改变优化的环节。

    小程序代码包准备(下载代码包) => 开发者代码注入(逻辑和渲染) => 首页(初次)渲染

    小程序代码包准备(下载代码包)

    下载代码包提升性能的最关键性一点就是,控制代码包的大小。

    至于如何控制包的大小?

    1. 分包加载
      我在另一篇博客中具体讲解了分包加载,大家可以移步去学习
      小程序分包加载实践
    2. 代码重构和优化
      通过代码重构,降低代码冗余。在使用如 Webpack 等打包工具时,要尽量利用 tree-shaking 等特性去除冗余代码,也要注意防止打包时引入不需要的库和依赖。
    3. 控制代码包内图片等资源
      避免在代码包中包含过多、过大的图片,应尽量采用网络图片。
    4. 及时清理没有使用到的代码和资源
      目前小程序打包是会将工程下所有文件都打入代码包内,因此需要及时清理已经没有被实际使用到的库文件和资源也。

    开发者代码注入

    1. 减少启动过程的同步调用
      在小程序启动流程中,会注入开发者代码并顺序同步执行 App.onLaunch, App.onShow, Page.onLoad, Page.onShow。
      在小程序初始化代码(Page,App 定义之外的内容)和启动相关的几个生命周期中,应避免执行复杂的计算逻辑或过度使用Sync结尾的同步API,如 wx.getStorageSync,wx.getSystemInfoSync 等。
      对于 getSystemInfo, getSystemInfoSync 的结果应进行缓存,避免重复调用。

    2. 使用懒注入
      通常情况下,在小程序启动时,启动页面所在分包和主包(独立分包除外)的所有JS代码会全部合并注入,包括其他未访问的页面以及未用到自定义组件,造成很多没有使用的代码注入到小程序运行环境中,影响注入耗时和内存占用。

    自基础库版本 2.11.1 起,小程序支持仅注入当前页面需要的自定义组件和当前页面代码,以降低小程序的启动时间和运行时内存。开发者可以在 app.json 中配置:

    {
      "lazyCodeLoading": "requiredComponents"
    }
    

    页面渲染优化

    1. 提前首屏数据请求

    数据预拉取:能够在小程序冷启动的时候通过微信后台提前向第三方服务器拉取业务数据,当代码包加载完时可以更快地渲染页面,减少用户等待时间,从而提升小程序的打开速度。

    周期性更新:在用户未打开小程序的情况下,也能从服务器提前拉取数据,当用户打开小程序时可以更快地渲染页面,减少用户等待时间。

    1. 骨架屏
      在页面数据未准备好时(如需要通过网络获取),尽量避免展示空白页面,应先通过骨架屏展示页面的大致结构,请求数据返回后在进行页面更新,以提升用户的等待意愿。

    2. 缓存请求数据
      小程序提供了wx.setStorage、wx.getStorage等读写本地缓存的能力,数据存储在本地,返回的会比网络请求快。可以优先从缓存中获取数据来渲染视图,等待网络请求返回后进行更新。

    3. 精简首屏数据
      延迟请求非关键渲染数据,与视图层渲染无关的数据尽量不要放在 data 中,加快页面渲染完成时间。

    提升渲染性能

    setData工作原理

    小程序的视图层目前使用 WebView 作为渲染载体,而逻辑层是由独立的 JavascriptCore 作为运行环境。在架构上,WebView 和 JavascriptCore 都是独立的模块,并不具备数据直接共享的通道。当前,视图层和逻辑层的数据传输,实际上通过两边提供的 evaluateJavascript 所实现。即用户传输的数据,需要将其转换为字符串形式传递,同时把转换后的数据内容拼接成一份 JS 脚本,再通过执行 JS 脚本的形式传递到两边独立环境。

    而 evaluateJavascript 的执行会受很多方面的影响,数据到达视图层并不是实时的。

    每调用一次setData, 都是逻辑层向渲染层的一次通讯,这个通信还不是直接传给webView, 而是通过走了native层,通讯的开销很大。

    渲染层收到通讯后,还需要重新渲染出来,所以,一次setData带来两次开销:通信的开销 + webview更新的开销。

    优化方法

    1. 减少setData的数据量
      如果一个数据不能会影响渲染层,则不用放在setData里面

    2. 合并setData的请求,减少通讯的次数

    3. 列表的局部更新

    在一个列表中,有n条数据,当前需求为实现点赞效果。

    首先,我们可以先展示效果,再进行异步请求,只在请求失败的情况下提示。
    其次,我们可以采用布局刷新,根据点赞数据的id和index分别获取数据以及更新数据

    this.setData({
        list[index] = newList[index]
    })
    
    1. 小心后台页面的js

    小程序中可能有n个页面,所有的这些页面,虽然都拥有自己的webview(渲染层), 但是却共享同一个js运行环境。也就是说,当你跳到了另外一个页面(假设是B页面),本页面(假设是A页面)的定时器等js操作仍在进行,并且不会被销毁,并且会抢占B页面的资源。

    在h5的环境中,当我们跳转到其他页面,老页面的js环境会被自动销毁,定时器什么都被销毁掉了,因此我们不需要关心老页面中,还有哪些js代码可能还会执行。但是在小程序中,我们必须手动的“清理”掉这样的代码。

    1. 小心onPageScroll

    pageScroll 事件,也是一次通讯,是webview层向js逻辑层的通讯。这个事件很容易被频繁调用,开销较大,并且假如回调函数有复杂的setData的话, 性能就会很差了。

    1. 合理使用小程序组件
      自定义组件的更新只在组件内部进行,不会影响页面其他元素。因为各个组件具有独立的逻辑空间、数据、样式环境及 setData 调用。
      基于自定义组件的 Shadow DOM 模型设计,我们可以将页面中一些需要高频执行 setData 更新的功能模块(如倒计时、进度条等)封装成自定义组件嵌入到页面中。
      当这些自定义组件视图需要更新时,执行的是组件自己的 setData,新旧节点树的对比计算和渲染树的更新都只限于组件内有限的节点数量,有效降低渲染时间开销。

    2. 图片懒加载

    渲染页面时,只渲染出现在视图范围内的元素。

    IntersectionObserver
    微信提供了IntersectionObserver对象,用于推断某些节点是否可以被用户看见、有多大比例可以被用户看见

    常规的做法是,通过getBoundingClientRect()获取元素的位置,然后与页面滚动位置比较,如果出现在视图内,就将img显示。这种方式有2个问题
    getBoundingClientRect()方法调用本身容易引起页面重排
    监听滚动事件本身就频繁触发,虽然可以通过节流的方式来减少,但还是容易增加无谓代码处理

    <img class="img-{{index}}" wx:for="{{data}}" wx:if="{{item.imgShow}}" wx:key="index"></img>
    
    let data = list;
    data.forEach((item,index)=>{
        this.createIntersectionObserver().relativeToViewport.observe(`.img-${index}`,res=>{
            if (res.intersectionRatio > 0){
                this.setData({
                    item.imgShow:true
                })
            }
        })
    })
    

    intersectionRatio值大于0,说明元素出现在视图中了,显示图片组件。

    1. 善用onReady和onLoad
      onLoad是页面加载阶段,在onLoad阶段异步请求页面展示数据,onReady是页面加载完成阶段,对于页面之后需要展示或使用,但刚开始并不展示的数据可以在onReady阶段再进行请求加载。
    展开全文
  • 当MySQL单表记录数过大时,增删改查性能都会急剧下降,可以参考以下步骤来优化: 单表优化 除非单表数据未来会一直不断上涨,否则不要一开始就考虑拆分,拆分会带来逻辑、部署、运维的各种复杂度,一般以整型值...

    当MySQL单表记录数过大时,增删改查性能都会急剧下降,可以参考以下步骤来优化:

    单表优化

     

    除非单表数据未来会一直不断上涨,否则不要一开始就考虑拆分,拆分会带来逻辑、部署、运维的各种复杂度,一般以整型值为主的表在千万级以下,字符串为主的表在五百万以下是没有太大问题的。而事实上很多时候MySQL单表的性能依然有不少优化空间,甚至能正常支撑千万级以上的数据量:

    字段

    1、尽量使用TINYINT、SMALLINT、MEDIUM_INT作为整数类型而非INT,如果非负则加上UNSIGNED

    2、VARCHAR的长度只分配真正需要的空间

    3、使用枚举或整数代替字符串类型

    4、尽量使用TIMESTAMP而非DATETIME,

    5、单表不要有太多字段,建议在20以内

    6、避免使用NULL字段,很难查询优化且占用额外索引空间

    7、用整型来存IP

    索引

     

    1、索引并不是越多越好,要根据查询有针对性的创建,考虑在WHERE和ORDER BY命令上涉及的列建立索引,可根据EXPLAIN来查看是否用了索引还是全表扫描

    2、应尽量避免在WHERE子句中对字段进行NULL值判断,否则将导致引擎放弃使用索引而进行全表扫描

    3、值分布很稀少的字段不适合建索引,例如”性别”这种只有两三个值的字段

    4、字符字段只建前缀索引

    5、字符字段最好不要做主键

    6、不用外键,由程序保证约束

    7、尽量不用UNIQUE,由程序保证约束

    8、使用多列索引时主意顺序和查询条件保持一致,同时删除不必要的单列索引

    查询SQL

    1、可通过开启慢查询日志来找出较慢的SQL

    2、不做列运算:SELECT id WHERE age + 1 = 10,任何对列的操作都将导致表扫描,它包括数据库教程函数、计算表达式等等,查询时要尽可能将操作移至等号右边

    3、sql语句尽可能简单:一条sql只能在一个cpu运算;大语句拆小语句,减少锁时间;一条大sql可以堵死整个库

    4、不用SELECT *

    5、OR改写成IN:OR的效率是n级别,IN的效率是log(n)级别,in的个数建议控制在200以内

    6、不用函数和触发器,在应用程序实现

    7、避免%xxx式查询

    8、少用JOIN

    9、使用同类型进行比较,比如用'123'和'123'比,123和123比

    10、尽量避免在WHERE子句中使用 != 或 <> 操作符,否则将引擎放弃使用索引而进行全表扫描

    11、对于连续数值,使用BETWEEN不用IN:SELECT id FROM t WHERE num BETWEEN 1 AND 5

    12、列表数据不要拿全表,要使用LIMIT来分页,每页数量也不要太大

    引擎

    目前广泛使用的是MyISAM和InnoDB两种引擎:

    MyISAM

    MyISAM引擎是MySQL 5.1及之前版本的默认引擎,它的特点是:

    1、不支持行锁,读取时对需要读到的所有表加锁,写入时则对表加排它锁

    2、不支持事务

    3、不支持外键

    4、不支持崩溃后的安全恢复

    5、在表有读取查询的同时,支持往表中插入新纪录

    6、支持BLOB和TEXT的前500个字符索引,支持全文索引

    7、支持延迟更新索引,极大提升写入性能

    8、对于不会进行修改的表,支持压缩表,极大减少磁盘空间占用

    InnoDB

    InnoDB在MySQL 5.5后成为默认索引,它的特点是:

    1、支持行锁,采用MVCC来支持高并发

    2、支持事务

    3、支持外键

    4、支持崩溃后的安全恢复

    5、不支持全文索引

    总体来讲,MyISAM适合SELECT密集型的表,而InnoDB适合INSERT和UPDATE密集型的表

    系统调优参数

    可以使用下面几个工具来做基准测试:

    sysbench:一个模块化,跨平台以及多线程的性能测试工具

    iibench-mysql:基于 Java 的 MySQL/Percona/MariaDB 索引进行插入性能测试工具

    tpcc-mysql:Percona开发的TPC-C测试工具

    具体的调优参数内容较多,具体可参考官方文档,这里介绍一些比较重要的参数:

    back_log:back_log值指出在MySQL暂时停止回答新请求之前的短时间内多少个请求可以被存在堆栈中。也就是说,如果MySql的连接数据达到max_connections时,新来的请求将会被存在堆栈中,以等待某一连接释放资源,该堆栈的数量即back_log,如果等待连接的数量超过back_log,将不被授予连接资源。可以从默认的50升至500

    wait_timeout:数据库连接闲置时间,闲置连接会占用内存资源。可以从默认的8小时减到半小时

    max_user_connection:最大连接数,默认为0无上限,最好设一个合理上限thread_concurrency:并发线程数,设为CPU核数的两倍

    skip_name_resolve:禁止对外部连接进行DNS解析,消除DNS解析时间,但需要所有远程主机用IP访问

    10、key_buffer_size:索引块的缓存大小,增加会提升索引处理速度,对MyISAM表性能影响最大。对于内存4G左右,可设为256M或384M,通过查询show status like'key_read%',保证key_reads / key_read_requests在0.1%以下最好

    innodb_buffer_pool_size:缓存数据块和索引块,对InnoDB表性能影响最大。通过查询show status like 'Innodb_buffer_pool_read%',保证 (Innodb_buffer_pool_read_requests– Innodb_buffer_pool_reads)/ Innodb_buffer_pool_read_requests越高越好

    innodb_additional_mem_pool_size:InnoDB存储引擎用来存放数据字典信息以及一些内部数据结构的内存空间大小,当数据库对象非常多的时候,适当调整该参数的大小以确保所有数据都能存放在内存中提高访问效率,当过小的时候,MySQL会记录Warning信息到数据库的错误日志中,这时就需要该调整这个参数大小

    innodb_log_buffer_size:InnoDB存储引擎的事务日志所使用的缓冲区,一般来说不建议超过32MB

    query_cache_size:缓存MySQL中的ResultSet,也就是一条SQL语句执行的结果集,所以仅仅只能针对select语句。当某个表的数据有任何任何变化,都会导致所有引用了该表的select语句在Query Cache中的缓存数据失效。所以,当我们的数据变化非常频繁的情况下,使用Query Cache可能会得不偿失。根据命中率(Qcache_hits/(Qcache_hits+Qcache_inserts)*100))进行调整,一般不建议太大,256MB可能已经差不多了,大型的配置型静态数据可适当调大.

    可以通过命令show status like 'Qcache_%'查看目前系统Query catch使用大小

    read_buffer_size:MySql读入缓冲区大小。对表进行顺序扫描的请求将分配一个读入缓冲区,MySql会为它分配一段内存缓冲区。如果对表的顺序扫描请求非常频繁,可以通过增加该变量值以及内存缓冲区大小提高其性能

    sort_buffer_size:MySql执行排序使用的缓冲大小。如果想要增加ORDER BY的速度,首先看是否可以让MySQL使用索引而不是额外的排序阶段。如果不能,可以尝试增加sort_buffer_size变量的大小

    read_rnd_buffer_size:MySql的随机读缓冲区大小。当按任意顺序读取行时(例如,按照排序顺序),将分配一个随机读缓存区。进行排序查询时,MySql会首先扫描一遍该缓冲,以避免磁盘搜索,提高查询速度,如果需要排序大量数据,可适当调高该值。但MySql会为每个客户连接发放该缓冲空间,所以应尽量适当设置该值,以避免内存开销过大。

    record_buffer:每个进行一个顺序扫描的线程为其扫描的每张表分配这个大小的一个缓冲区。如果你做很多顺序扫描,可能想要增加该值

    thread_cache_size:保存当前没有与连接关联但是准备为后面新的连接服务的线程,可以快速响应连接的线程请求而无需创建新的

    table_cache:类似于thread_cache_size,但用来缓存表文件,对InnoDB效果不大,主要用于MyISAM

    升级硬件

    Scale up,这个不多说了,根据MySQL是CPU密集型还是I/O密集型,通过提升CPU和内存、使用SSD,都能显著提升MySQL性能

    读写分离

    也是目前常用的优化,从库读主库写,一般不要采用双主或多主引入很多复杂性,尽量采用文中的其他方案来提高性能。同时目前很多拆分的解决方案同时也兼顾考虑了读写分离

    缓存

    缓存可以发生在这些层次:

    MySQL内部:在系统调优参数介绍了相关设置

    数据访问层:比如MyBatis针对SQL语句做缓存,而Hibernate可以精确到单个记录,这里缓存的对象主要是持久化对象Persistence Object

    应用服务层:这里可以通过编程手段对缓存做到更精准的控制和更多的实现策略,这里缓存的对象是数据传输对象Data Transfer Object

    Web层:针对web页面做缓存

    浏览器客户端:用户端的缓存

    可以根据实际情况在一个层次或多个层次结合加入缓存。这里重点介绍下服务层的缓存实现,目前主要有两种方式:

    直写式(Write Through):在数据写入数据库后,同时更新缓存,维持数据库与缓存的一致性。这也是当前大多数应用缓存框架如Spring Cache的工作方式。这种实现非常简单,同步好,但效率一般。

    回写式(Write Back):当有数据要写入数据库时,只会更新缓存,然后异步批量的将缓存数据同步到数据库上。这种实现比较复杂,需要较多的应用逻辑,同时可能会产生数据库与缓存的不同步,但效率非常高。

    表分区

    MySQL在5.1版引入的分区是一种简单的水平拆分,用户需要在建表的时候加上分区参数,对应用是透明的无需修改代码

    对用户来说,分区表是一个独立的逻辑表,但是底层由多个物理子表组成,实现分区的代码实际上是通过对一组底层表的对象封装,但对SQL层来说是一个完全封装底层的黑盒子。MySQL实现分区的方式也意味着索引也是按照分区的子表定义,没有全局索引

    用户的SQL语句是需要针对分区表做优化,SQL条件中要带上分区条件的列,从而使查询定位到少量的分区上,否则就会扫描全部分区,可以通过EXPLAIN PARTITIONS来查看某条SQL语句会落在那些分区上,从而进行SQL优化,如下图5条记录落在两个分区上:

    史上最全MySQL 大表优化方案(长文)

     

    分区的好处是:

    1、可以让单表存储更多的数据

    2、分区表的数据更容易维护,可以通过清楚整个分区批量删除大量数据,也可以增加新的分区来支持新插入的数据。另外,还可以对一个独立分区进行优化、检查、修复等操作

    3、部分查询能够从查询条件确定只落在少数分区上,速度会很快

    4、分区表的数据还可以分布在不同的物理设备上,从而搞笑利用多个硬件设备

    5、可以使用分区表赖避免某些特殊瓶颈,例如InnoDB单个索引的互斥访问、ext3文件系统的inode锁竞争

    7、可以备份和恢复单个分区

    分区的限制和缺点:

    1、一个表最多只能有1024个分区

    2、如果分区字段中有主键或者唯一索引的列,那么所有主键列和唯一索引列都必须包含进来

    3、分区表无法使用外键约束

    4、NULL值会使分区过滤无效

    5、所有分区必须使用相同的存储引擎

    分区的类型:

    RANGE分区:基于属于一个给定连续区间的列值,把多行分配给分区

    LIST分区:类似于按RANGE分区,区别在于LIST分区是基于列值匹配一个离散值集合中的某个值来进行选择

    HASH分区:基于用户定义的表达式的返回值来进行选择的分区,该表达式使用将要插入到表中的这些行的列值进行计算。这个函数可以包含MySQL中有效的、产生非负整数值的任何表达式

    KEY分区:类似于按HASH分区,区别在于KEY分区只支持计算一列或多列,且MySQL服务器提供其自身的哈希函数。必须有一列或多列包含整数值

    分区适合的场景有:

    最适合的场景数据的时间序列性比较强,则可以按时间来分区,如下所示:

    CREATE TABLE members ( firstname VARCHAR(25) NOT NULL, lastname VARCHAR(25) NOT NULL, username VARCHAR(16) NOT NULL, email VARCHAR(35), joined DATE NOT NULL)PARTITION BY RANGE( YEAR(joined) ) ( PARTITION p0 VALUES LESS THAN (1960), PARTITION p1 VALUES LESS THAN (1970), PARTITION p2 VALUES LESS THAN (1980), PARTITION p3 VALUES LESS THAN (1990), PARTITION p4 VALUES LESS THAN MAXVALUE);

    查询时加上时间范围条件效率会非常高,同时对于不需要的历史数据能很容的批量删除。

    如果数据有明显的热点,而且除了这部分数据,其他数据很少被访问到,那么可以将热点数据单独放在一个分区,让这个分区的数据能够有机会都缓存在内存中,查询时只访问一个很小的分区表,能够有效使用索引和缓存

    另外MySQL有一种早期的简单的分区实现 – 合并表(merge table),限制较多且缺乏优化,不建议使用,应该用新的分区机制来替代

    垂直拆分

    垂直分库是根据数据库里面的数据表的相关性进行拆分,比如:一个数据库里面既存在用户数据,又存在订单数据,那么垂直拆分可以把用户数据放到用户库、把订单数据放到订单库。垂直分表是对数据表进行垂直拆分的一种方式,常见的是把一个多字段的大表按常用字段和非常用字段进行拆分,每个表里面的数据记录数一般情况下是相同的,只是字段不一样,使用主键关联,比如原始的用户表是:

    垂直拆分后是:

    史上最全MySQL 大表优化方案(长文)

     

    垂直拆分的优点是:

    史上最全MySQL 大表优化方案(长文)

     

    1、可以使得行数据变小,一个数据块(Block)就能存放更多的数据,在查询时就会减少I/O次数(每次查询时读取的Block 就少)

    2、可以达到最大化利用Cache的目的,具体在垂直拆分的时候可以将不常变的字段放一起,将经常改变的放一起

    3、数据维护简单

    缺点是:

    1、主键出现冗余,需要管理冗余列

    2、会引起表连接JOIN操作(增加CPU开销)可以通过在业务服务器上进行join来减少数据库压力

    3、依然存在单表数据量过大的问题(需要水平拆分)

    事务处理复杂

    水平拆分

    概述

    水平拆分是通过某种策略将数据分片来存储,分库内分表和分库两部分,每片数据会分散到不同的MySQL表或库,达到分布式的效果,能够支持非常大的数据量。前面的表分区本质上也是一种特殊的库内分表

    库内分表,仅仅是单纯的解决了单一表数据过大的问题,由于没有把表的数据分布到不同的机器上,因此对于减轻MySQL服务器的压力来说,并没有太大的作用,大家还是竞争同一个物理机上的IO、CPU、网络,这个就要通过分库来解决

    前面垂直拆分的用户表如果进行水平拆分,结果是:

    史上最全MySQL 大表优化方案(长文)

     

    实际情况中往往会是垂直拆分和水平拆分的结合,即将Users_A_M和Users_N_Z再拆成Users和UserExtras,这样一共四张表

    水平拆分的优点是:

    不存在单库大数据和高并发的性能瓶颈

    应用端改造较少

    提高了系统的稳定性和负载能力

    缺点是:

    分片事务一致性难以解决

    跨节点Join性能差,逻辑复杂

    数据多次扩展难度跟维护量极大

    分片原则

    能不分就不分,参考单表优化

    分片数量尽量少,分片尽量均匀分布在多个数据结点上,因为一个查询SQL跨分片越多,则总体性能越差,虽然要好于所有数据在一个分片的结果,只在必要的时候进行扩容,增加分片数量

    分片规则需要慎重选择做好提前规划,分片规则的选择,需要考虑数据的增长模式,数据的访问模式,分片关联性问题,以及分片扩容问题,最近的分片策略为范围分片,枚举分片,一致性Hash分片,这几种分片都有利于扩容

    尽量不要在一个事务中的SQL跨越多个分片,分布式事务一直是个不好处理的问题

    查询条件尽量优化,尽量避免Select * 的方式,大量数据结果集下,会消耗大量带宽和CPU资源,查询尽量避免返回大量结果集,并且尽量为频繁使用的查询语句建立索引。

    通过数据冗余和表分区赖降低跨库Join的可能

    这里特别强调一下分片规则的选择问题,如果某个表的数据有明显的时间特征,比如订单、交易记录等,则他们通常比较合适用时间范围分片,因为具有时效性的数据,我们往往关注其近期的数据,查询条件中往往带有时间字段进行过滤,比较好的方案是,当前活跃的数据,采用跨度比较短的时间段进行分片,而历史性的数据,则采用比较长的跨度存储。

    总体上来说,分片的选择是取决于最频繁的查询SQL的条件,因为不带任何Where语句的查询SQL,会遍历所有的分片,性能相对最差,因此这种SQL越多,对系统的影响越大,所以我们要尽量避免这种SQL的产生。

    解决方案

    由于水平拆分牵涉的逻辑比较复杂,当前也有了不少比较成熟的解决方案。这些方案分为两大类:客户端架构和代理架构。

    客户端架构

    通过修改数据访问层,如JDBC、Data Source、MyBatis,通过配置来管理多个数据源,直连数据库,并在模块内完成数据的分片整合,一般以Jar包的方式呈现

    这是一个客户端架构的例子:

    史上最全MySQL 大表优化方案(长文)

     

    可以看到分片的实现是和应用服务器在一起的,通过修改Spring JDBC层来实现

    客户端架构的优点是:

    应用直连数据库,降低外围系统依赖所带来的宕机风险

    集成成本低,无需额外运维的组件

    缺点是:

    限于只能在数据库访问层上做文章,扩展性一般,对于比较复杂的系统可能会力不从心

    将分片逻辑的压力放在应用服务器上,造成额外风险

    代理架构

    通过独立的中间件来统一管理所有数据源和数据分片整合,后端数据库集群对前端应用程序透明,需要独立部署和运维代理组件

    这是一个代理架构的例子:

    史上最全MySQL 大表优化方案(长文)

     

    代理组件为了分流和防止单点,一般以集群形式存在,同时可能需要Zookeeper之类的服务组件来管理

    代理架构的优点是:

    能够处理非常复杂的需求,不受数据库访问层原来实现的限制,扩展性强

    对于应用服务器透明且没有增加任何额外负载

    缺点是:

    需部署和运维独立的代理中间件,成本高

    应用需经过代理来连接数据库,网络上多了一跳,性能有损失且有额外风险

    各方案比较

    如此多的方案,如何进行选择?可以按以下思路来考虑:

    史上最全MySQL 大表优化方案(长文)

     

     

    史上最全MySQL 大表优化方案(长文)

     

     

    史上最全MySQL 大表优化方案(长文)

     

    确定是使用代理架构还是客户端架构。中小型规模或是比较简单的场景倾向于选择客户端架构,复杂场景或大规模系统倾向选择代理架构

    具体功能是否满足,比如需要跨节点ORDER BY,那么支持该功能的优先考虑

    不考虑一年内没有更新的产品,说明开发停滞,甚至无人维护和技术支持

    最好按大公司->社区->小公司->个人这样的出品方顺序来选择

    选择口碑较好的,比如github星数、使用者数量质量和使用者反馈

    开源的优先,往往项目有特殊需求可能需要改动源代码

    按照上述思路,推荐以下选择:

    1、客户端架构:ShardingJDBC

    2、代理架构:MyCat或者Atlas

    兼容MySQL且可水平扩展的数据库

    目前也有一些开源数据库兼容MySQL协议,如:

    1、TiDB

    2、Cubrid

    但其工业品质和MySQL尚有差距,且需要较大的运维投入,如果想将原始的MySQL迁移到可水平扩展的新数据库中,可以考虑一些云数据库:

    阿里云PetaData

    阿里云OceanBase

    腾讯云DCDB

    NoSQL

    在MySQL上做Sharding是一种戴着镣铐的跳舞,事实上很多大表本身对MySQL这种RDBMS的需求并不大,并不要求ACID,可以考虑将这些表迁移到NoSQL,彻底解决水平扩展问题,例如:

    1、日志类、监控类、统计类数据

    2、非结构化或弱结构化数据

    3、对事务要求不强,且无太多关联操作的数据

    展开全文
  • sql优化问题(百万级数据优化方案)   一.sql数据库优化方案 1、索引 2、分库分表分区 3、数据库引擎  4、预处理  5、读写分离 1、索引,建立索引是数据库优化各种方案之中成本最低,见效最快的解决方案,...
  • 数据库SQL优化大总结之 百万级数据库优化方案

    万次阅读 多人点赞 2016-06-23 09:43:50
    网上关于SQL优化的教程很多,但是比较杂乱。近日有空整理了一下,写出来跟大家分享一下,其中有错误和不足的地方,还请大家纠正补充。 这篇文章我花费了大量的时间查找资料、修改、排版,希望大家阅读之后,感觉好的...
  • 大数据量中的模糊查询优化方案

    万次阅读 2016-09-19 22:21:10
    对工作单使用 like模糊查询时,实际上 数据库内部索引无法使用 ,需要逐条比较查询内容,效率比较低在数据量很多情况下, 提供模糊查询性能,我们可以使用lucene全文索引库技术。本文示例是在SSH框架中进行使用。...
  • 如何优化一个网站的完整方案-SEO

    万次阅读 2017-03-02 16:25:24
    一个完整的SEO优化方案主要由四个小组组成:(不要以为一个人就可以完事,当然你会编程更有优势)一、前端人员二、内容编辑人员三、推广人员四、数据分析人员首先,前端/页编人员主要负责站内优化,主要从四个方面...
  • 多请求多数据量综合页面优化方案

    千次阅读 2018-01-25 19:24:06
    关于页面优化一直是一个让...之前了一个综合展示页面,这个页面布局就想一个桌子,上面摆满了,水果,青菜,大鱼大肉,还有碗筷刀叉,总之东西很多,所以页面显示很慢。打开页面会有10s左右的卡死状态。老大让我解决
  • mysql 百万级数据库优化方案

    千次阅读 2018-06-21 14:26:45
    https://blog.csdn.net/Kaitiren/article/details/80307828一、百万级数据库优化方案1.对查询进行优化,要尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。2.应尽量避免在 where 子句中对...
  • MySQL大表优化方案

    万次阅读 多人点赞 2020-11-02 13:51:07
    方案概述 一、数据库设计及索引优化 MySQL数据库本身高度灵活,造成性能不足,严重依赖开发人员的表设计能力以及索引优化能力,在这里给几点优化建议 时间类型转化为时间戳格式,用int类型储存,建索引增加查询效率 ...
  • 一个网站完整的SEO优化方案

    万次阅读 2018-10-18 23:50:19
    一个完整的SEO优化方案主要由四个小组组成: 一、前端/页编人员 二、内容编辑人员 三、推广人员 四、数据分析人员 接下来,我们就对这四个小组分配工作。 首先,前端/页编人员主要负责站内优化,主要从四个方面入手...
  • 网站SEO优化方案 全面详细的写法

    万次阅读 2018-02-26 13:46:27
    不管是我们为自己的网站SEO优化,还是公司网站的SEO优化,又或者是给客户提供SEO服务,小编都希望大家能在SEO工作开始之前做好一份详细的SEO方案,不要怕费时间,只有有计划地去工作,才能让效率大大地提高,...
  • 浅谈移动端页面性能优化方案

    千次阅读 2019-06-11 12:04:29
    这就要求我们产品质量越来越高,那对于我们前端工程师来说也是一个挑战,如何让我们所开发的页面能有更好的体验,就是我们今天讨论的话题:移动端页面性能优化。 Html5的出现对于移动端影响挺大,HTML5框架可以提升...
  • H5性能优化方案

    千次阅读 2016-08-17 18:06:07
    原本H5的渲染性能就不及native的app,如果不把性能优化做起来,将极大地影响用户使用产品的积极性。用户感受当用户能够在1-2秒内打开H5页面,看到信息的展示,或者能够开始进行下一步的操作,用户会感觉速度还好,...
  • postgreSQL单表数据量上千万分页查询缓慢的优化方案

    万次阅读 多人点赞 2019-12-26 17:03:08
    postgreSQL单表数据量上千万分页查询缓慢的优化方案
  • SEO整站优化方案制作

    千次阅读 2018-08-15 13:53:41
    SEO整站优化方案制作 不管是我们为自己的网站SEO还是给自己公司的网站优化,又或者是给客户提供SEO服务,在SEO工作开始之前做好一份详细的SEO方案,不要怕费时间,只有有计划地去工作,才能让效率大大地提高。 ...
  • 数据库SQL优化大总结1之- 百万级数据库优化方案

    万次阅读 多人点赞 2017-06-06 09:55:51
    一、百万级数据库优化方案 1.对查询进行优化,要尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。 2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而...
  • Vue.js 应用性能优化,给你专业的分析+解决方案

    千次阅读 多人点赞 2021-10-24 16:19:32
    为什么我们需要 Vue JS 性能优化? Vue 性能不佳背后的主要原因 如何检查您的 VueJS 应用的大小? 如何优化 Vue js 应用程序的性能? 1. 在 Vue js 中懒加载 2. 基于路线的代码拆分 3.Vue js预加载组件 4. ...
  • 页面性能优化的几个方面页面性能的问题对用户体验的影响非常大,而现在移动端流量的提升给页面性能优化带来了更多的挑战,因为相同的页面从pc端移到手机上后,性能的问题都会被更明显的感受到。要知道如何优化页面...
  • 关于Android发展至今,在各项功能十分成熟的情况下,我们越来越重视App的性能优化,以及用户体验,这关乎一个线上应用的DAU持续增长的基础,以及用户口碑的问题,今天刘某人带大家来一起分析一下崩溃/卡顿/ANR/OOM/...
  • 启英泰伦产品方案开发流程

    千次阅读 2022-02-14 14:03:43
    语音识别作为最自然的交互方式,越来越多的被用户所接受,但是语音方案与传统的逻辑开发不同,需要多方配合才能打造好的产品。本文档主要介绍启英泰伦的语音识别方案方案设计和项目开发,适用于新接触语音的成品...
  • 一个完整的SEO优化方案主要由四个小组组成: 一、前端/页编人员 二、内容编辑人员 三、推广人员 四、数据分析人员 接下来,我们就对这四个小组分配工作。 首先,前端/页编人员主要负责站内优化,主要从四个...
  • ListView优化方案

    千次阅读 2015-03-10 13:26:23
    一、复用convertView,减少findViewById的次数1、优化一:复用convertViewAndroid系统本身为我们考虑了ListView的优化问题,在复写的Adapter的类中,比较重要的两个方法是getCount()和getView()。界面上有多少个条...
  • Web交互设计优化方案+check list

    千次阅读 2017-08-03 09:58:52
    优化已有产品的体验”这是用户体验相关岗位职责中常见的描述。我们的产品常常是在快速的迭代过程中不断完善的,就像孩子生下来需要养育才能长大一样,优化已有功能/产品,和设计新功能/产品同样重要,不可偏废。 ...
  •  祥云平台上的完整SEO优化解决方案包含四个主要组件:  第一:网站前端设计师  第二:网站内容录入人员  第三:网站优化和推广人员  第四:背景数据分析师  接下来,我们将为这四个部门分配工作。  ...
  • 优化很笼统的词汇,这说明它包含的信息量很大,要处理的事情很多。 ...3、高并发方案 一个一个来,先说 代码优化 代码优化主要对代码结构层次的优化,目的就是更加方便代码的可维护性与可读.
  • 总结了一份关于SSD的优化方案,让新买的SSD速度不会慢。 1. 选择带有SATA3(6Gbps)原生接口的主板 SSD硬盘对主板的接口类型有要求,必须得用SATA3(6Gbps)接口才能发挥出它的性能来。不过现在基本上所有...
  • 一、Kafka优化总结 翻译原文如下: https://www.infoq.com/articles/apache-kafka-best-practices-to-optimize-your-deployment 1. 设置日志配置参数以使日志易于管理 kafka 日志文档...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 292,183
精华内容 116,873
关键字:

如何做产品优化方案