精华内容
下载资源
问答
  • 由“感知级”到“经验级”,再到“可预测级”、“持续优化级”这个过程是不可跨越的,不同级别的“用数据管理过程”,都是由商业目标驱动的,只是不同级别所达到的量化管理程度不一样。那为什么要“用数据管理过程”...

    摘要:

    用数据说话,这是当前很流行的话题,本文将数据管理过程划分成4个层次,并阐述企业如何达到这四个层次。

    1.初级量化管理:以数据“感知”项目的状况(相当于CMMI2级)

    2.中级量化管理:通过经验值来管理项目(相当于CMMI3级)

    3.高级量化管理:用PCB进行项目管理(相当于CMMI4级)

    4.超级量化管理:持续优化的量化管理(相当于CMMI5级)

    我将通过4篇文章为大家分享!

     

     

    6. 超级量化管理-持续优化级

     

    高级量化管理主要讲述的是如何把不稳定的过程变成稳定的过程,而超级量化管理主要讲述的是把有能力的过程变成更加有能力的过程。

    前面提到的CPI中值和上下限,有可能不满足商业目标的需要,如CPI平均值达不是所期望的值,上下限的范围太宽,这样就有必要想办法做一些改进,提高能力,并且能力提高后的过程同样也是稳定的过程。

     

    那么有什么办法可以提高能力呢?

    1) 改进过程。过程的性能基线的中值及上下限,是由公共原因所引至的,要提高其性能,必须从这些公共原因入手,对信号进行分析,想办法进一步削弱公共原因引起的偏差,想办法改造过程,使中值符合要求。

    2) 采用新技术。考虑引入新的技术,并调整相应的过程,来提高过程的性能。

    3) 对噪音进行原因分析。噪音是由公共原因和可归属原因共同作用引起的,对每个噪音的可归属原因进行详细的分析,将很可能找到改进的机会。

    分析过程的可改进地方是比较复杂的,有可能需要对过程进行更深一步的细分,可能会发现原来的数据分组达不到要求,需要增强粒度,以便区分出更多的公共原因,找出可改进点。另外,原来不需要进行基线级别管理的过程,可能也会因为商业目标的需要,需要建立基线并进行基线级别的管理。

    在这个级别上用数据管理过程意义在于,通过数据来监控过程的改进效果,比较能力的变化,为决策提供依据。当形成新的更有能力的能力基线时,企业将用新的能力基线来管理项目。

    SPC的建立及优化过程的成本是很高的,但进行SPC管理的过程的成功概率是非常高的,企业根据自己的商业目标需要,选择需要进行SPC管理的过程,并不断优化,使企业具备越来越强的竞争力,而这种竞争力是别的企业难以模仿并难以超越的。

     

     

    7. 总结

     

    由“感知级”到“经验级”,再到“可预测级”、“持续优化级”这个过程是不可跨越的,不同级别的“用数据管理过程”,都是由商业目标驱动的,只是不同级别所达到的量化管理程度不一样。

    “感知级”通过软件度量,大概了解项目的状况,并作为工作调整的依据。

    “经验级”通过软件度量,对比项目的历史经验数据,把握项目的状况,并进行相应的工作调整。同时,项目的历史经验数据,可供估算等工作进行参考。

    “可预测级”,把“经验级”推向一个更高的高度,对影响问题的因素进行详细的分析,排除和削弱影响项目性能的各种因素,对历史经验数据进行合理分组,统计出性能基线,并用于项目管理。用基线来管理的过程都是稳定的过程,这些过程从统计角度来说都可以准确地预测出将来的结果。

    “持续优化级”是“数据管理过程”的最高级别,达到这个级别意味着企业能根据商业目标持续的优化SPC管理,使企业形成别的企业难以模仿并难以超越的核心竞争力。

     

    那是不是越高级越好呢?企业是不是都应该追求更高级的量化管理呢?

    答案是否定的。

    要实施“可预测级”的量化管理,是有条件限制的,就是过程要稳定,稳定就意味着项目间要有可比性,采用的技术、过程要大体一致。如果一个企业是创新型的企业,经常用新技术做新项目,这样项目的可比性就比较弱,就很难具备稳定的基础条件,很难形成基线。

    所以,不能说CMMI级别越高的企业,就一定比级别低的企业管理要更好一点,有时候企业的特点就决定了企业不可能做到45级。各企业的最高领导,关键是清楚理解自己的商业目标,理解高级别的量化管理能带来什么帮助。

     

    那为什么要“用数据管理过程”呢?

    如果有一个非量化管理的办法,成本更低,并且能更有效地达到目的,那我们当然就采用那个办法,而不会片面地追求“用数据管理过程”了。所有想“用数据管理过程”的人士,都应该认真思考这个问题,并考虑哪种层次的量化管理级别适合您的商业目标。

     

     

    全文完

     

    作者:张传波

    创新工场创业课堂讲师

    软件研发管理资深顾问

    CMMI首席专家

    《火球——UML大战需求分析》作者

    www.umlonline.org 创始人

     

    展开全文
  • oracle 优化重量

    2015-03-31 20:00:07
    oracle 优化重量指南 专家讲座
  • 一对一映射 hibernate优化配置一缓存步骤 有图有真相
  • RecyclerView性能优化及高级使用

    万次阅读 多人点赞 2019-03-13 13:49:55
    最近研究应用流畅度专题时,发现RecyclerView里边的坑真多,有很多可以优化的点,在理解优化点之前,最好对RecyclerView的缓存机制有一些了解,比如得知道CacheView和RecycledViewPool的区别和联系,RecyclerView的...

    最近研究应用流畅度专题时,发现RecyclerView里边的坑真多,有很多可以优化的点,在理解优化点之前,最好对RecyclerView的缓存机制有一些了解,比如得知道CacheView和RecycledViewPool的区别和联系,RecyclerView的绘制流程有一定了解,再来谈RecyclerView的性能提升。缓存机制可以看看这篇文章:基于滑动场景解析RecyclerView的回收复用机制原理

    还有一篇外国人写的,ViewHolder的探究,这篇文章把RecyclerView的各级缓存作用剖析得很清晰,以前看过很多人写的文章,感觉都是一知半解,总结下:

    1、RecyclerView缓存

    1.1 RecyclerView主要有三级缓存:

    (1)Attached scrap & Changed scrap

    ArrayList<ViewHolder> mAttachedScrap 

    主要用在插入或是删除itemView时,先把屏幕内的ViewHolder保存至AttachedScrap中,作用在LayoutManager中,它仅仅把需要从ViewGroup中移除的子view设置它的父view为null,从而实现了从RecyclerView中移除操作detachView()。需要新插入的view从cacheView/Pool中找,没找到则createViewHolder。而从ViewGroup中移除的子view会放到Pool缓存池中,如下图中的itemView b。

    ArrayList<ViewHolder> mChangedScrap :
    

    主要用到刷新屏幕上的itemView数据,它不需要重新layout,notifyItemChanged()或者notifyItemRangeChanged()
    (2) cache Views :保存最近移出屏幕的ViewHolder,包含数据和position信息,复用时必须是相同位置的ViewHolder才能复用,应用场景在那些需要来回滑动的列表中,当往回滑动时,能直接复用ViewHolder数据,不需要重新bindView。用一个数组保存ViewHolder,实现是:ArrayList<ViewHolder> mCachedViews 

    (3) RecyclerViewPool :缓存池,当cacheView满了后,将cacheView中移出的ViewHolder放到Pool中,放之前会把ViewHolder数据清除掉,所以复用时需要重新bindView。实现是: SparseArray<ArrayList<ViewHolder>> mScrap;//按viewType来保存ViewHolder,每种类型最大缓存个数默认为5

    1.2 RecyclerView缓存过程:

    在滑动过程中,会先滑动的itemView保存到CacheView中,CacheView大小默认是2,如果超过了最大容量,则按FIFO,将队列头部的itemView出队,保存至缓存池RecyclerViewPool中,缓存池是按itemView的类型itemType来保存的,每种itemType默认缓存个数是5,超过了,则直接由GC回收。具体表现如下图:

    可以看到CacheView缓存中蓝色的块一直最最近两个,而RecycledViewPool中,保存最大是5,超过5了后ViewHolder都被回收。

    1.3 RecyclerView缓存寻找过程:

    RecyclerView在找到可用ViewHodler的顺序是:如果在缓存CacheViews中找到,则直接复用;如果在缓存池RecycerViewPool找到,则需要bindView;如果没有找到可用的ViewHolder,则需要create新建一个ViewHolder,并bindView绑定view。

    1.4 调用notifyDataSetChanged过程:

    如果调用notifyDataSetChanged,每个itemView没有稳定的id的话,RecyclerView不知道接下来会发生什么,也不知道哪些改变,它假设所有都改变了,会将每一个ViewHolder设置成无效并且放到缓存池Pool中,如果我们仅是把屏幕上的第四条itemView移到第六条的位置,屏幕上所有itemView都会重新layout一遍,这样只能从缓存池RecycledViewPool池中取缓存的ViewHolder,如果不够时,需要重新create ViewHolder.具体实现如下:

    如果设置了Stable Ids,即每一个itemView都有一个唯一的id来标识,通过getItemId()来获取这个唯一标识id,当然我们不能用position来标识,因为itemView会复用,位置会乱序。当调用notifyDataSetChanged()方法时,ViewHolder会进入上面的一级缓存mAttachedScrap中,而不是进入缓存池pool中,这样的好处:1)不会存在缓存池pool满的问题,不需要重新createViewHolder; 2) 不需要重新bindView了。

    下面说说RecyclerView的一些优化方案和使用技巧:

    1、recyclerView.setHasFixedSize(true);

    当Item的高度如是固定的,设置这个属性为true可以提高性能,尤其是当RecyclerView有条目插入、删除时性能提升更明显。RecyclerView在条目数量改变,会重新测量、布局各个item,如果设置了setHasFixedSize(true),由于item的宽高都是固定的,adapter的内容改变时,RecyclerView不会整个布局都重绘。具体可用以下伪代码表示:

    void onItemsInsertedOrRemoved() {
       if (hasFixedSize) layoutChildren();
       else requestLayout();
    }

     

    2、使用getExtraLayoutSpace为LayoutManager设置更多的预留空间

    在RecyclerView的元素比较高,一屏只能显示一个元素的时候,第一次滑动到第二个元素会卡顿。  

    RecyclerView (以及其他基于adapter的view,比如ListView、GridView等)使用了缓存机制重用子 view(即系统只将屏幕可见范围之内的元素保存在内存中,在滚动的时候不断的重用这些内存中已经存在的view,而不是新建view)。

    这个机制会导致一个问题,启动应用之后,在屏幕可见范围内,如果只有一张卡片可见,当滚动的时 候,RecyclerView找不到可以重用的view了,它将创建一个新的,因此在滑动到第二个feed的时候就会有一定的延时,但是第二个feed之 后的滚动是流畅的,因为这个时候RecyclerView已经有能重用的view了。

    如何解决这个问题呢,其实只需重写getExtraLayoutSpace()方法。根据官方文档的描述 getExtraLayoutSpace将返回LayoutManager应该预留的额外空间(显示范围之外,应该额外缓存的空间)。

    LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this) {
        @Override
        protected int getExtraLayoutSpace(RecyclerView.State state) {
            return 300;
        }
    };

    3、RecyclerView 数据预取

    android sdk>=21时,支持渲染(Render)线程,RecyclerView数据显示分两个阶段:

    1)在UI线程,处理输入事件、动画、布局、记录绘图操作,每一个条目在进入屏幕显示前都会被创建和绑定view;

    2)渲染(Render)线程把指令送往GPU。

    数据预取的思想就是:将闲置的UI线程利用起来,提前加载计算下一帧的Frame Buffer

    在新的条目进入视野前,会花大量时间来创建和绑定view,而在前一帧却可能很快完成了这些操作,导致前一帧的UI线程有一大片空闲时间。RecyclerView开发工程师将创建和绑定移到前一帧,使UI线程与渲染线程同时工作,在一个条目即将进入视野时预取数据。具体如下图,在前一帧的红色虚线圈中,UI线程有一定的空闲时间,可以把第二帧Create B的工作移到前一帧的空闲时间来完成。

    具体实现方式是:在 RecyclerView 开始一个滚动时new Runnable对象,根据 layout manager 和滚动的方向预取即将进入视野的条目,可以同时取出一个或多个条目,例如在使用 GridLayoutManager 时新的一行马上要出现的时候。在 25.1 版本中,预取操作被分为单独的创建/绑定操作,比对整组条目操作更容易被纳入 UI 线程的空隙中。具体实现原理可参考:RecyclerView预加载机制源码分析

    完成这些工作基本上没有任何代价,因为 UI 线程在两帧之间的空隙不做任何工作。我们可以使用这些空闲时间来完成将来的工作,并使得未来的帧出现得更快,

    如果使用 RecyclerView 提供的LayoutManager,自动使用了这种优化操作。如果使用嵌套 RecyclerView 或者自己实现Layout Manager,则需要在代码中设置。

    1)对于嵌套 RecyclerView,要获取最佳的性能,在内部的 LayoutManager 中调用 LinearLayoutManager.setInitialItemPrefetchCount()方法(25.1版本起可用)。

    例如:如果竖直方向的list至少展示三个条目,调用 setInitialItemPrefetchCount(4)。

    2)如果自己实现了LayoutManager,需要重写 LayoutManager.collectAdjacentPrefetchPositions()方法。该方法在数据预取开启时被 RecyclerView 调用(LayoutManager 的默认实现什么都不做)。在嵌套的内层 RecyclerView 中,如果想让LayoutManager 预取数据,同样应当实现 LayoutManager.collectInitialPrefetchPositions()

    4、避免创建过多对象

    onCreateViewHolder 和 onBindViewHolder 对时间都比较敏感,尽量避免繁琐的操作和循环创建对象。例如创建 OnClickListener,可以全局创建一个。同时onBindViewHolder调用次数会多于onCreateViewHolder的次数,如从RecyclerViewPool缓存池中取到的View都需要重新bindView,所以我们可以把监听放到CreateView中进行。

    优化前:

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        holder.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View v) {
             //do something
           }
        });
    }

    优化后:

    private class XXXHolder extends RecyclerView.ViewHolder {
            private EditText mEt;
            EditHolder(View itemView) {
                super(itemView);
                mEt = (EditText) itemView;
                mEt.setOnClickListener(mOnClickListener);
            }
        }
        private View.OnClickListener mOnClickListener = new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //do something
            }
        }

    5、局部刷新

    可以用一下一些方法,替代notifyDataSetChanged,达到局部刷新的目的。notifyDataSetChanged会触发所有item的detached回调再触发onAttached回调。

    notifyItemChanged(int position)
    notifyItemInserted(int position)
    notifyItemRemoved(int position)
    notifyItemMoved(int fromPosition, int toPosition) 
    notifyItemRangeChanged(int positionStart, int itemCount)
    notifyItemRangeInserted(int positionStart, int itemCount) 
    notifyItemRangeRemoved(int positionStart, int itemCount) 
    如果必须用 notifyDataSetChanged(),那么最好设置 mAdapter.setHasStableIds(true)

    6、重写onScroll事件

    对于大量图片的RecyclerView,滑动暂停后再加载;RecyclerView中存在几种绘制复杂,占用内存高的楼层类型,但是用户只是快速滑动到底部,并没有必要绘制计算这几种复杂类型,所以也可以考虑对滑动速度,滑动状态进行判断,满足条件后再加载这几种复杂的。

    7、RecyclerView缓存

    7.1 setItemViewCacheSize(int )

    RecyclerView可以设置自己所需要的ViewHolder缓存数量,默认大小是2。cacheViews中的缓存只能position相同才可得用,且不会重新bindView,CacheViews满了后移除到RecyclerPool中,并重置ViewHolder,如果对于可能来回滑动的RecyclerView,把CacheViews的缓存数量设置大一些,可以减少bindView的时间,加快布局显示。

    注:此方法是拿空间换时间,要充分考虑应用内存问题,根据应用实际使用情况设置大小。

    网上大部分设置CacheView大小时都会带上:

    setDrawingCacheEnabled(true)和setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH)

    setDrawingCacheEnabled这个是View本身的方法,意途是开启缓存。通过setDrawingCacheEnabled把cache打开,再调用getDrawingCache就可以获得view的cache图片,如果cache没有建立,系统会自动调用buildDrawingCache方法来生成cache。一般截图会用到,这里的设置drawingcache,可能是在重绘时不需要重新计算bitmap的宽高等,能加快dispatchDraw的速度,但开启drawingcache,肯定也会耗应用的内存,所以也慎用。

    7.2 复用RecycledViewPool

    在TabLayout+ViewPager+RecyclerView的场景中,当多个RecyclerView有相同的item布局结构时,多个RecyclerView共用一个RecycledViewPool可以避免创建ViewHolder的开销,避免GC。RecycledViewPool对象可通过RecyclerView对象获取,也可以自己实现。

    RecycledViewPool mPool = mRecyclerView1.getRecycledViewPool();
    下一个RecyclerView可直接进行setRecycledViewPool

    mRecyclerView2.setRecycledViewPool(mPool);

    mRecyclerView3.setRecycledViewPool(mPool);

    注意:

    (1)RecycledViewPool是依据ItemViewType来索引ViewHolder的,必须确保共享的RecyclerView的Adapter是同一个,或view type 是不会冲突的。

    (2)RecycledViewPool可以自主控制需要缓存的ViewHolder数量,每种type的默认容量是5,可通过setMaxRecycledViews来设置大小。mPool.setMaxRecycledViews(itemViewType, number); 但这会增大应用内存开销,所以也需要根据应用具体情况来使用。

    (3)利用此特性一般建议设置layout.setRecycleChildrenOnDetach(true);此属性是用来告诉LayoutManager从RecyclerView分离时,是否要回收所有的item,如果项目中复用RecycledViewPool时,开启该功能会更好的实现复用。其他RecyclerView可以复用这些回收的item。

    什么时候LayoutManager会从RecyclerView上分离呢,有两种情况:1)重新setLayoutManager()时,比如淘宝页面查看商品列表,可以线性查看,也可以表格形式查看,2)还有一种是RecyclerView从视图树上被remove时。但第一种情况,RecyclerView内部做了回收工作,设不设置影响不大,设置此属性作用主要针对第二种情况。

    8、RecyclerView中的一些方法

    onViewRecycled():当 ViewHolder 已经确认被回收,且要放进 RecyclerViewPool 中前,该方法会被回调。移出屏幕的ViewHolder会先进入第一级缓存ViewCache中,当第一级缓存空间已满时,会考虑将一级缓存中已有的ViewHolder移到RecyclerViewPool中去。在这个方法中可以考虑图片回收。

    onViewAttachedFromWindow(): RecyclerView的item进入屏幕时回调
    onViewDetachedFromWindow():RecyclerView的item移出屏幕时回调

    onAttachedToRecyclerView() :当 RecyclerView 调用了 setAdapter() 时会触发,新的 adapter 回调 onAttached。
    onDetachedFromRecyclerView():当 RecyclerView 调用了 setAdapter() 时会触发,旧的 adapter 回调 onDetached

    setHasStableIds()/getItemId():setHasStableIds用来标识每一个itemView是否需要一个唯一标识,当stableId设置为true的时候,每一个itemView数据就有一个唯一标识。getItemId()返回代表这个ViewHolder的唯一标识,如果没有设置stableId唯一性,返回NO_ID=-1。通过setHasStableIds可以使itemView的焦点固定,从而解决RecyclerView的notify方法使得图片加载时闪烁问题。注意:setHasStableIds()必须在 setAdapter() 方法之前调用,否则会抛异常。因为RecyclerView.setAdapter后就设置了观察者,设置了观察者stateIds就不能变了。具体案例可参考:RecyclerView notifyDataSetChanged 导致图片闪烁的真凶

     

    9、更多高级用法

    9.1 SnapHelper实现卡片效果或ViewPager效果

    SnapHelper是一个抽象类,Google 内置了两个默认实现类,LinearSnapHelperPagerSnapHelper

    1)LinearSnapHelper可以使RecyclerView 的当前Item 居中显示(横向和竖向都支持)

    2)PagerSnapHelper使RecyclerView 像ViewPager一样的效果,每次只能滑动一页(LinearSnapHelper支持快速滑动), PagerSnapHelper也是Item居中对齐。

    使用方法如下,想了解更多可参考Android中使用RecyclerView + SnapHelper实现类似ViewPager效果

     LinearLayoutManager manager = new LinearLayoutManager(getContext());
     manager.setOrientation(LinearLayoutManager.VERTICAL);
     mRecyclerView.setLayoutManager(manager);
    // 将SnapHelper attach 到RecyclrView
     LinearSnapHelper snapHelper = new LinearSnapHelper();
     snapHelper.attachToRecyclerView(mRecyclerView);

    9.2 用SortedList实现添加删除ItemView自动更新

    我们在给RecyclerView的ArrayList<Item> data添加一个Data数据时,一般需要自己通知RecyclerView更新,尤其是遇到去重操作,还需要遍历一次data,定位后再决定是插入还是更新现有数据,调用notifyItemInserted(pos),Android Support Lirbrary中提供了一个SortedList工具类,它是一个有序列表,数据变动时会回调SortedList.Callback中方法。

    具体使用:

    class SortedListAdapter extends RecyclerView.Adapter<TodoViewHolder> {
        final SortedList<Item> mData;
        final LayoutInflater mLayoutInflater;
        public SortedListAdapter(Context context) {
            mLayoutInflater = LayoutInflater.from(context);
            mData = new SortedList<Item>(Item.class, new SortedListAdapterCallback<Item>(this){
                @Override
                public int compare(Item t0, Item t1) {
                    // 实现这个方法来定义Item的显示顺序
                    int txtComp = t0.mText.compareTo(t1.mText);
                    if (txtComp != 0) {
                        return txtComp;
                    }
                    if (t0.id < t1.id) {
                        return -1;
                    } else if (t0.id > t1.id) {
                        return 1;
                    }
                    return 0;
                }
                @Override
                public boolean areContentsTheSame(Item oldItem,
                        Item newItem) {
                    // 比较两个Item的内容是否一致,如不一致则会调用adapter的notifyItemChanged()
                    return oldItem.mText.equals(newItem.mText);
                }
                @Override
                public boolean areItemsTheSame(Item item1, Item item2) {
                    // 两个Item是不是同一个东西,
                    // 它们的内容或许不一样,但id相同代表就是同一个
                    return item1.id == item2.id;
                }
            });
        }
        public void addItem(Item item) {
            mData.add(item);
            // 会通过SortedListAdapterCallback自动通知更新
        }
        ...
        @Override
        public int getItemCount() {
            return mData.size();
        }
    }

    当数据发生改变时,例如删除,增加等,只需直接对mDataList进行相应操作,无需关心mAdapter内数据显示更新问题,不用再调用notifyDataChanged等函数,因为SortedListAdapterCallback内的回调函数自动完成了。

    9.3 详解7.0带来的新工具类:DiffUtil

    DiffUtil是support-v7:24.2.0中的新工具类,它用来比较两个数据集,寻找出旧数据集—>新数据集的最小变化量,它和mAdapter.notifyDataSetChanged()最大不同在于它会自动计算新老数据集的差异,并根据差异情况,自动调用以下四个方法:

    adapter.notifyItemRangeInserted(position, count);
    adapter.notifyItemRangeRemoved(position, count);
    adapter.notifyItemMoved(fromPosition, toPosition);
    adapter.notifyItemRangeChanged(position, count, payload);

    且调用notifyDataSetChanged()不会触发RecyclerView的动画(删除、新增、位移、change动画),其次性能较低,它不管数据是否一样都整个刷新了一遍整个RecyclerView 。

    具体使用方法:

    DiffUtil.Callback抽象类如下:

        public abstract static class Callback {
            public abstract int getOldListSize();//老数据集size
    
            public abstract int getNewListSize();//新数据集size
    
            //新老数据集在同一个position的Item是否是一个对象,如果给itemView设置了stableIds,则仅比较它们单独的id(可能内容不同,如果这里返回true,会调用下面的方法)
            public abstract boolean areItemsTheSame(int oldItemPosition, int newItemPosition);
    
            //这个方法仅仅是上面方法返回true才会调用,判断item的内容是否有变化,类似于Object.equals(Object)
            public abstract boolean areContentsTheSame(int oldItemPosition, int newItemPosition);
    
            //当areItemsTheSame()返回true且areContentsTheSame()返回false,用下面的方法找出两个itemView的data不同之处
            @Nullable
            public Object getChangePayload(int oldItemPosition, int newItemPosition) {
                return null;
            }
        }

    使用时需要实现Callback接口,再将差异结果赋值到我们写的Adapter上。

    DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new ProductListDiffCallback(mProducts, newProducts));
    diffResult.dispatchUpdatesTo(mProductAdapter);

    有一篇外国文章介绍的也很好:DiffUtil使用介绍

    9.4 NestedScrollView嵌套RecyclerView

    1) 滑动lRecyclerView列表会出现强烈的卡顿感

    mRecyclerView.setNestedScrollingEnabled(false);//RecyclerView默认是setNestedScrollingEnabled(true),是支持嵌套滚动的,也就是说当它嵌套在NestedScrollView中时,默认会随着NestedScrollView滚动而滚动,放弃了自己的滚动。将该值置false可以让RecyclerView不支持嵌套滑动,这样RecyclerView可以自己响应滑动事件。

    2)每次打开界面都是定位在RecyclerView在屏幕顶端,列表上面的布局都被顶上去了

    RecyclerView抢占了焦点,自动滚动导致的.

    RecyclerView会在构造方法中调用setFocusableInTouchMode(true), 抢占焦点后一定会定位到第一行的位置,可以在NestedScrollView中添加属性:android:focusableInTouchMode="true",同时在RecyclerView中添加属性:android:descendantFocusability="blocksDescendants"或直接设置mRecyclerVIew.setFocusableInTouchMode(false)

    10、别人遇到的问题

    10.1 由于RecyclerView缓存view复用导致图片错乱

    Recyclerview的缓存机制,作者主要在对RecyclerView的ItemView某些图片进行了属性动画变换,这样就改变了ViewHolder中ImageView的属性,在滑动时,RecyclerView的缓存复用机制可能导致ViewHolder不会重新创建,也不会重新bindView,这样某些ItemView的图片是View属性动画变换后的图片,导致不是自己想要的结果。

    10.2 由于RecyclerView关联的GapWorker导致内存泄漏

    RecyclerView导致内存泄漏问题分析,其实主要是RecyclerView关联的GapWorker中有一个静态的ThreadLocal对象,静态属性生命周期和应用进程生命周期一致,发生内存泄漏肯定是因为GapWorker的引用链一直关联到Activity中,且没有在相应的时候释放这条引用链。按道理RecyclerView内部onAttachedToWindow和onDetachedFromWindow分别进行了引用和释放引用,是不会发生内存泄漏的,但是由于开发者应对的环境不一样,遇到的坑也不一样。作者这种分析办法还是很值得学习。

    后记:

    RecyclerView的优化点肯定还有很多,坑也还有很多,这和应用的实际使用情况有很大关系。同时Google开发工程师也一直在优化RecyclerView,我们也要一直学习着。

    展开全文
  • 指令级优化及工具介绍 What is Performance Analysis? Performance Analysis = 在特定平台上,评估平台能够提供的能力与现实的差距 定位以及化解Performance Bottlenecks
  • 随着嵌人式系统的快速发展,软件功耗问题显得越来越重要,应该将“省电”作为软件优化的一项技术指标,这样对软件优化的*价体系才算完整。值得注意的是,大多数情况下性能和功耗并不矛盾,减少程序执行时间同样会使...
  • oracle数据库的毫秒级优化技巧,
  • 专业系统优化诺德尔系统内核级优化软件V5适用网吧终极优化
  • 百万数据库优化方案
  • 介绍了sql 百万数据库优化方案,有需要的朋友可以参考一下
  • 本文深入浅出的分析了如何给千万mysql快速分页做优化,非常的实用,是篇不可多得的文章,程序员必读!!
  • 系统内核级优化 V3

    2010-03-01 22:10:40
    xp系统内核级优化 用着不错 介绍给大家
  • 数据库面试题索引sql优化.pdf+数据库SQL优化总结之百万数据库优化.pdf 附赠Oracle高性能sql优化
  • 千万大表如何优化,这是一个很有技术含量的问题,通常我们的直觉思维都会跳转到拆分或者数据分区,在此我想做一些补充和梳理,想和大家做一些这方面的经验总结,也欢迎大家提出建议。从一开始脑海里开始也是火光四...
  •  中兴通讯的终端级优化与运营解决方案正是面向这一新变化而推出的。通过信令层级的分析,在海量数据中提取运营商原本无法获取的网络中终端类型分布情况、不同终端类型业务选择和客户感受情况。运用这一解决方案,...
  • 采用一cache设计对五流水线CPU进行优化,Cache的工作原理是基于程序访问的局部性。根据程序的局部性原理,可以在主存和CPU通用寄存器之间设置一个高速的容量相对较小的存储器,把正在执行的指令地址附近的一部分...
  • MySQL 百万分页优化(Mysql千万快速分页),主要解决大数量优化
  • 主要介绍了Mysql百万分页优化技巧,包括普通分页和优化分页两种,在数据量比较大的时候,我们尽量去利用索引来优化语句。下面通过本文给大家详细讲解,一起看看吧
  • Zend_企业应用与优化Zend_企业应用与优化
  • recyclerview优化以及二菜单展开,recyclerview优化以及二菜单展开,recyclerview优化以及二菜单展开
  • 用CFD方法优化设计ISO5洁净室rar,用CFD方法优化设计ISO5洁净室
  • mysql千万数据量根据索引优化查询速度

    万次阅读 多人点赞 2016-08-15 18:05:16
    (一)索引的作用 ...提升查询速度的方向一是提升硬件(内存、cpu、硬盘),二是在软件上优化(加索引、优化sql;优化sql不在本文阐述范围之内)。 能在软件上解决的,就不在硬件上解决,毕竟硬件提升...

    (一)索引的作用

    索引通俗来讲就相当于书的目录,当我们根据条件查询的时候,没有索引,便需要全表扫描,数据量少还可以,一旦数据量超过百万甚至千万,一条查询sql执行往往需要几十秒甚至更多,5秒以上就已经让人难以忍受了。

    能在软件上解决的,就不在硬件上解决,毕竟硬件提升代码昂贵,性价比太低。代价小且行之有效的解决方法就是合理的加索引。索引使用得当,能使查询速度提升上千倍,效果惊人。

    (二)mysql的索引类型:

    mysql的索引有5种:主键索引、普通索引、唯一索引、全文索引、聚合索引(多列索引)。

    唯一索引和全文索引用的很少,我们主要关注主键索引、普通索引和聚合索引。

    1)主键索引:主键索引是加在主键上的索引,设置主键(primary key)的时候,mysql会自动创建主键索引;

    2)普通索引:创建在非主键列上的索引;

    3)聚合索引:创建在多列上的索引。

    (三)索引的语法:

    查看某张表的索引:show index from 表名;

    创建普通索引:alter table 表名 add index  索引名 (加索引的列) 

    创建聚合索引:alter table 表名 add index  索引名 (加索引的列1,加索引的列2) 

    删除某张表的索引:drop index 索引名 on 表名;

    (四)性能测试

    测试环境:博主工作用台式机

    处理器为Intel Core i5-4460 3.2GHz;

    内存8G;

    64位windows。

    1:创建一张测试表

    DROP TABLE IF EXISTS `test_user`;
    CREATE TABLE `test_user` (
      `id` bigint(20)  PRIMARY key not null AUTO_INCREMENT,
      `username` varchar(11) DEFAULT NULL,
      `gender` varchar(2) DEFAULT NULL,
      `password` varchar(100) DEFAULT NULL
    ) ENGINE=MyISAM DEFAULT CHARSET=utf8;

    存储引擎使用MyISAM是因为此引擎没有事务,插入速度极快,方便我们快速插入千万条测试数据,等我们插完数据,再把存储类型修改为InnoDB。

    2:使用存储过程插入1千万条数据

    create procedure myproc() 
    begin 
    declare num int; 
    set num=1; 
    while num <= 10000000 do 
    insert into test_user(username,gender,password) values(num,'保密',PASSWORD(num)); 
    set num=num+1;
    end while;
     end
    call myproc();
    

    由于使用的MyISAM引擎,插入1千万条数据,仅耗时246秒,若是InnoDB引擎,就要花费数小时了。

    然后将存储引擎修改回InnDB。使用如下命令:  alter table test_user engine=InnoDB;此命令执行时间大约耗时5分钟,耐心等待。

    tips:这里是测试,生产环境中不要随意修改存储引擎,还有alter table 操作,会锁整张表,慎用。其次:myisam引擎没有事务,且只是将数据写到内存中,然后定期将数据刷出到磁盘上,因此突然断电的情况下,会导致数据丢失。而InnDB引擎,是将数据写入日志中,然后定期刷出到磁盘上,所以不怕突然断电等情况。因此在实际生产中能用InnDB则用。

    3:sql测试

    select id,username,gender,password from test_user where id=999999

    耗时:0.114s。

    因为我们建表的时候,将id设成了主键,所以执行此sql的时候,走了主键索引,查询速度才会如此之快。

    我们再执行select id,username,gender,password from test_user where username='9000000'
    耗时:4.613s。

    我们给username列加上普通索引。

    ALTER TABLE `test_user` ADD INDEX index_name(username) ;

    此过程大约耗时 54.028s,建索引的过程会全表扫描,逐条建索引,当然慢了。

    再来执行:selectid,username,gender,password from test_user where username='9000000'
    耗时:0.043s。

     

    再用username和password来联合查询

    select id,username,gender,password  from test_user where username='9000000' and `password`='*3A70E147E88D99888804E4D472410EFD9CD890AE'

    此时虽然我们队username加了索引,但是password列未加索引,索引执行password筛选的时候,还是会全表扫描,因此此时

    查询速度立马降了下来。

    耗时:4.492s。

    当我们的sql有多个列的筛选条件的时候,就需要对查询的多个列都加索引组成聚合索引:

    加上聚合索引:ALTER TABLE `test_user` ADD INDEX index_union_name_password(username,password)
    再来执行:

    耗时:0.001s。

    开篇也说过软件层面的优化一是合理加索引;二是优化执行慢的sql。此二者相辅相成,缺一不可,如果加了索引,还是查询很慢,这时候就要考虑是sql的问题了,优化sql。

    Tips:

    1:加了索引,依然全表扫描的可能情况有:

    索引列为字符串,而没带引号;

    索引列没出现在where条件后面;

    索引列出现的位置没在前面。

    2:关联查询不走索引的可能情况有:

    关联的多张表的字符集不一样;

    关联的字段的字符集不一样;

    存储引擎不一样;

    字段的长度不一样。

     

    展开全文
  • Mysql百万数据优化方案
  • 如何优化MySQL千万大表,我写了6000字的解读

    万次阅读 多人点赞 2019-10-21 20:03:03
    千万大表如何优化,这是一个很有技术含量的问题,通常我们的直觉思维都会跳转到拆分或者数据分区,在此我想做一些补充和梳理,想和大家做一些这方面的经验总结,也欢迎大家提出建议。 从一开始脑海里开始也是...

    这是学习笔记的第 2138 篇文章

      640?wx_fmt=gif

    千万级大表如何优化,这是一个很有技术含量的问题,通常我们的直觉思维都会跳转到拆分或者数据分区,在此我想做一些补充和梳理,想和大家做一些这方面的经验总结,也欢迎大家提出建议。 

    从一开始脑海里开始也是火光四现,到不断的自我批评,后来也参考了一些团队的经验,我整理了下面的大纲内容。

    640?wx_fmt=png

    既然要吃透这个问题,我们势必要回到本源,我把这个问题分为三部分:

    “千万级”,“大表”,“优化”,

    也分别对应我们在图中标识的

    “数据量”,“对象”和“目标”。

    我来逐步展开说明一下,从而给出一系列的解决方案。 

    1.数据量:千万级

    千万级其实只是一个感官的数字,就是我们印象中的数据量大。 这里我们需要把这个概念细化,因为随着业务和时间的变化,数据量也会有变化,我们应该是带着一种动态思维来审视这个指标,从而对于不同的场景我们应该有不同的处理策略。

     

    1) 数据量为千万级,可能达到亿级或者更高

    通常是一些数据流水,日志记录的业务,里面的数据随着时间的增长会逐步增多,超过千万门槛是很容易的一件事情。

    2) 数据量为千万级,是一个相对稳定的数据量

    如果数据量相对稳定,通常是在一些偏向于状态的数据,比如有1000万用户,那么这些用户的信息在表中都有相应的一行数据记录,随着业务的增长,这个量级相对是比较稳定的。

    3) 数据量为千万级,不应该有这么多的数据

    这种情况是我们被动发现的居多,通常发现的时候已经晚了,比如你看到一个配置表,数据量上千万;或者说一些表里的数据已经存储了很久,99%的数据都属于过期数据或者垃圾数据。

    数据量是一个整体的认识,我们需要对数据做更近一层的理解,这就可以引出第二个部分的内容。 

    2.对象:数据表

    数据操作的过程就好比数据库中存在着多条管道,这些管道中都流淌着要处理的数据,这些数据的用处和归属是不一样的。

    一般根据业务类型把数据分为三种:

    (1)流水型数据

    流水型数据是无状态的,多笔业务之间没有关联,每次业务过来的时候都会产生新的单据,比如交易流水、支付流水,只要能插入新单据就能完成业务,特点是后面的数据不依赖前面的数据,所有的数据按时间流水进入数据库。

    (2)状态型数据

    状态型数据是有状态的,多笔业务之间依赖于有状态的数据,而且要保证该数据的准确性,比如充值时必须要拿到原来的余额,才能支付成功。

    (3)配置型数据

    此类型数据数据量较小,而且结构简单,一般为静态数据,变化频率很低。

    至此,我们可以对整体的背景有一个认识了,如果要做优化,其实要面对的是这样的3*3的矩阵,如果要考虑表的读写比例(读多写少,读少写多...),那么就会是3*3*4=24种,显然做穷举是不显示的,而且也完全没有必要,可以针对不同的数据存储特性和业务特点来指定不同的业务策略。 

    对此我们采取抓住重点的方式,把常见的一些优化思路梳理出来,尤其是里面的核心思想,也是我们整个优化设计的一把尺子,而难度决定了我们做这件事情的动力和风险。

    数据量增长情况

    数据表类型

    业务特点

    优化核心思想

    优化难度

    数据量为千万级,是一个相对稳定的数据量

    状态表

    OLTP业务方向

    能不拆就不拆读需求水平扩展

    ****

    数据量为千万级,可能达到亿级或者更高

    流水表

    OLTP业务的历史记录

    业务拆分,面向分布式存储设计

    ****

    OLAP业务统计数据源

    设计数据统计需求存储的分布式扩展

    ***

    数据量为千万级,不应该有这么多的数据

    配置表

    通用业务

    小而简,避免大一统

    *

    而对于优化方案,我想采用面向业务的维度来进行阐述。 

    3.目标:优化

    在这个阶段,我们要说优化的方案了,总结的有点多,相对来说是比较全了。

    整体分为五个部分:

    640?wx_fmt=png

    其实我们通常所说的分库分表等方案只是其中的一小部分,如果展开之后就比较丰富了。

    640?wx_fmt=png

    其实不难理解,我们要支撑的表数据量是千万级别,相对来说是比较大了,DBA要维护的表肯定不止一张,如何能够更好的管理,同时在业务发展中能够支撑扩展,同时保证性能,这是摆在我们面前的几座大山。

    我们分别来说一下这五类改进方案:

    优化设计方案1.规范设计

    在此我们先提到的是规范设计,而不是其他高大上的设计方案。

    黑格尔说:秩序是自由的第一条件。在分工协作的工作场景中尤其重要,否则团队之间互相牵制太多,问题多多。

    规范设计我想提到如下的几个规范,其实只是属于开发规范的一部分内容,可以作为参考。

    640?wx_fmt=png

    规范的本质不是解决问题,而是有效杜绝一些潜在问题,对于千万级大表要遵守的规范,我梳理了如下的一些细则,基本可以涵盖我们常见的一些设计和使用问题,比如表的字段设计不管三七二十一,都是varchar(500),其实是很不规范的一种实现方式,我们来展开说一下这几个规范。

    1)配置规范

    (1)MySQL数据库默认使用InnoDB存储引擎。

    (2)保证字符集设置统一,MySQL数据库相关系统、数据库、表的字符集使都用UTF8,应用程序连接、展示等可以设置字符集的地方也都统一设置为UTF8字符集。

    注:UTF8格式是存储不了表情类数据,需要使用UTF8MB4,可在MySQL字符集里面设置。在8.0中已经默认为UTF8MB4,可以根据公司的业务情况进行统一或者定制化设置。

    (3)MySQL数据库的事务隔离级别默认为RR(Repeatable-Read),建议初始化时统一设置为RC(Read-Committed),对于OLTP业务更适合。

    (4)数据库中的表要合理规划,控制单表数据量,对于MySQL数据库来说,建议单表记录数控制在2000W以内。

    (5)MySQL实例下,数据库、表数量尽可能少;数据库一般不超过50个,每个数据库下,数据表数量一般不超过500个(包括分区表)。

    2)建表规范

    (1)InnoDB禁止使用外键约束,可以通过程序层面保证。

    (2)存储精确浮点数必须使用DECIMAL替代FLOAT和DOUBLE。

    (3)整型定义中无需定义显示宽度,比如:使用INT,而不是INT(4)。

    (4)不建议使用ENUM类型,可使用TINYINT来代替。

    (5)尽可能不使用TEXT、BLOB类型,如果必须使用,建议将过大字段或是不常用的描述型较大字段拆分到其他表中;另外,禁止用数据库存储图片或文件。

    (6)存储年时使用YEAR(4),不使用YEAR(2)。

    (7)建议字段定义为NOT NULL。

    (8)建议DBA提供SQL审核工具,建表规范性需要通过审核工具审核后

    3)命名规范

    (1)库、表、字段全部采用小写。

    (2)库名、表名、字段名、索引名称均使用小写字母,并以“_”分割。

    (3)库名、表名、字段名建议不超过12个字符。(库名、表名、字段名支持最多64个字符,但为了统一规范、易于辨识以及减少传输量,统一不超过12字符)

    (4)库名、表名、字段名见名知意,不需要添加注释。

    对于对象命名规范的一个简要总结如下表4-1所示,供参考。

    命名列表

    对象中文名称

    对象英文全称

    MySQL对象简写

    视图

    view

    view_

    函数

    function

    func_

    存储过程

    procedure

    proc_

    触发器

    trigger

    trig_

    普通索引

    index

    idx_

    唯一索引

    unique index

    uniq_

    主键索引

    primary key

    pk_

    4)索引规范

    (1)索引建议命名规则:idx_col1_col2[_colN]、uniq_col1_col2[_colN](如果字段过长建议采用缩写)。

    (2)索引中的字段数建议不超过5个。

    (3)单张表的索引个数控制在5个以内。

    (4)InnoDB表一般都建议有主键列,尤其在高可用集群方案中是作为必须项的。

    (5)建立复合索引时,优先将选择性高的字段放在前面。

    (6)UPDATE、DELETE语句需要根据WHERE条件添加索引。

    (7)不建议使用%前缀模糊查询,例如LIKE “%weibo”,无法用到索引,会导致全表扫描。

    (8)合理利用覆盖索引,例如:

    (9)SELECT email,uid FROM user_email WHERE uid=xx,如果uid不是主键,可以创建覆盖索引idx_uid_email(uid,email)来提高查询效率。

    (10)避免在索引字段上使用函数,否则会导致查询时索引失效。

    (11)确认索引是否需要变更时要联系DBA。

    5)应用规范

    (1)避免使用存储过程、触发器、自定义函数等,容易将业务逻辑和DB耦合在一起,后期做分布式方案时会成为瓶颈。

    (2)考虑使用UNION ALL,减少使用UNION,因为UNION ALL不去重,而少了排序操作,速度相对比UNION要快,如果没有去重的需求,优先使用UNION ALL。

    (3)考虑使用limit N,少用limit M,N,特别是大表或M比较大的时候。

    (4)减少或避免排序,如:group by语句中如果不需要排序,可以增加order by null。

    (5)统计表中记录数时使用COUNT(*),而不是COUNT(primary_key)和COUNT(1);InnoDB表避免使用COUNT(*)操作,计数统计实时要求较强可以使用Memcache或者Redis,非实时统计可以使用单独统计表,定时更新。

    (6)做字段变更操作(modify column/change column)的时候必须加上原有的注释属性,否则修改后,注释会丢失。

    (7)使用prepared statement可以提高性能并且避免SQL注入。

    (8)SQL语句中IN包含的值不应过多。

    (9)UPDATE、DELETE语句一定要有明确的WHERE条件。

    (10)WHERE条件中的字段值需要符合该字段的数据类型,避免MySQL进行隐式类型转化。

    (11)SELECT、INSERT语句必须显式的指明字段名称,禁止使用SELECT * 或是INSERT INTO table_name values()。

    (12)INSERT语句使用batch提交(INSERT INTO table_name VALUES(),(),()……),values的个数不应过多。

    优化设计方案2:业务层优化

    业务层优化应该是收益最高的优化方式了,而且对于业务层完全可见,主要有业务拆分,数据拆分和两类常见的优化场景(读多写少,读少写多)

    640?wx_fmt=png

    1)业务拆分

    ü 将混合业务拆分为独立业务

    ü 将状态和历史数据分离

    业务拆分其实是把一个混合的业务剥离成为更加清晰的独立业务,这样业务1,业务2。。。独立的业务使得业务总量依旧很大,但是每个部分都是相对独立的,可靠性依然有保证。

    对于状态和历史数据分离,我可以举一个例子来说明。

    例如:我们有一张表Account,假设用户余额为100。

    640?wx_fmt=png

    我们需要在发生数据变更后,能够追溯数据变更的历史信息,如果对账户更新状态数据,增加100的余额,这样余额为200。

    这个过程可能对应一条update语句,一条insert语句。

    对此我们可以改造为两个不同的数据源,account和account_hist

    在account_hist中就会是两条insert记录,如下:

    640?wx_fmt=png

    而在account中则是一条update语句,如下:

    640?wx_fmt=png

    这也是一种很基础的冷热分离,可以大大减少维护的复杂度,提高业务响应效率。

    2)数据拆分

    2.1 按照日期拆分,这种使用方式比较普遍,尤其是按照日期维度的拆分,其实在程序层面的改动很小,但是扩展性方面的收益很大。

    • 数据按照日期维度拆分,如test_20191021

    • 数据按照周月为维度拆分,如test_201910

    • 数据按照季度,年维度拆分,如test_2019

    2.2 采用分区模式,分区模式也是常见的使用方式,采用hash,range等方式会多一些,在MySQL中我是不大建议使用分区表的使用方式,因为随着存储容量的增长,数据虽然做了垂直拆分,但是归根结底,数据其实难以实现水平扩展,在MySQL中是有更好的扩展方式。

    2.3 读多写少优化场景

    采用缓存,采用Redis技术,将读请求打在缓存层面,这样可以大大降低MySQL层面的热点数据查询压力。

    2.4 读少写多优化场景,可以采用三步走:

    1) 采用异步提交模式,异步对于应用层来说最直观的就是性能的提升,产生最少的同步等待。

    2) 使用队列技术,大量的写请求可以通过队列的方式来进行扩展,实现批量的数据写入。

    3) 降低写入频率,这个比较难理解,我举个例子

    对于业务数据,比如积分类,相比于金额来说业务优先级略低的场景,如果数据的更新过于频繁,可以适度调整数据更新的范围(比如从原来的每分钟调整为10分钟)来减少更新的频率。

    例如:更新状态数据,积分为200,如下图所示

    640?wx_fmt=png

    可以改造为,如下图所示。

    640?wx_fmt=png

    如果业务数据在短时间内更新过于频繁,比如1分钟更新100次,积分从100到10000,则可以根据时间频率批量提交。

    例如:更新状态数据,积分为100,如下图所示。

    640?wx_fmt=png

    无需生成100个事务(200条SQL语句)可以改造为2条SQL语句,如下图所示。

    640?wx_fmt=png

    对于业务指标,比如更新频率细节信息,可以根据具体业务场景来讨论决定。

    优化设计方案3:架构层优化

    架构层优化其实就是我们认为的那种技术含量很高的工作,我们需要根据业务场景在架构层面引入一些新的花样来。

    640?wx_fmt=png

    3.1.系统水平扩展场景

    3.1.1采用中间件技术,可以实现数据路由,水平扩展,常见的中间件有MyCAT,ShardingSphere,ProxySQL等

    640?wx_fmt=jpeg

    3.1.2 采用读写分离技术,这是针对读需求的扩展,更侧重于状态表,在允许一定延迟的情况下,可以采用多副本的模式实现读需求的水平扩展,也可以采用中间件来实现,如MyCAT,ProxySQL,MaxScale,MySQL Router等

    640?wx_fmt=png

    3.1.3 采用负载均衡技术,常见的有LVS技术或者基于域名服务的Consul技术等

    3.2.兼顾OLTP+OLAP的业务场景,可以采用NewSQL,优先兼容MySQL协议的HTAP技术栈,如TiDB

    3.3.离线统计的业务场景,有几类方案可供选择。

    3.3.1 采用NoSQL体系,主要有两类,一类是适合兼容MySQL协议的数据仓库体系,常见的有Infobright或者ColumnStore,另外一类是基于列式存储,属于异构方向,如HBase技术

    3.3.2 采用数仓体系,基于MPP架构,如使用Greenplum统计,如T+1统计

    优化设计方案4:数据库优化

    数据库优化,其实可打的牌也不少,但是相对来说空间没有那么大了,我们来逐个说一下。

    640?wx_fmt=png

    4.1 事务优化

    根据业务场景选择事务模型,是否是强事务依赖

    对于事务降维策略,我们来举出几个小例子来。

    4.1.1 降维策略1:存储过程调用转换为透明的SQL调用

    对于新业务而言,使用存储过程显然不是一个好主意,MySQL的存储过程和其他商业数据库相比,功能和性能都有待验证,而且在目前轻量化的业务处理中,存储过程的处理方式太“重”了。

    有些应用架构看起来是按照分布式部署的,但在数据库层的调用方式是基于存储过程,因为存储过程封装了大量的逻辑,难以调试,而且移植性不高,这样业务逻辑和性能压力都在数据库层面了,使得数据库层很容易成为瓶颈,而且难以实现真正的分布式。

    所以有一个明确的改进方向就是对于存储过程的改造,把它改造为SQL调用的方式,可以极大地提高业务的处理效率,在数据库的接口调用上足够简单而且清晰可控。

    4.1.2 降维策略2:DDL操作转换为DML操作

    有些业务经常会有一种紧急需求,总是需要给一个表添加字段,搞得DBA和业务同学都挺累,可以想象一个表有上百个字段,而且基本都是name1,name2……name100,这种设计本身就是有问题的,更不用考虑性能了。究其原因,是因为业务的需求动态变化,比如一个游戏装备有20个属性,可能过了一个月之后就增加到了40个属性,这样一来,所有的装备都有40个属性,不管用没用到,而且这种方式也存在诸多的冗余。

    我们在设计规范里面也提到了一些设计的基本要素,在这些基础上需要补充的是,保持有限的字段,如果要实现这些功能的扩展,其实完全可以通过配置化的方式来实现,比如把一些动态添加的字段转换为一些配置信息。配置信息可以通过DML的方式进行修改和补充,对于数据入口也可以更加动态、易扩展。

    4.1.3 降维策略3:Delete操作转换为高效操作

    有些业务需要定期来清理一些周期性数据,比如表里的数据只保留一个月,那么超出时间范围的数据就要清理掉了,而如果表的量级比较大的情况下,这种Delete操作的代价实在太高,我们可以有两类解决方案来把Delete操作转换为更为高效的方式。 

    第一种是根据业务建立周期表,比如按照月表、周表、日表等维度来设计,这样数据的清理就是一个相对可控而且高效的方式了。 

    第二种方案是使用MySQL rename的操作方式,比如一张2千万的大表要清理99%的数据,那么需要保留的1%的数据我们可以很快根据条件过滤补录,实现“移形换位”。

    4.2 SQL优化

    其实相对来说需要的极简的设计,很多点都在规范设计里面了,如果遵守规范,八九不离十的问题都会杜绝掉,在此补充几点:

    4.2.1 SQL语句简化,简化是SQL优化的一大利器,因为简单,所以优越。

    4.2.2 尽可能避免或者杜绝多表复杂关联,大表关联是大表处理的噩梦,一旦打开了这个口子,越来越多的需求需要关联,性能优化就没有回头路了,更何况大表关联是MySQL的弱项,尽管Hash Join才推出,不要像掌握了绝对大杀器一样,在商业数据库中早就存在,问题照样层出不穷。

    4.2.3 SQL中尽可能避免反连接,避免半连接,这是优化器做得薄弱的一方面,什么是反连接,半连接?其实比较好理解,举个例子,not in ,not exists就是反连接,in,exists就是半连接,在千万级大表中出现这种问题,性能是几个数量级的差异。 

    4.3 索引优化

    应该是大表优化中需要把握的一个度。

    4.3.1 首先必须有主键,规范设计中第一条就是,此处不接收反驳。

    4.3.2 其次,SQL查询基于索引或者唯一性索引,使得查询模型尽可能简单。

    4.3.3 最后,尽可能杜绝范围数据的查询,范围扫描在千万级大表情况下还是尽可能减少。

    优化设计方案4:管理优化

    这部分应该是在所有的解决方案中最容易被忽视的部分了,我放在最后,在此也向运维同事致敬,总是为很多认为本应该正常的问题尽职尽责(背锅)。

    640?wx_fmt=png

    千万级大表的数据清理一般来说是比较耗时的,在此建议在设计中需要完善冷热数据分离的策略,可能听起来比较拗口,我来举一个例子,把大表的Drop 操作转换为可逆的DDL操作。

    Drop操作是默认提交的,而且是不可逆的,在数据库操作中都是跑路的代名词,MySQL层面目前没有相应的Drop操作恢复功能,除非通过备份来恢复,但是我们可以考虑将Drop操作转换为一种可逆的DDL操作。

    MySQL中默认每个表有一个对应的ibd文件,其实可以把Drop操作转换为一个rename操作,即把文件从testdb迁移到testdb_arch下面;从权限上来说,testdb_arch是业务不可见的,rename操作可以平滑的实现这个删除功能,如果在一定时间后确认可以清理,则数据清理对于已有的业务流程是不可见的,如下图所示。

    640?wx_fmt=png

    此外,还有两个额外建议,一个是对于大表变更,尽可能考虑低峰时段的在线变更,比如使用pt-osc工具或者是维护时段的变更,就不再赘述了。

    最后总结一下,其实就是一句话:

    千万级大表的优化是根据业务场景,以成本为代价进行优化的,绝对不是孤立的一个层面的优化。

    近期热文:

    个人新书 《MySQL DBA工作笔记》

    个人公众号:jianrong-notes

    QQ群号:763628645

    QQ群二维码如下,个人微信号:jeanron100, 添加请注明:姓名+地区+职位,否则不予通过

    640?wx_fmt=png640?wx_fmt=png

    在看,让更多人看到

    展开全文
  • 设计商密SM1、SM2、SM3、SM4模块、专用算法模块和物理噪声源模块,对LED轻量密码算法芯片消耗功率进行优化,采用门功耗优化技术降低动态消耗。与未优化前的芯片进行实验对比,结果表明,功耗优化后的LED轻量...
  • 数据库SQL优化大总结之 百万数据库优化方案

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,000,459
精华内容 400,183
关键字:

优化级