Mysql数据库读书笔记
(三)數據庫
1.數據庫的分類及常用的數據庫:
數據庫分為:關系型數據庫和非關系型數據庫
常用的關系型數據庫有:MySQL、oracle、sqlserver等; 常用的非關系型數據庫有:redis、memcache、mogodb、hadoop等。2.簡單介紹一下關系型數據庫的三大范式:
什么是范式? (簡單理解,就是關系型數據庫在設計表的時候需要遵循的規范)
要想滿足第二范式,必須先滿足第一范式;要滿足第三范式,必須先滿足第二范式;第一范式(1NF):[列數據的不可分割] 指數據庫表的每一列都是不可分割的基本數據項,同一列中不能有多個值,即實體中的每一個屬性都不能有多個值、或者不能有重復的屬性;
第二范式(2NF):[主鍵唯一] 要求數據庫表中的每一行必須被唯一區分,為實現區分通常需要為表加上一個列,以存儲各個實例的唯一標識; 除主鍵以外的其他字段,都必須完全依賴與主鍵;
第三范式(3NF):[外鍵] 要求一個數據庫表中不包含已經在其表中已包含的非主鍵信息,即要求除主鍵之外的其他字段不能存在依賴關系;
反三范式:有的時候,為了效率,可以設置重復或者可以推導出的字段;例如:一張表中已經有"訂單項(單價)",但是,當訂單項太多的時候,為了效率,會在表中添加"訂單(總價)"【即使由單價和數量可以推導出總價】。
3.MySQL的SQL語句:
SQL:(Structure Query Language)結構化查詢語言
DDL:數據庫定義語言(定義數據庫、數據表的結構)【create:創建; drop :刪除;alter:修改】
DML:數據操縱語言(主要用來操作語言)【insert:插入; update:修改; delete:刪除】
DCL:數據控制語言(定義訪問權限、取消訪問權限:條件管理、安全設置)【grant:分配權限】
DQL:數據查詢語言【select:查詢;from子句;where子句】
登錄數據庫服務器:mysql -u賬戶 -p密碼1)數據庫的增刪改查(CRUD)的操作:
-
創建數據庫:
- (1)【create database+數據庫名字】;
- (2)【create database +數據庫名字+character set UTF-8】:創建數據庫的時候,可以指定字符集;
- (3)【create database+數據庫名字+character set UTF-8+collate+校對規則】:設置默認的校對規則,不區分大小;
-
查看數據庫:
- 【show databases】:查看所有數據庫;
- 【show create database+數據庫名稱】:查看數據庫定義的語句;
-
修改數據庫:(一個數據庫創建出來,基本上就不會去動它了)
- 【alter database+數據庫名稱+character set+字符集名稱】:修改數據庫名稱指定字符集;
-
刪除數據庫:
- 【drop database+數據庫名稱】;
2)數據表的增刪改查操作:
-
創建表:
create table+表名(
列名1 列的類型(長度) 約束,
列名2 列的類型(長度) 約束);列的約束:
(1)primary key(主鍵約束):默認不能為空,內容必須唯一;一張表只能有一個主鍵;外鍵都是指向另一張表的主鍵;
(2)unique(唯一約束):列里面的內容必須唯一,不能出現重復,可以為空;一張表可以有多個唯一約束;唯一約束不可以作為其他表的外鍵;
(3)not null(非空約束);
(4)auto_increment(自動增長);列的類型:
int(整型類型);
char(固定長度的字符類型);
varchar(可變長度的字符類型);
double / float / boolean
blob(存放的是二進制);
text(主要用來存放文本)
data:YYYY-MM-DD;
time: hh:mm:ss;
datatime: YYYY-MM-DD hh:mm:ss(默認值為null);
timestamp: YYYY-MM-DD hh:mm:ss(默認使用當前時間); -
查看表:
- 查看所有表:【show tables】;
- 查看表的創建過程:【show create table+表名】;
- 查看表結構:【desc +表名】;
-
修改表:
- 添加列(add):【alter table+表名+add+列名+列的類型+列的約束】;
- 修改列(modify):【alter table+表名+modify+列名+列的類型+列的約束】;
- 修改列名(change):【alter table+表名+change+舊表名+新表名+列的類型】;
- 刪除列(drop):【alter table+表名+drop+列名】;
- 修改表的字符集:【alter table+表名+character set+字符集名稱】;
- 修改表名:【rename table+舊表名+to+新表名】;
-
刪除表:
- 【drop table+表名】;
3)對表中數據的增刪改查操作:
-
為表中增加數據:
方法1:
insert into +表名(列名1,列名2)+values+(值1,值2);(注:插入部分列,列名不能省略);方法2:
insert into +表名+values+(值1,值2,值3);方法3(批量插入):
insert into +表名+values+(值11,值12,值13)+(值21,值22,值23)+(值31,值32,值33);注:命令行中插入中文,會出現亂碼問題:
step1:在任務管理器中,暫停MySQL的服務;step2:在MySQL安裝路徑中找到 my.ini 配置文件;step3:將文件中第57行的編碼改為GBK,保存文件,退出;step4:啟動MySQL服務。
(1)臨時的解決方案是:set names GBK;
(2)永久的解決方案是:修改 my.ini 配置(在MySQL軟件的安裝路徑里),具體步驟: -
為表中刪除數據:
delete from +表名+ where 條件
(如果沒有指定條件,會將表中的數據全部刪除)
- 為表中修改數據:
update +表名+set+ 列名1=列的值1,列名2=列的值2 where 條件;
例如:update student set sname="李四" where sid=3;//表示將sid為5的名字改為李四;如果沒有寫where條件,那么整個表的每一行都會被修改;- 為表中查詢數據:
新建商品分類表(包含屬性:分類ID、分類名稱、分類描述),如下:
create table category(
cid int primary key auto_increament,
cname varchar(10),
cdesc varchar(30)
);
為分類表中插入數據,如下:
insert into category values(null,‘手機數碼’,‘電子產品’);
insert into category values(null,‘鞋靴箱包’,‘江南皮革廠制造’);
insert into category values(null,‘香煙酒水’,‘二鍋頭’);
insert into category values(null,‘酸奶餅干’,‘安慕希’);
1)簡單查詢(select…from):
select * from product;//查詢所有商品 select pname,price from product;//查詢商品名稱和商品價格2)別名查詢(as):
select p.pname,p.price from product as p;//表別名 select pname as 商品名稱, price as 商品價格 from product;//列別名3)去掉重復的值(distinct):
select price from product;//查詢商品表中所有的商品價格 select distinct price from product;//查詢所有的未重復的商品價格4)運算查詢(在查詢結果上做了加減乘除運算):
select *,price*1.5 as 折后價 from product; //顯示商品表,并多出來一列:折后價5)條件查詢(where):
select * from product where price>60;//查詢商品價格大于60的所有商品信息where后的條件寫法: (1)關系運算符: >, >=, <, <=, =, !=( 相當于<> )select * from product where price <> 88;//查詢商品價格不等于88的所有商品(2)邏輯運算:and,or,notselect * from product where price>10 and price<100;或者select * from product where price between 10 and 100;(3)模糊查詢(like):_:代表一個字符; %:代表多個字符;例如:select * from product where pname like '%餅%';//查詢出名字中帶有'餅'的所有商品select * from product where pname like '_熊%';//查詢第二個字為'熊'的所有商品(4)在某個范圍中獲得值(in):例如:select * from product where cno in (1,3,4);//查詢出商品分類ID在1,3,4里面的商品6)排序查詢(order by):
注:asc:(ascend)升序;desc:(descend) 降序; 例如: select * from product order by price;//查詢所有商品,并按照商品價格排序 select * from product order by price desc;//查詢所有商品,并按照商品價格降序排序 select * from product where pname like '%小%' order by price asc;//查詢商品名稱中帶‘小’的商品,并按照價格升序排序7)聚合函數:
sum():求和; avg():求平均值; count():統計數量; max():求最大值; min():求最小值;注意:where條件后面不能接聚合函數select sum(price) from product;//獲取所有商品的價格總和 select avg(price) from product;//獲取所有商品的平均價格 select count(*) from product;//獲取所有商品的個數8)分組(group by … having):
select cno,count(*) from product group by cno; //根據cno字段分組,分組后統計商品的數量select cno,avg(price) from product group by cno having avg(price)>70; //根據cno字段分組,分組統計出平均價格大于70 的每組商品的平均價格小結:
編寫順序:select ... from ... where ... group by ... having ... order by ;執行順序:from ... where ... group by ... having ... select ...order by ;4.SQL的多表操作:
1)多表之間的關系如何維護?
添加外鍵約束:foreign key例如:在商品表product中添加外鍵 cno ,指向商品分類表category的 cid; 方法1: alter table product add foreign key (cno) references category (cid);方法2:直接在新建商品表product時,增加外鍵,如下: create table product(pid int primary key auto_increament,pname varchar(10),price double,pdate timestamp,cno int,foreign key (cno) references category (cid) );2)刪除的時候,先刪除外鍵關聯的所有數據,再刪除分類的數據;
例如:從商品分類表category中刪除分類ID為4的信息,需要:(1)首先,在商品表product中,刪除 cno=4 的商品;(2)然后,才能刪除商品分類表category中 cid=4的信息;3)多表之間的建表原則:
一對多:(如商品和分類)在多的一方(商品表)中添加一個外鍵指向一的一方(分類表)的主鍵; 多對多:(如老師和學生)多建一張中間表,把多對多的關系拆分為一對多的關系,中間表中至少要有兩個外鍵,這兩個外鍵指向原來的那兩張表; 一對一:不常用,拆表操作4)數據庫客戶端軟件(SQLyog)
5)多表查詢:
-
交叉連接查詢:
- select * from product,category;
- select * from product,category where cno=cid;//過濾出有意義的數據
-
內連接查詢:
-
隱式內連接:(交叉連接實際上就是隱式內連接)
select * from product p, category c where p.cno=c.cid;
-
顯式內連接(inner join … on ):
select * from product p inner join category c on p.cno=c.cid
-
隱式內連接和顯式內連接的區別:
隱式內連接是,在查詢出的結果的基礎上 去做where條件的過程;
顯式內連接是,帶著條件去查詢結果的過程,所以效率較高 -
-
外連接查詢:
- 左外連接:會將左表中的所有數據都查詢出來,如果右表中沒有對應的數據全是null;
select * from product p left outer join category c on p.cno=c.cid - 右外連接:會將右表中的所有數據都查詢出來,如果左表中沒有對應的數據全是null;
select * from product p right outer join category c on p.cno=c.cid
- 左外連接:會將左表中的所有數據都查詢出來,如果右表中沒有對應的數據全是null;
-
分頁查詢(limit):
-
假設每頁數據的上限為3條,起始索引為 0:
select * from product limit 0,3;//第一個參數 0 表示起始索引;第二個參數 3 表示每頁顯示的個數 -
計算每一頁的起始索引:
startindex = (index-1)*num
其中,index表示當前顯示為第幾頁;num表示每頁的數據上限;頁數從1開始,索引從0開始;
-
-
子查詢(實際上就是SQL的嵌套):
例子1:查詢出分類名稱為’手機數碼’的所有商品
step1:查詢分類名稱為'手機數碼'的分類ID:select cid from category where cname="手機數碼"; step2:查詢出響應分類ID等于1的所有商品:select * from product where cno=1; 合并以上,有:select * from product where cno=(select cid from category where cname='手機數碼');例子2:查詢商品名稱、商品分類名稱的信息:
左連接:select p.pname,c.cname from product p left outer join category c on p.cno=c.cid;子查詢:select p.pname,(select c.cname from category c where c.cid=p.cno) as 商品分類名稱 from product p;5.JDBC(JAVA database connection):
簡單說一下你對JDBC的理解:
JAVA database connection,數據庫連接。數據庫管理系統(MySQL、oracle等)有很多,每個數據庫管理系統支持的命令是不一樣的。Java只定義接口,讓數據庫廠商自己實現接口,對于我們開發者而言,只需要導入對應廠商開發的實現即可。然后以接口的方式進行調用(MySQL+MySQL驅動(實現)+jdbc)我們的Java代碼只要使用sun公司提供jdbc驅動即可。寫一個簡單的JDBC的程序(或者寫一個訪問oracle數據的JDBC程序):
在Java Eclipse 中驅動JDBC,首先,需要導入jar包,具體操作如下:在項目下新建一個文件夾lib,右擊Build Path/Configure build path/java build path/libraries/add external jars/選擇文件"MySQL-connector-Java-5.1.46-bin.jar"
JDBC 的基本步驟: (1)注冊驅動;(com.mysql.jdbc.Driver; oracle.jdbc.driver.OracleDriver) (2)建立連接;(DriverManager.getConnection(url,username,password)) (3)創建statement,設置參數;(PreparedStatement/Statement; ps.setXXX(index,value)) (4)執行sql語句;(executeQuery(); executeUpdate()) (5)釋放連接;(是否連接要從小到大,必須放到finally)具體如下:
1)首先,新建一個配置文件jdbc.properties,如下:
driverClass=com.mysql.jdbc.Driver url=jdbc:mysql://localhost/數據庫名稱 name=賬戶 password=密碼2)然后,JDBC 工具類整合:
public class JDBCUtil{static String driverClass=null;static String url=null;static String name=null;static String password=null;static{try{//創建一個屬性配置對象Properties properties=new Properties();//兩種方式導入輸入流InpurStream is=new FileInputStream("jdbc.properties");//或者 InputStream is = JDBCUtil.class.getClassLoader.getResourceAsStream("jdbc.properties");properties.load(is);//讀取屬性driverClass=properties.getProperty("jdbc.properties");url=properties.getProperty("url");name=properties.getProperty("name");password=properties.getProperty("password");}catch(Exception e){e.printStackTrace();}}//獲取連接對象public static getConn(){Connection conn=null;try{//驅動class.forName(driverClass);//建立連接conn=DriverManage.getConnection(url,name,password);}catch(Exception e){e.printStackTrace();}return conn;}//釋放資源public static void release(Connection conn,Statement st, ResultSet rs) {closeConn(conn);closeSt(st);closeRs(rs);}public static void release(Connection conn,Statement st) {closeConn(conn);closeSt(st);}private static void closeConn(Connection conn) {try {if(conn!=null) {conn.close();}}catch(SQLException e) {e.printStackTrace();}finally {conn=null;}}private static void closeSt(Statement st) {try {if(st!=null) {st.close();}}catch(SQLException e) {e.printStackTrace();}finally {st=null;}}private static void closeRs(ResultSet rs) {try {if(rs!=null) {rs.close();}}catch(SQLException e) {e.printStackTrace();}finally {rs=null;}} }3)利用JDBC Dao模式,去實現數據庫的登錄和查詢操作:
首先,新建一個Dao接口,里面聲明數據庫的訪問規則,如下:
//定義操作數據庫的方法 public interface UserDao {// 查詢所有void findAll();/**登錄* @param username* @param password*/void login(String username,String password); }然后,新建一個Dao的實現類,具體實現之前接口中定義的規則,如下:
public class UserDaoImpl implements UserDao {@Overridepublic void findAll() {Connection conn=null;Statement st=null;ResultSet rs =null;try {conn = JDBCUtil.getConn();st = conn.createStatement();String sql = "select * from t_user";rs = st.executeQuery(sql);while(rs.next()) {String username=rs.getString("username");int password=rs.getInt("password");System.out.println("username="+username+",password="+password);}} catch (Exception e) {e.printStackTrace();}finally {JDBCUtil.release(conn, st, rs);}}public void login(String username, String password) {Connection conn=null;PreparedStatement ps=null;ResultSet rs=null;try {conn = JDBCUtil.getConn();//SELECT * FROM t_user WHERE username= 'zhangsan' AND PASSWORD=9876String sql = "select * from t_user where username=?and password=?";ps = conn.preparedStatement(sql);ps.setString(1,username);ps.setString(2,password);rs = ps.executeQuery();if(rs.next()) {System.out.println("登錄成功");}else {System.out.println("登錄失敗");}} catch (Exception e) {e.printStackTrace();}finally{JDBCUtil.release(conn, st, rs);}} }最后,直接使用實現,如下:
public class testUserDaoImpl {@Testpublic void testFindAll() {UserDao dao=new UserDaoImpl();dao.findAll();}@Testpublic void testLogin() {UserDao dao=new UserDaoImpl();dao.login("lisi", "9856");}}JDBC中 PreparedStatemnt 相比 Statement 的好處:
大多數,我們使用PreparedStatement代替Statement,好處如下:
1)PreparedStatement是預編譯的,比Statement速度快;
2)使用PreparedStatement,代碼的可讀性和可維護性更強;
3)安全性:PreparedStatement可以防止SQL注入攻擊,而Statement卻不能;例如:
String sql="select * from propuct where name="+name+"and password"+password; 如果我們把['or' 1 '=' 1]作為password傳入進來,因為'1=1'肯定成立,所以任意用戶都可以通過驗證。如果使用預處理編譯語句,傳入的任何內容就不會和原來的語句發生任何匹配的關系; 只要全使用預編譯語句,就用不著對傳入的數據做任何過濾; 而如果使用普通的Statement,有可能要對drop等做費盡心機的判斷和過濾。6. 事務
Transaction 其實指的是一組操作,里面包含許多個單一事務邏輯。只要有一個邏輯沒有執行成功,那么都算失敗,所有的數據都回歸到最初的狀態(回滾)。
- 為什么要有事務?
為了確保邏輯的成功。例子:銀行的轉賬
使用命令行方式演示事務
-
開啟事務
start transaction;
-
提交或者回滾
commit; 提交事務,數據將會寫到磁盤上的數據庫;
rollback;數據回滾,回到最初的狀態;
1). 關閉自動提交功能。
cmd ---> MySQL -u root -p ********** use bank; select * from count;+----+---------+-------+| id | name | money |+----+---------+-------+| 1 | aobama | 1000 || 2 | xierdun | 1000 |+----+---------+-------+ update count set money = money -100 where id = 1;+----+---------+-------+| id | name | money |+----+---------+-------+| 1 | aobama | 900 || 2 | xierdun | 1000 |+----+---------+-------+ show variables like'%commit%';+--------------------------------+-------+| Variable_name | Value |+--------------------------------+-------+| autocommit | ON || innodb_commit_concurrency | 0 || innodb_flush_log_at_trx_commit | 1 |+--------------------------------+-------+ set autocommit = off;+--------------------------------+-------+| Variable_name | Value |+--------------------------------+-------+| autocommit | OFF || innodb_commit_concurrency | 0 || innodb_flush_log_at_trx_commit | 1 |+--------------------------------+-------+2). 演示事務
update count set money = money -100 where id = 1; select * from count;+----+---------+-------+| id | name | money |+----+---------+-------+| 1 | aobama | 800 || 2 | xierdun | 1000 |+----+---------+-------+ 但是,磁盤上的money沒有變化; commit;(commit之后,磁盤上的money就變化了) start transaction; update count set money = money -100 where id =1; select * from acount;+----+---------+-------+| id | name | money |+----+---------+-------+| 1 | aobama | 700 || 2 | xierdun | 1000 |+----+---------+-------+ 但是,磁盤上的money沒有變化; rollback;(rollback之后,磁盤上的money就變化了) select * from count;+----+---------+-------+| id | name | money |+----+---------+-------+| 1 | aobama | 800 || 2 | xierdun | 1000 |+----+---------+-------+使用代碼行方式演示事務
新建一個Java project;然后在Java project下面新建一個folder(命名為lib);然后將connection的jar包導入lib,并右擊build path --> add…;
將之前的jdbc工具包和jdbc.properties復制到src下面,并且在src下面新建一個test包,并在包里新建一個測試類;
右擊項目名,點擊build path --> add library --> JUnit —> JUnit4(導入org.junit.Test);可以在方法上面加@Test(并導入import org.junit.Test;)
注意: 代碼里面的事務,主要是針對于連接的,
1.通過 conn.setAutoCommit(false) 來關閉自動提交的設置;2.提交事務 conn.commit();3.回滾事務 conn.rollback();下面的代碼中,可以發現:因為異常,第二次加錢沒有成功
@Test public void test2(){Connection conn=null;PreparedStatement ps=null;ResultSet rs=null;try {conn = JDBCUtil.getConn();String sql = "update count set money = money -? where id =?";ps = conn.prepareStatement(sql);//扣錢,扣id為1 的100 塊ps.setInt(1, 100);ps.setInt(2, 1);ps.executeUpdate();int a=10/0;//加錢,加id為2 的100 塊ps.setInt(1, -100);ps.setInt(2, 2);ps.executeUpdate();} catch (SQLException e) {e.printStackTrace();}finally{JDBCUtil.release(conn, ps);} }改進上面的代碼,演示事務
@Test public void testTransaction(){Connection conn=null;PreparedStatement ps=null;ResultSet rs=null;try {conn = JDBCUtil.getConn();//連接:事務默認的是自動提交conn.setAutoCommit(false);String sql = "update count set money = money -? where id =?";ps = conn.prepareStatement(sql);//扣錢,扣id為1 的100 塊ps.setInt(1, 100);ps.setInt(2, 1);ps.executeUpdate();int a=10/0;//這里加入一個異常//加錢,加id為2 的100 塊ps.setInt(1, -100);ps.setInt(2, 2);ps.executeUpdate();//成功,則提交事務;conn.commit();} catch (SQLException e) {try {conn.rollback();} catch (SQLException e1) {e1.printStackTrace();}e.printStackTrace();}finally{JDBCUtil.release(conn, ps);} }事務的特性(ACID)
- 原子性
指的是:事務中包含的邏輯,不可分割;
- 一致性
指的是:事務執行前后,數據完整性;
- 隔離性
指的是:事務在執行期間不應該受到其他事物的影響;
- 持久性
指的是:事務執行成功,那么事務應該持久保存到磁盤上;
不考慮事務的隔離級別設置,那么會出現下面的安全隱患:
-
讀問題:
- 臟讀
一個事務讀到另外一個事務還未提交的數據;
- 不可重復讀
一個事務讀到了另外一個事務提交的數據,造成了前后兩次查詢結果不一致;
- 幻讀
一個事務讀到另外一個事務已提交的插入的數據,導致多次查詢結果不一致;
-
寫問題:
- 丟失更新
理解(見圖)
事務的隔離級別
MySQL默認的隔離級別是 可重復讀 ;Oracle默認的隔離級別是 讀已提交;
- read uncommitted[讀未提交]
指的是:一個事務可以讀到另一個事務還未提交的數據。
這會引發"臟讀":(讀取到的是數據庫內存中的數據,而并非真正磁盤上的數據)
- read committed[讀已提交]
指的是:只能讀取到其他事務已經提交的數據,那些沒有提交的數據是讀不出來的。
這解決了"臟讀"問題,但是會引發另一個問題"不可重復讀":(前后讀到的結果不一樣)
- repeatable read[重復讀]----MySQL默認的隔離級別
該隔離級別,可以讓事務在自己的會話中重復讀取數據,并且不會出現結果不一樣的狀況,即使其他事務已經提交了,也依然還是顯示以前的數據(不會因為其他事務已經提交了,而改變)
- serializable[可串行化]
如果有一個連接的隔離級別設置為可串行化,那么誰先打開了事務,誰就有了先執行的權利;誰后打開事務,誰就只能等著,等前面的那個事務提交或者回滾后,才能執行。
但是這種隔離級別一般比較少用,因為容易造成性能上的問題,效率比較低;
該事務隔離級別是最高級別的事務級別了。比前面幾個都要強大一些,也就是前面的幾種問題【臟讀、不可重復讀、幻讀】都能解決,但是效率上有一些缺陷。
按效率劃分,從高到低:
read uncommitted > read committed > repeatable read > serializable
按攔截程度劃分,從高到低
realizable > repeatable read > read committed > read uncommitted
讀未提交 演示
1). cmd打開兩個MySQL連接;設置A窗口的隔離級別為 讀未提交;
//查看當前連接的隔離級別select @@tx_isolation;+-----------------+| @@tx_isolation |+-----------------+| REPEATABLE-READ |+-----------------+ //設置事務A的隔離級別為read committed set session transaction isolation level read committed; select @@tx_isolation;+----------------+| @@tx_isolation |+----------------+| READ-COMMITTED |+----------------+2). 兩個事務(A、B)都分別開啟事務;
start transaction;讀已提交 演示
這個隔離級別 能夠屏蔽"臟讀"的現象,但引發了另一個問題:“不可重復讀”;
解決丟失更新
-
悲觀鎖(for update)
可以在查詢的時候,加入 for update 加鎖:
select * from count for update;
但是,實際開發中,加鎖的操作是很浪費資源的,因此,出現了樂觀鎖。
分析(見圖):
-
樂觀鎖
要求程序員自己控制;
應用場景:買家不是特別多,沖突的機會不多的情況下,可以使用樂觀鎖;(在沖突機會多的情況下,如:網上搶票,用樂觀鎖是不可以的)
分析(見圖):
7. 數據庫連接池
主要用于管理數據庫連接(詳情見圖)
1.數據庫的連接對象創建工作,比較消耗性能;
2.一開始先在內存中開辟一塊空間(集合),往池子里面放置多個連接對象;后面需要連接的話,直接從池子里取,不需要再自己去創建連接了;使用完畢,要記得歸還連接,確保連接對象能循環利用。
簡單搭建
DataSource 是數據庫連接池接口
1). 新建一個Java project(ConnectionPool),并導入connection的jar包,并且add build path;
2). 復制jdbc的工具包和jdbc.properties到src下;
3). 然后新建一個類,負責搭建一個數據庫連接池, 如下:
新建一個數據庫連接池的代碼(MyDataSource.java):
簡單使用
4). 建立一個測試類:來從連接池取一個連接使用;
public class TestPool {@Testpublic void testPool(){Connection conn =null;PreparedStatement ps =null;MyDataSource dataSource=new MyDataSource();try {conn = dataSource.getConnection();String sql="insert into count values (null, ?, ?)";ps = conn.prepareStatement(sql);ps.setString(1, "lisi");ps.setInt(2, 100);ps.executeUpdate();} catch (SQLException e) {e.printStackTrace();}finally{try {ps.close();} catch (SQLException e) {e.printStackTrace();}//歸還連接dataSource.addBack(conn);}} }自定義數據庫連接池
-
代碼實現(如上)
-
出現的問題:
/* 問題:
sun公司針對數據庫連接池定義的一套規范(DataSource接口)
1.需要額外記住 addBack()方法;
2.單例;
3.無法面向接口編程:
UserDao dao=new UserDaoImp1();//編譯看左邊,運行看右邊
dao.insert();
MyDataSource dataSource=new MyDataSource();
//如果把前面的 MyDataSource 換成 DataSource,
//那么后面的dataSource.addBack(conn);編譯就不能通過;
//因為接口里面沒有定義addBack()方法。
4.怎么解決? 以addBack()為切入點
* /
自定義數據庫連接池出現的問題
1)由于多了一個addBack()方法,所以使用這個連接池的地方,需要額外記住這個方法,并且還不能面向接口編程;
2)我們打算修改接口中的close()方法。原來的Connection對象的close()方法,是真的關閉連接;
3)打算修改close()方法,以后再調用close(),并不是真的關閉,而是歸還連接對象;
如何擴展某一個方法? ( 原有的方法邏輯,不是我們想要的,想修改自己的邏輯 );方法1:直接改源碼 ----> 無法實現方法2:繼承(重寫方法): 必須得知道這個接口(Connection)的具體實現類是誰,但是我們不知道方法3:使用裝飾者模式方法4:動態代理裝飾者模式(例子)
1). 新建一個Java project(WraperDemo),在src下面新建一個接口(Waiter),并新建兩個實現類(第二個實現類是第一個實現類的包裝類)
接口Waiter.java代碼如下:
public interface Waiter {void service(); }實現類Waitress.java代碼如下:
public class Waitress implements Waiter {public void service() {System.out.println("在服務-------");} }包裝類WaitressWrap.java代碼如下:
public class WaitressWrap implements Waiter{Waiter waiter=null;public WaitressWrap(Waiter waiter) {this.waiter=waiter;}public void service() {System.out.println("。。。微笑。。。");waiter.service();} }2). 新建一個測試類
public class MainTest {public static void main(String[] args) {/*Waiter waiter=new Waitress();waiter.service();*/WaitressWrap waitressWrap=new WaitressWrap(new Waitress());waitressWrap.service();} }- 題外話—面向接口編程(好處)
接口:[領導]規定需要的一些方法,具體方法的實現可以由[下屬]來寫實現類;
接口名 dao = new 實現類名(); UserDao dao = new UserDaoImp(); dao.insert();- 裝飾者模式(解決自定義數據庫連接池出現的問題)
修改上面的新建數據庫連接池的MyDataSource.java代碼:
public class MyDataSource implements DataSource {List<Connection> list=new ArrayList<Connection>();public MyDataSource() {for (int i = 0; i < 10; i++) {Connection conn = JDBCUtil.getConn();list.add(conn);}}//該連接池對外公布的獲取連接的方法public Connection getConnection() throws SQLException {//來拿連接的時候,先看看池子里面還有沒有連接;if(list.size()==0){for (int i = 0; i < 5; i++) {Connection conn = JDBCUtil.getConn();list.add(conn);}}//remove(0)---->移除第一個(移除的是集合中的第一個)Connection conn = list.remove(0);//把這個連接對象拋出去的時候,對這個對象進行包裝Connection connection = new ConnectionWrap(conn, list);return connection;}/*** 用完連接后,記得歸還* @param conn*//*public void addBack(Connection conn){list.add(conn);}*/Connection的包裝類(ConectionWrap.java)代碼修改如下:
public class ConnectionWrap implements Connection {Connection connection=null;List<Connection> list;public ConnectionWrap(Connection connectionon,List<Connection> list) {super();this.connection = connectionon;this.list = list;}public void close() throws SQLException {//connection.close();System.out.println("有人來歸還連接對象了,歸還前,數據庫連接池中的連接數量是"+list.size());list.add(connection);System.out.println("有人來歸還連接對象了,歸還后。。。,數據庫連接池中的連接數量是"+list.size());}public PreparedStatement prepareStatement(String sql) throws SQLException {return connection.prepareStatement(sql);}測試類的代碼(TestPool.java)修改如下:
public class TestPool {@Testpublic void testPool(){Connection conn =null;PreparedStatement ps =null;MyDataSource dataSource=new MyDataSource();try {conn = dataSource.getConnection();String sql="insert into count values (null, ?, ?)";ps = conn.prepareStatement(sql);ps.setString(1, "lisi");ps.setInt(2, 100);ps.executeUpdate();} catch (SQLException e) {e.printStackTrace();}finally{//歸還連接//dataSource.addBack(conn);JDBCUtil.release(conn, ps);}} }開源數據庫連接池:DBCP & C3P0
用來處理:連接、管理、創建、回收
DBCP(DataBase Connection Pool)
DBCP是數據庫連接池,是Java數據庫連接池中的一種,由Apache開發,通過數據庫連接池,也可以讓程序自動管理數據庫連接的釋放和斷開;
1). 導入jar包 commons-dbcp.jar和commons-pool.jar;
2). 代碼連接(不使用配置文件方式)
DataSource是數據庫連接池的開源接口;BasicDataSource實現了接口DataSource;
@Test public void testDBCP(){Connection conn =null;PreparedStatement ps =null;try {//1.構建數據源對象BasicDataSource dataSource=new BasicDataSource();//2.連的是什么類型的數據庫,訪問的是哪個數據庫,用戶名,密碼//jdbc:mysql://localhost/bank 主協議:子協議://本地/數據庫名dataSource.setDriverClassName("com.mysql.jdbc.Driver");dataSource.setUrl("jdbc:mysql://localhost/bank");dataSource.setUsername("root");dataSource.setPassword("hxzgnpy123");//3.得到連接對象conn = dataSource.getConnection();String sql ="insert into count values (null, ?, ?)";ps = conn.prepareStatement(sql);ps.setString(1, "zhangsan");ps.setInt(2, 500);ps.executeUpdate();} catch (Exception e) {e.printStackTrace();}finally{try {ps.close();conn.close();} catch (SQLException e) {e.printStackTrace();}} }3). 代碼連接(配置文件方式)
BasicDataSourceFactory類實現了ObjectFactory接口,方法有creatDataSource(Properties properties)
@Test public void testDBCP02(){Connection conn =null;PreparedStatement ps =null;try {BasicDataSourceFactory factory=new BasicDataSourceFactory();Properties properties=new Properties();InputStream is=new FileInputStream("src//jdbc.properties");properties.load(is);BasicDataSource dataSource = factory.createDataSource(properties);//2.得到連接對象conn = dataSource.getConnection();String sql ="insert into count values (null, ?, ?)";ps = conn.preparedStatement(sql);ps.setString(1, "zhangsan");ps.setInt(2, 500);ps.executeUpdate();} catch (Exception e) {e.printStackTrace();}finally{try {ps.close();conn.close();} catch (SQLException e) {e.printStackTrace();}} }C3P0(***)
C3P0是一個開源的JDBC連接池,它實現了數據源和JNDI綁定,支持JDBC3規范和JDBC2的標準擴展。
目前使用它的開源項目有Hibernate,Spring等。
1). 導入jar包— c3p0-0.9.5.2.jar
2). 代碼連接(不使用配置文件方式)
@Test public void test01(){Connection conn =null;PreparedStatement ps =null;try {ComboPooledDataSource dataSource=new ComboPooledDataSource();dataSource.setDriverClass("com.mysql.jdbc.Driver");dataSource.setJdbcUrl("jdbc:mysql://localhost/bank");dataSource.setUser("root");dataSource.setPassword("*****");conn = dataSource.getConnection();String sql="insert into count values (null, ?, ?)";ps = conn.prepareStatement(sql);ps.setString(1, "KAIKAI");ps.setInt(2, 300);ps.executeUpdate();} catch (Exception e) {e.printStackTrace();}finally{try {ps.close();conn.close();} catch (Exception e) {e.printStackTrace();}} }配置文件是支持兩種方式:c3p0.properities 和 c3p0-config.xml(后一種比較常用,注意,文件名一定要是c3p0-config.xml,comboPooledDataSource類的源碼中由類加載器自動加載這個文件,所以不能改名)
1). 創建在src 下面創建 c3p0-config.xml:
<?xml version="1.0" encoding="UTF-8"?> <c3p0-config><!-- 默認的數據庫 --><default-config><property name="driverClass">com.mysql.jdbc.Driver</property><property name="jdbcUrl">jdbc:mysql://localhost/bank</property><property name="user">root</property><property name="password">*****</property><property name="initialPoolSize">10</property><property name="maxIdleTime">30</property><property name="maxPoolSize">100</property><property name="minPoolSize">10</property><property name="maxStatements">200</property><!-- <user-overrides user="test-user"><property name="maxPoolSize">10</property><property name="minPoolSize">1</property><property name="maxStatements">0</property></user-overrides> --> </default-config><!-- 這里可以通過設置name的值,來控制什么類型的數據庫;例如這里可以令name="oracle". This app is massive ! --> <!-- This app is massive ! --> <!-- <named-config name="intergalactoApp"><property name="acquireIncrement">50</property><property name="initialPoolSize">100</property><property name="mixPoolSize">50</property><property name="maxPoolSize">1000</property> --><!-- intergalactoApp adopts a different approach to configuring statement caching --><!-- <property name="maxStatements">0</property><property name="maxStatementsPerConnection">5</property>--><!-- he's important, but there's only one of him --><!-- <user-overrides user="master-of-the-universe"><property name="acquireIncrement">1</property><property name="initialPoolSize">1</property><property name="mixPoolSize">1</property><property name="maxPoolSize">5</property><property name="maxStatementsPerConnection">50</property></user-overrides> </named-config>--> </c3p0-config>2)C3P0Demo.java的代碼:
public class C3P0Demo02 { @Test public void test02(){Connection conn =null;PreparedStatement ps =null;try {//就只是new了一個對象,默認會找xml中的<default-config>ComboPooledDataSource dataSource=new ComboPooledDataSource();//也可以是別的,比如之后連接oracle數據庫時;可以通過設置<named-config name="oracle">//但是,一般一個應用只用一個數據庫//ComboPooledDataSource dataSource2=new ComboPooledDataSource("oracle");conn = dataSource.getConnection();String sql="insert into count values (null, ?, ?)";ps = conn.prepareStatement(sql);ps.setString(1, "KAIKAI");ps.setInt(2, 300);ps.executeUpdate();} catch (Exception e) {e.printStackTrace();}finally{try {ps.close();conn.close();} catch (Exception e) {e.printStackTrace();}} } }注:(修改JDBCUtil.java為C3P0的形式,如下:
之后,JDBCUtil.java 可以向之前一樣使用
8. mysql 數據庫的默認的最大連接數是:
為什么需要最大連接數?
特定服務器上面的數據庫只能支持一定數目同時連接,這時候我們設置最大連接數(即最多同時服務多少連接),在數據庫安裝時都會有一個默認的最大連接數。
可以在MySQL安裝的默認路徑中,可以找到配置文件 my.ini,打開后,查找max,就能知道MySQL的默認最大連接數。max_connections=100實際工作中,一般都會設置更大的最大連接數。9. 簡單說一下MySQL的分頁、oracle的分頁:
為什么需要分頁?(在很多數據中,不可能完全顯示數據,需要進行分段顯示)
1)MySQL是使用關鍵字limit來進行分頁的:limit offset,size表示從多少索引去多少位;
String sql="select * from product order by pid limit"+pageSize*(pageNumber-1)+","+pageSize;2)oracle的分頁,思路是:要使用三層嵌套查詢;具體記不住,如果在工作中使用,可以到原來的項目中拷貝、或者上網查詢;
String sql="select * from"+(select *,rownum rid from (select * from students order by postime desc) where rid <="+pageSize*pageNumber+") as t"+"where t>" + pageSize*(pageNumber-1);10. 觸發器的使用場景:
觸發器,需要有觸發條件,當條件滿足以后做什么操作;
觸發器的使用場景有很多,比如:校內網、開心網、Facebook,具體地:你發一個日志,自動通知好友,其實就是在增加日志時做一個后觸發,再向通知表中寫入條目,因為觸發器效率高。(而UCH沒有觸發器,效率和數據處理能力都很低)。
例如:每插入一個帖子,都希望將版面表中的最后發帖時間、帖子總數字段進行同步更新,用觸發器做效率就很高。
create table board1 (id int primary key auto_increment,name varchar(50),articleCount int );creat table article1(id int primary key auto_increment,title varchar(50),bid int references board1(id) );delimiter| //把分隔符;改為|create tigger insertArticle_Trigger after insert on article1 for each row beginupdate board1 set articleCount=articleCount+1 where id=NEW.bid; end;|delimiter;insert into board1 value(null,'test',0); insert into article1 value(null,'test',1);11. 數據庫的存儲過程的優點:
1)存儲過程只在創建時進行編譯,以后每次執行存儲過程都不需要再重新編譯,而一般SQL語句每執行一次就編譯一次,因此,使用存儲過程可以大大提高數據庫執行速度;
2)通常,復雜的業務邏輯需要多條SQL語句,這些語句要分別地從客戶機發送到服務器,當客戶機和服務器之間的操作很多時,將產生大量的網絡傳輸,如果將這些操作放在一個存儲過程中,那么客戶機和服務器之間的網絡傳輸就會大大減少,降低了網絡負載;
3)存儲過程創建一次便可以重復使用,從而可以減少數據庫開發人員的工作量;
4)安全性高,存儲過程可以屏蔽對底層數據庫對象的直接訪問,使用execute權限調用存儲過程,無需擁有訪問底層數據庫對象的顯示權限。
目前常用的數據庫都支持存儲過程,例如:SQLServer、Oracle、Access等,開源數據庫系統MySQL在5.0的時候實現了對存儲過程的支持
定義存儲過程: create procedure insert_Student(_name varchar(50),_age int,out_id int ); begininsert into student value(null,_name,_age);select max(stuId) into _id from student; end;call insert_Student('wfz',23,@id); select @id;12.用JDBC怎么調用存儲過程?
(賈璉欲執事) 加載驅動 獲取連接 設置參數 執行 釋放連接Class.forName("com.mysql.jdbc.Driver"); conn=DriverManager.getConnection("jdbc:mysql:///test","root","root"); cstmt = conn.prepareCall("{call insert_Student (?,?,?)}"); catmt.registerOutParameter(3,Types.INTEGER); catmt.setString(1,"wangwu"); catmt.setInt(2,25); catmt.execute();13. 數據庫連接池的作用:
1)限定數據庫連接的個數,不會導致由于數據庫連接過多導致系統運行緩慢或崩潰;
2)數據庫連接不需要每次都去創建或者銷毀,節約了資源;
3)數據庫連接不需要每次都去創建,響應時間更快。
14. MySQL數據庫優化方面的事情:
定位:查詢、定位慢查詢,并優化;
優化手段:(其他數據庫類似)
a. 創建索引:(創建合適的索引,我們就可以利用索引查詢,查詢到以后,直接找對應的記錄)
b. 分表:(當一張表的數據比較多、或者一張表的某些字段的值比較多并且很少使用時,采用水平分表和垂直分表來優化)
c. 讀寫分離:(當一臺服務器不能滿足需求時,采用讀寫分離的方式進行集群)
d. 緩存:(使用redis來進行緩存)
e. 一些常用的優化技巧
如何查找慢查詢、并定位慢查詢?
在項目自驗項目轉測試之前,在啟動MySQL數據庫時開啟慢查詢,并且把執行慢的語句寫到日志中,在運行一定時間后,通過查看日志找到慢查詢語句。
要找出項目中的慢sql時: 1)關閉數據庫服務器(關閉服務); 2)把慢查詢記錄到日志中; 3)設置慢查詢時間:set global long_query_time=1; 4)找出日志中的慢查詢SQL.使用explain 慢查詢語句,來詳細分析語句的問題
mysql> explain select * from 數據庫名稱 where 列名='aaa'\G注: 數據庫優化,數據庫表設計時需要遵循:
三范式(列不可分割、主鍵唯一,外鍵)、反三范式(特殊情況,允許依賴)
選擇合適的數據庫存儲引擎:
在開發過程中,我們經常使用的存儲引擎有:myisam、innodb、memory
1)myisam存儲引擎:
如果表對事務的要求不高,同時是以查詢和添加為主的,我們考慮使用myisam存儲引擎;比如:bbs中的發帖表、回復表。
2)innodb存儲引擎:
如果表對事務的要求高,保存的數據都是重要數據;我們建議使用innodb存儲引擎;比如:訂單表、賬號表。
3)memory存儲引擎:
如果數據變化頻繁、不需要入庫,同時又需要頻繁的查詢和修改;我們考慮使用memory存儲引擎,速度極快。
mysql數據庫中使用的是myisam、innodb存儲引擎,它們的主要區別是:
(1)事務安全(myisam不支持事務,innodb支持);
(2)查詢和添加速度(myisam不支持事務,就不需要考慮同步鎖,所以查找和添加數據的速度快);
(3)支持全文索引(myisam支持全文索引,但是innodb不支持);
(4)鎖機制(myisam只支持表鎖,而innodb支持行鎖);
(5)外鍵(myisam不支持外鍵,innodb支持外鍵)
選擇合適的索引:
索引(index)是幫助DBMS高效獲取數據的數據結構;
索引的分類:普通索引、唯一索引、主鍵索引、全文索引,具體如下:
普通索引:允許重復的值出現;
唯一索引:處理不允許重復值的記錄外,其他和普通索引一樣(例如:用戶名、用戶身份證等);
主鍵索引:唯一且沒有null值;主鍵索引隨著設定主鍵而創建,也就是把某個列設為主鍵的時候,數據庫就會給該列創建索引;
全文索引:用來對表中的文本域(char、varchar、text)進行索引,全文索引針對myisam存儲引擎;
explain select * from 數據庫名稱 where match (title,body) against(‘database’);//會使用全文索引
索引使用的小技巧:
索引的弊端:
a. 占用磁盤空間
b. 對dml(插入、修改、刪除)操作有影響,變慢
索引的使用場景:
a. 肯定在where條件中經常使用,如果不做查詢就沒有意義
b. 該字段的內容不是唯一的幾個值
c. 字段內容不會頻繁變化
索引使用的具體技巧:
a. 對于創建的多列索引(復合索引),不是使用的第一部分就不會使用索引;
alter table 表名 add index 索引名 (左邊的列名,右邊的列名);
explain select * from 表名 where 左邊的列名=‘aaa’\G;\會使用到索引
explain select * from 表名 where 右邊的列名=‘aaa’\G; \就不會使用到索引
b. 對于使用like的查詢,查詢如果是’%aaa’,就不會使用到索引;而’aaa%‘會使用到索引;
explain select * from 表名 where 列名=’%aaa’\G;\不會使用到索引
explain select * from 表名 where 列名=‘aaa%’\G; \會使用到索引
因此,在like查詢的時候,'關鍵字’的最前面不能使用 % 或 _ 這樣的字符,如果一定要前面有變化的值,則考慮使用全文索引 sphinx。
c. 如果條件中有or,有條件沒有使用索引,即使其中有條件帶索引也不會使用。換言之,就是要求使用的所有字段,都必須單獨使用時能使用索引;
d. 如果列類型是字符串,那一定要在條件中將數據使用引號引用起來,否則不使用索引;
e. 如果mysql估計使用全表掃描要比使用索引快,則不使用索引。例如:表里面只有一條記錄。
分表:
分表分為:水平分表(按行)、垂直分表(按列)
水平分表:就是按行分表,一般情況下,**表數據達到百萬級(行很多)**時,查詢效率會很低,容易造成表鎖,甚至堆積很多鏈接,直接掛掉。這種情況下,水平分表能夠很大程度上減少這些壓力;
垂直分表:就是按列分表,如果一張表中的某個字段值非常長(如長文本、二進制等),并且只在很少情況下會查詢;這是就可以把這個字段單獨放到一個表中,可以通過外鍵關聯起來。比如:考試詳情,字段很長,并且一般我們只會關注開始成績,此時可以采用垂直分表。
水平分表的策略:(常用按區間范圍分表、hash分表)
a. 按時間分表: 這種分表方式有一定的局限性,數據具有較強的時效性,例如:微博發送記錄、微信消息記錄等,這種數據很少有用戶會查詢幾個月以前的數據,這種數據就可以按月分表;
b. 按區間范圍分表: 一般在有嚴格的自增id需求上,例如:按照user_id水平分表,如下:
table_1 user_id從1~100w
table_2 user_id從101~200w
table_3 user_id從201~300w
c. hash分表:通過一個原始目標的ID或者名稱、通過一定的hash算法,算出數據存儲表的表名,然后訪問相應的表。
讀寫分離:
由于一臺數據庫支持的最大并發連接數是有限的,如果用戶并發訪問太多,并且一臺服務器滿足不了要求時,就可以集群處理。mysql的集群處理技術最常用的就是讀寫分離。
讀寫分離的具體操作:
(1)主從分離:數據庫最終會把數據持久化到磁盤上,如果集群處理必須確保每個數據庫服務器的數據都是一致的,因此,將能改變數據庫數據的操作都往主數據庫去寫,而其他的數據庫從主數據庫上同步數據。
(2)讀寫分離:使用負載均衡來實現寫的操作都往主數據庫服務器去,而讀的操作都往從數據庫服務器去。
緩存:(最經典的優化技術)
在持久層(dao)和數據庫(db)之間添加一個緩存層,如果用戶訪問的數據已經緩存起來時,在用戶訪問時直接從緩存中獲取,不用再訪問數據庫,而緩存是在操作內存,訪問速度快。
緩存的作用:減少數據庫服務器壓力,減少訪問時間。
Java中常用的緩存有:
a. hibernate的二級緩存,該緩存不能完成分布式緩存;
b. 可以使用redis、memcahe作為中央緩存;對緩存的數據進行集中處理
sql語句優化小技巧:(DDL/DML優化比較常用)
DDL優化:
a. 通過禁用索引來提供導入數據性能,這個操作主要針對有數據庫的表,追加數據;//去除鍵alter table 表名 DISABLE keys;//批量插入數據insert into 表名1 select * from 表名2;//恢復鍵alter table 表名 ENABLE keys;b. 關閉唯一校驗:set unique_checks=0 //關閉set unique_checks=1 //開啟c. 修改事務提交方式(導入):(變多次提交為一次)set autocommit=0 //關閉//批量插入set autocommit=1 //開啟DML優化:(變多次提交為一次)
insert into 表名 values(1,2); insert into 表名 values(1,3); insert into 表名 values(1,4); //合并多條為一條 insert into 表名 values(1,2),(1,3),(1,4);DQL優化:
order by 優化:a. 多用索引排序b. 普通結果排序(非索引排序)Filesortgroup by 優化:是使用order by null,取消默認排序;子查詢優化://在'客戶列表'找到不在'支付列表'的客戶,查詢沒買過東西的客戶explainselect * from customer where customer_id not in (select DISTINCT customer_id from payment);//這種是基于func外鏈explainselect * from customer c left join payment p on (c.customer_id=p.customer_id) where p.customer_id is null;//這種是基于'索引'外鏈or 優化:在兩個獨立索引上使用 or 的性能優于:a. or 兩邊都是用索引字段做判斷,性能好!b. or兩邊,有一邊不用,性能差;c. 如果employee表的name和email這兩列是一個復合索引,但是如果是:name='A' or email='B' 這種方式,不會用到索引limit 優化:select film_id,description from film order by title limit 50,5;select a.film_id,a.description from file a inner join (select film_id from film order by title limit 50,5) b on a.film_id = b.film_id;JDBC批量插入幾百萬條數據怎么實現?(***)
原理:變多次提交為一次,使用批量操作
conn=DriverManager.getConnection(url,user,password);conn.setAutoCommit(false);//(1)String sql="insert into product (id,name) values (?,?)"; ps=conn.preparedStatement(sql); for(int i=1;i<count;i+=batch_size){ps.clearBatch();//(2)for(int j=0;j<batch_size;j++){ps.setInt(1,i+j);ps.setString(2,data);ps.addBatch();//(3)}ps.executeBatch();//(4)if((i+batch_size-1)%commit_size==0){conn.commit();//(5)} }省出的時間可觀。
像這樣的批量插入操作能不使用代碼操作就不使用,可以使用存儲過程來實現。
15. Redis
簡單介紹Redis:
Redis是一個key-value的nosql數據庫;
(好處):先存到內存中,會根據一定的策略持久化到磁盤,即使斷電也不會丟失數據,支持的數據類型比較多。
(作用):redis主要用來緩存數據庫的數據;web集群時,redis可以當做中央緩存存放session;
mysql、redis、memcache的比較:
| 類型 | 關系型 | 非關系型 | 非關系型 |
| 存儲位置 | 磁盤 | 磁盤和內存 | 內存 |
| 存儲過期 | 不支持 | 支持 | 支持 |
| 讀寫性能 | 低 | 非常高 | 非常高 |
1)redis和memcache都是把數據存放在內存中,不過memcache還可以用于緩存其他東西,例如:圖片、視頻等;
2)redis不僅支持簡單的key-value類型的數據,同時還提供list、set、hash等數據結構的存儲;
3)虛擬存儲—redis當物理內存用完時,可以將一些很久沒用的value交換到磁盤。
Redis的使用場景:
1)作為緩存:把經常需要查詢、很少修改的數據;(redis是內存級別的,因此訪問時間很快)
2)作為計數器:redis中提供了一種計數器是原子性的內存操作;(最簡單的例子:可以解決庫存溢出問題)
3)session緩存服務器(中央緩存):web集群時,redis可以作為session緩存服務器;
具體地:當一臺web服務器不能滿足需求時,需要提供多臺web服務器;作為集群,需要在前面增加一個負載均衡,負責將請求均衡的分配到多臺web服務器上面;每臺web服務器都有自己獨立的session,并有一個專門管理session的區域;導致存在一個web服務器上的數據,另一個服務器不知道;因此,在服務器和數據庫之間增加一個中央緩存(redis)。
redis存儲對象的方式:(經常使用的是json字符串)
1)json字符串:需要把對象轉換為json字符串,當做字符串處理,直接使用set、get來設置或者獲取;
優點:設置和獲取比較簡單
缺點:沒有提供專門的方法,需要把對象轉換為json字符串(直接使用jsonlib,就可以實現);
2)字節流:需要做序列化,就是把對象序列化為字節保存。
如果是存儲百萬級的大數據對象,建議采用存儲序列化對象方式;如果是少量的數據對象、或者是數據對象字段不多,還是建議采用json轉換成字符串方式
redis數據淘汰機制:
由于內存大小有限,需要保存有效的數據,就需要淘汰一些暫時不用的數據;
redis提供了6種數據淘汰機制:
volatile-lru:從已設置過期時間的數據集中挑選最近最少使用的數據淘汰;
volatile-ttl:從已設置過期時間的數據集中挑選將要過期的數據淘汰;
volatile-random:從已設置過期時間的數據集中任意選擇數據淘汰;
allkeys-lru:從數據集中挑選最近最少使用的數據淘汰;
allkeys-random:從數據集中任意選擇數據淘汰;
no-enviction:禁止驅逐數據
Java訪問redis的方式:
1)使用jedis Java客戶端來訪問redis服務器(類似于通過jdbc訪問mysql);
2)如果是spring進行集成時,可以使用spring data來訪問redis;spring data只是對jedis的二次封裝(類似于jdbcTemplate和jdbc的關系);
redis集群:
當一臺數據庫無法滿足要求時,可以使用redis集群來處理,類似于mysql的讀寫分離。
總結
以上是生活随笔為你收集整理的Mysql数据库读书笔记的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 金融开发术语
- 下一篇: ERROR [KafkaApi-1] N