android 数据库怎么监听数据变化

android 数据库怎么监听数据变化,第1张

在android中经常会用到改变数据库内容后再去使用数据库更新的内容,很多人会重新去query一遍,但是这样的问题就是程序会特别占内存,而且有可能会搂关cursor而导致程序内存未释放等等。其实android内部提供了一种ContentObserver的东西来监听数据库内容的变化。

ContentObserver的构造函数需要一个参数Hanlder,因为ContentObserver内部使用了一个实现Runnable接口的内部类NotificationRunnable,来实现数据库内容的变化。需要使用hanlder去post消息。注册ContentObserver的方法是:getContentResolver().registerContentObserver(uri, notifyForDescendents, observer).

上面3个参数为:uri----Uri类型,是需要监听的数据库的uri.

notifyForDescendents---boolean  true的话就会监听所有与此uri相关的uri。false的话则是直接特殊的uri才会监听。一般都设置为true.

observer-----ContentObserver  就是需要的contentobserver.

初始化一个ContentObserver对象,重载onChange(boolean ),在这个方法里去 *** 作数据库的使用,针对变化后的使用。

写了一个小demo,可以参考下。提示这种监听方式必须是contentprovider才能使用,因为contentprovider有uri.简单的那种sqlite数据库没有uri是使用不了的。

下面demo *** 作的是在一个activityA里点击button跳转至另外一个activityB,在B中点击button往数据库中加数据,加完后关闭B回到A。A的button的文字自动变化设置到数据库中的字符串。[code]

package ty.com.lto    

02         

03    import android.app.Activity    

04    import android.content.Intent    

05    import android.database.ContentObserver    

06    import android.os.Bundle    

07    import android.os.Handler    

08    import android.view.View    

09    import android.widget.Button    

10         

11    public class ListenDataTest extends Activity{    

12        private Button testBtn    

13         

14            @Override    

15            protected void onCreate(Bundle savedInstanceState) {    

16                    super.onCreate(savedInstanceState)    

17                    setContentView(R.layout.listen_data_test)    

18                   getContentResolver().registerContentObserver(DataChangeProvider.CONTENT_URI,    

19                                    true, cob)    

20                         

21                    testBtn = (Button)findViewById(R.id.test_btn)    

22                    testBtn.setOnClickListener(new View.OnClickListener() {    

23                                 

24                            public void onClick(View v) {    

25                                    Intent in = newIntent(ListenDataTest.this,DataChangeTest.class)    

26                                    startActivity(in)    

27                                         

28                            }    

29                    })    

30                         

31            }    

32                 

33            private ContentObserver cob = new ContentObserver(new Handler()) {    

34         

35                    @Override    

36                    public boolean deliverSelfNotifications() {    

37                            return super.deliverSelfNotifications()    

38                    }    

39         

40                    @Override    

41                    public void onChange(boolean selfChange) {    

42                            super.onChange(selfChange)    

43                           testBtn.setText(DataUtils.getChangeName(getApplicationContext()))    

44                    }    

45                          

46             }    

47         

48            @Override    

49            protected void onDestroy() {    

50                    super.onDestroy()    

51                    getContentResolver().unregisterContentObserver(cob)    

52            }    

53              

54                  

55    }    

[code]01    package ty.com.lto    

02         

03    import android.app.Activity    

04    import android.content.ContentValues    

05    import android.content.Intent    

06    import android.database.ContentObservable    

07    import android.database.ContentObserver    

08    import android.os.Bundle    

09    import android.os.Handler    

10    import android.view.View    

11    import android.widget.Button    

12         

13    public class DataChangeTest extends Activity{    

14         private Button dataBtn    

15         DataSqlite mDataSqlite    

16         @Override    

17         protected void onCreate(Bundle savedInstanceState) {    

18            super.onCreate(savedInstanceState)    

19            setContentView(R.layout.data_change_test)    

20            dataBtn = (Button)findViewById(R.id.data_test_btn)    

21            mDataSqlite = new DataSqlite(this)    

22            dataBtn.setOnClickListener(new View.OnClickListener() {    

23                     

24                public void onClick(View v) {    

25                    ContentValues con = new ContentValues()    

26                    con.put("name", "数据变化了")    

27                    getContentResolver().insert(DataChangeProvider.CONTENT_URI, con)    

28                    finish()    

29                }    

30            })    

31         }    

32    }    

[code]view sourceprint?

001    package ty.com.lto    

002          

003          

004    import android.content.ContentProvider    

005    import android.content.ContentUris    

006    import android.content.ContentValues    

007    import android.content.Context    

008    import android.content.UriMatcher    

009    import android.database.Cursor    

010    import android.database.SQLException    

011    import android.database.sqlite.SQLiteDatabase    

012    import android.database.sqlite.SQLiteOpenHelper    

013    import android.database.sqlite.SQLiteQueryBuilder    

014    import android.database.sqlite.SQLiteDatabase.CursorFactory    

015    import android.net.Uri    

016    import android.text.TextUtils    

017          

018    public class DataChangeProvider extends ContentProvider{    

019        private SQLiteOpenHelper mOpenHelper    

020        private static final int ALARMS = 1    

021        private static final int ALARMS_ID = 2    

022        private static final UriMatcher sURLMatcher = new UriMatcher(UriMatcher.NO_MATCH)    

023        public static final Uri CONTENT_URI = Uri.parse("content://ty.com.lto/test")    

024          

025        static {    

026            sURLMatcher.addURI("ty.com.lto", "test", ALARMS)    

027            sURLMatcher.addURI("ty.com.lto", "test/#", ALARMS_ID)    

028        }    

029             

030        private static class DatabaseHelper extends SQLiteOpenHelper{    

031             private static final String TEST_DATABASE = "test.db"    

032             private static final int VERSION = 1    

033                  

034             public DatabaseHelper(Context context) {    

035                 super(context, TEST_DATABASE, null, VERSION)    

036                 // TODO Auto-generated constructor stub    

037             }    

038                   

039          

040             @Override    

041             public void onCreate(SQLiteDatabase db) {    

042                 String sql = "CREATE TABLE "+"test"+" (" +    

043                         "_id INTEGER PRIMARY KEY," +    

044                         "name TEXT "+    

045                          ")"    

046                 db.execSQL(sql)    

047             }    

048          

049             @Override    

050             public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {    

051                 String sql = "DROP TABLE IF EXIST "+TEST_DATABASE    

052                 db.execSQL(sql)    

053                 onCreate(db)    

054             }    

055                  

056        }    

057             

058        public DataChangeProvider() {    

059        }    

060             

061        @Override    

062        public int delete(Uri url, String where, String[] whereArgs) {    

063            SQLiteDatabase db = mOpenHelper.getWritableDatabase()    

064            int count    

065            long rowId = 0    

066            switch (sURLMatcher.match(url)) {    

067                case ALARMS:    

068                    count = db.delete("test", where, whereArgs)    

069                    break    

070                case ALARMS_ID:    

071                    String segment = url.getPathSegments().get(1)    

072

一般来说,当检测到性能问题时,我们会收集覆盖了发生问题的时间段的AWR报告-但是最好只收集覆盖1个小时时间段的AWR报告-如果时间过长,那么AWR报告就不能很好的反映出问题所在。还应该收集一份没有性能问题的时间段的AWR报告,作为一个参照物来对比有问题的时间段的AWR报告。这两个AWR报告的时间段应该是一致的,比如都是半个小时的,或者都是一个小时的。

Interpretation

在处理性能问题时,我们最关注的是数据库正在等待什么。

当进程因为某些原因不能进行 *** 作时,它需要等待。花费时间最多的等待事件是我们最需要关注的,因为降低它,我们能够获得最大的好处。

AWR报告中的"Top 5 Timed Events"部分就提供了这样的信息,可以让我们只关注主要的问题。

Top 5 Timed Events

正如前面提到的,"Top 5 Timed Events"是AWR报告中最重要的部分。它指出了数据库的sessions花费时间最多的等待事件,如下:

Top 5 Timed Events Avg %Total

~~~~~~~~~~~~~~~~~~wait Call

Event WaitsTime (s) (ms) Time Wait Class

------------------------------ ------------ ----------- ------ ------ ----------

db file scattered read 10,152,564 81,327 8 29.6 User I/O

db file sequential read 10,327,231 75,878 7 27.6 User I/O

CPU time 56,207 20.5

read by other session 4,397,330 33,455 8 12.2 User I/O

PX Deq Credit: send blkd 31,398 26,5768469.7 Other

-------------------------------------------------------------

Top 5 Events部分包含了一些跟Events(事件)相关的信息。它记录了这期间遇到的等待的总次数,等待所花费的总时间,每次等待的平均时间;这一部分是按照每个Event占总体call time的百分比来进行排序的。

根 据Top 5 Events部分的信息的不同,接下来我们需要检查AWR报告的其他部分,来验证发现的问题或者做定量分析。等待事件需要根据报告期的持续时间和当时数据 库中的并发用户数进行评估。如:10分钟内1000万次的等待事件比10个小时内的1000万等待更有问题;10个用户引起的1000万次的等待事件比 10,000个用户引起的相同的等待要更有问题。

就像上面的例子,将近60%的时间是在等待IO相关的事件。

其他20%的时间是花在使用或等待CPU time上。过高的CPU使用经常是性能不佳的SQL引起的(或者这些SQL有可能用更少的资源完成同样的 *** 作);对于这样的SQL,过多的IO *** 作也是一个症状。关于CPU使用方面,我们会在之后讨论。

在以上基础上,我们将调查是否这个等待事件是有问题的。若有问题,解决它;若是正常的,检查下个等待事件。

过多的IO相关的等待一般会有两个主要的原因:

Top 5 Events部分的显示的信息会帮助我们检查:

需要注意,接下来的分析步骤取决于我们在TOP 5部分的发现。在上面的例子里,3个top wait event表明问题可能与SQL语句执行计划不好有关,所以接下来我们要去分析"SQL Statistics"部分。

同样的,因为我们并没有看到latch相关的等待,latch在我们这个例子里并没有引发严重的性能问题;那么我们接下来就完全不需要分析latch相关的信息。

一 般来讲,如果数据库性能很慢,TOP 5等待事件里"CPU", "db file sequential read" 和"db file scattered read" 比较明显(不管它们之间的顺序如何),我们总是需要检查Top SQL (by logical and physical reads)部分;调用SQL Tuning Advisor或者手工调优这些SQL来确保它们是有效率的运行。

是否数据库做了大量的读 *** 作:

上面的图显示了在这段时间里两类读 *** 作都分别大于1000万,这些 *** 作是否过多取决于报告的时间是1小时或1分钟。我们可以检查AWR报告的elapsed time如果这些读 *** 作确实是太多了,接下来我们需要检查AWR报告中 SQL Statistics 部分的信息,因为读 *** 作都是由SQL语句发起的。

是否是每次的IO读 *** 作都很慢:

上面的图显示了在这段时间里两类读 *** 作平均的等待时间是小于8ms的

至于8ms是快还是慢取决于底层的硬件设备;一般来讲小于20ms的都可以认为是可以接受的。

我们还可以在AWR报告"Tablespace IO Stats"部分得到更详细的信息

Tablespace IO Stats DB/Inst: VMWREP/VMWREP Snaps: 1-15

->ordered by IOs (Reads + Writes) desc

Tablespace

------------------------------

Av Av Av Av Buffer Av Buf

Reads Reads/s Rd(ms) Blks/Rd Writes Writes/s Waits Wt(ms)

-------------- ------- ------ ------- ------------ -------- ---------- ------

TS_TX_DATA

14,246,367 2837.6 4.6 145,263,8802,883 3,844,1618.3

USER

204,834 4 10.7 1.0 17,849,021 354 15,2499.8

UNDOTS1

19,725 03.0 1.0 10,064,086 200 1,9644.9

AE_TS

4,287,567 855.4 6.7 9320465,7933.7

TEMP

2,022,883 400.0 5.8 878,049 17 00.0

UNDOTS3

1,310,493 264.6 1.0 941,675 19 430.0

TS_TX_IDX

1,884,478 377.3 1.0 23,6950 73,7038.3

>SYSAUX

346,094 75.6 3.9 112,7442 00.0

SYSTEM

101,771 27.9 3.5 25,09806532.7

如上图,我们关心Av Rd(ms)的指标。如果它高于20ms并且同时有很多读 *** 作的,我们可能要开始从OS的角度调查是否有潜在的IO问题。

注:对于一些比较空闲的tablespace/files,我们可能会得到一个比较大的Av Rd(ms)值;对于这样的情况,我们应该忽略这样的tablespace/files因为这个很大的值可能是由于硬盘自旋(spin)引起的,没有太大的参考意义。比如对

于一个有1000万次读 *** 作而且很慢的系统,引起问题的基本不可能是一个只有10次read的tablespace/file.

虽 然高"db file scattered read"和"db file sequential read"等待可以是I / O相关的问题,但是很多时候这些等待也可能是正常的;实际上,对一个已经性能很好的数据库系统,这些等待事件往往在top 5等待事件里,因为这意味着您的数据库没有那些真正的“问题”。

诀窍是能够评估引起这些等待的语句是否使用了最优的访问路径。如果"db file scattered read"比较高,那么相关的SQL语句可能使用了全表扫描而没有使用索引(也许是没有创建索引,也许是没有合适的索引);相应的,如果"db file sequential read"过多,则表明也许是这些SQL语句使用了selectivity不高的索引从而导致访问了过多不必要的索引块或者使用了错误的索引。这些等待可 能说明SQL语句的执行计划不是最优的。

接下来就需要通过AWR来检查这些top SQL是否可以进一步的调优,我们可以查看AWR报告中 SQL Statistics 的部分.

上面的例子显示了20%的时间花在了等待或者使用CPU上,我们也需要检查 SQL statistics 部分来进一步的分析。

数据库做了太多的读 *** 作

每次的IO读 *** 作都很慢

事件"db file scattered read"一般表明正在做由全表扫描或者index fast full scan引起的多块读。

事件"db file sequential read"一般是由不能做多块读的 *** 作引起的单块读(如读索引)

SQL Statistics

AWR包含了一些不同的SQL统计值:

根据Top 5 部分的Top Wait Event不同,我们需要检查不同的SQL statistic。

在我们这个例子里,Top Wait Event是"db file scattered read","db file sequential read"和CPU;我们最需要关心的是SQL ordered by CPU Time, Gets and Reads。

我们会从"SQL ordered by gets"入手,因为引起高buffer gets的SQL语句一般是需要调优的对象。

SQL ordered by Gets

->Resources reported for PL/SQL code includes the resources used by all SQL

statements called by the code.

->Total Buffer Gets: 4,745,943,815

->Captured SQL account for 122.2% of Total

Gets CPU Elapsed

Buffer Gets Executionsper Exec %Total Time (s) Time (s)SQL Id

-------------- ------------ ------------ ------ -------- --------- -------------

1,228,753,877 168 7,314,011.2 25.9 8022.46 8404.73 5t1y1nvmwp2

SELECT ADDRESSID",CURRENT$."ADDRESSTYPEID",CURRENT$URRENT$."ADDRESS3",

CURRENT$."CITY",CURRENT$."ZIP",CURRENT$."STATE",CURRENT$."PHONECOUNTRYCODE",

CURRENT$."PHONENUMBER",CURRENT$."PHONEEXTENSION",CURRENT$."FAXCOU

1,039,875,759 62,959,363 16.5 21.9 5320.27 5618.96 grr4mg7ms81

Module: DBMS_SCHEDULER

INSERT INTO "ADDRESS_RDONLY" ("ADDRESSID","ADDRESSTYPEID","CUSTOMERID","

ADDRESS1","ADDRESS2","ADDRESS3","CITY","ZIP","STATE","PHONECOUNTRYCODE","PHONENU

854,035,223 168 5,083,543.0 18.0 5713.50 7458.95 4at7cbx8hnz

SELECT "CUSTOMERID",CURRENT$."ISACTIVE",CURRENT$."FIRSTNAME",CURRENT$."LASTNAME",CU<

RRENT$."ORGANIZATION",CURRENT$."DATEREGISTERED",CURRENT$."CUSTOMERSTATUSID",CURR

ENT$."LASTMODIFIEDDATE",CURRENT$."SOURCE",CURRENT$."EMPLOYEEDEPT",CURRENT$.

对这些Top SQL,可以手工调优,也可以调用SQL Tuning Advisor。

分析:

Other SQL Statistic Sections

就像之前提到的那样,AWR报告中有很多不同的部分用来分析各种不同的问题。如果特定的问题并没有出现,那么分析AWR报告的这些部分并不能有很大的帮助。

下面提到了一些可能的问题:

Waits for 'Cursor: mutex/pin' 如 果发现了一些像"Cursor: pin S wait on X" 或"Cursor: mutex X" 类的mutex等待,那么可能是由于parsing引起的问题。检查"SQL ordered by Parse Calls" 和"SQL ordered by Version Count"部分的Top SQL,这些SQL可能引起这类的问题。

单次执行buffer gets过多

SQL_ID为'5t1y1nvmwp2'和'4at7cbx8hnz'的SQL语句总共被执行了168次,但是每次执行引起的buffer gets超过500万。这两个SQL应该是主要的需要调优的候选者。

执行次数过多

SQL_ID 'grr4mg7ms81' 每次执行只是引起16次buffer gets,减少这条SQL每次执行的buffer get可能并不能显著减少总共的buffer gets。这条语句的问题是它执行的太频繁了,6500万次。

改变这条SQL的执行次数可能会更有意义。这个SQL看起来是在一个循环里面被调用,如果可以让它一次处理的数据更多也许可以减少它执行的次数。

->Total Buffer Gets: 4,745,943,815

假设这是一个一个小时的AWR报告,4,745,943,815是一个很大的值;所以需要进一步分析这个SQL是否使用了最优的执行计划

Individual Buffer Gets

上面的例子里单个的SQL的buffer get非常多,最少的那个都是8亿5千万。这三个SQL指向了两个不同的引起过多buffers的原因:

注意:对于某些非常繁忙的系统来讲,以上的数字可能都是正常的。这时候我们需要把这些数字跟正常时段的数字作对比,如果没有什么太大差别,那么这些SQL并不是引起问题的元凶(虽然通过调优这些SQL我们仍然可以受益)

Load Profile

根据Top 5等待事件的不同,"Load Profile"可以提供一些有用的背景资料或潜在问题的细节信息。

Load Profile

~~~~~~~~~~~~Per Second Per Transaction

--------------- ---------------

Redo size: 4,585,414.80 3,165,883.14

Logical reads: 94,185.63 65,028.07

Block changes: 40,028.57 27,636.71

Physical reads: 2,206.12 1,523.16

Physical writes: 3,939.97 2,720.25

User calls: 50.08 34.58

Parses: 26.96 18.61

Hard parses: 1.49 1.03

Sorts: 18.36 12.68

Logons: 0.13 0.09

Executes: 4,925.89 3,400.96

Transactions: 1.45

% Blocks changed per Read: 42.50Recursive Call %:99.19

Rollback per transaction %: 59.69 Rows per Sort: 1922.64

在这个例子里,Top 5 Events部分显示问题可能跟SQL的执行有关,那么我们接下来检查load profile部分。

如果您检查AWR report是为了一般性的性能调优,那么可以看到有比较多的redo activity和比较高的physical writes. Physical writes比physical read要高,并且有42%的块被更改了.

此外,hard parse的次数要少于soft parse.

如果mutex等待事件比较严重,如"library cache: mutex X",那么查看所有parse的比率会更有用。

当然,如果把Load Profile部分跟正常时候的AWR报告做比较会更有用,比如,比较redo size, users calls, 和 parsing这些性能指标。

Instance Efficiency

Instance Efficiency部分更适用于一般性的调优,而不是解决某个具体问题(除非等待事件直接指向这些指标)。

Instance Efficiency Percentages (Target 100%)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Buffer Nowait %: 99.91 Redo NoWait %: 100.00

Buffer Hit %: 98.14In-memory Sort %: 99.98

Library Hit %: 99.91Soft Parse %: 94.48

Execute to Parse %: 99.45 Latch Hit %: 99.97

Parse CPU to Parse Elapsd %: 71.23 % Non-Parse CPU: 99.00

从我们的这个例子来看,最有用的信息是%Non-Parse CPU,它表明几乎所有的CPU都消耗在了Execution而不是Parse上,所以调优SQL会对性能有改善。


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/sjk/10808698.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-11
下一篇 2023-05-11

发表评论

登录后才能评论

评论列表(0条)

保存