首页
关于
友链
Search
1
wlop 4K 壁纸 4k8k 动态 壁纸
1,517 阅读
2
Nacos持久化MySQL问题-解决方案
963 阅读
3
Docker搭建Typecho博客
765 阅读
4
滑动时间窗口算法
750 阅读
5
Nginx反向代理微服务配置
717 阅读
生活
解决方案
JAVA基础
JVM
多线程
开源框架
数据库
前端
分布式
框架整合
中间件
容器部署
设计模式
数据结构与算法
安全
开发工具
百度网盘
天翼网盘
阿里网盘
登录
Search
标签搜索
java
javase
docker
java8
springboot
thread
spring
分布式
mysql
锁
linux
redis
源码
typecho
centos
git
map
RabbitMQ
lambda
stream
少年
累计撰写
189
篇文章
累计收到
24
条评论
首页
栏目
生活
解决方案
JAVA基础
JVM
多线程
开源框架
数据库
前端
分布式
框架整合
中间件
容器部署
设计模式
数据结构与算法
安全
开发工具
百度网盘
天翼网盘
阿里网盘
页面
关于
友链
搜索到
16
篇与
的结果
2022-12-11
mysql-5.7.33-winx64.zip安装
mysql-5.7.33-winx64.zip安装一、解压到安装目录二、编写配置文件my.ini将编写好的配置文件放到mysql-5.7.33-winx64根目录下。配置文件如下:[mysql] # 设置mysql客户端默认字符集 default-character-set=utf8 [mysqld] # 设置3306端口 port = 3306 # 设置mysql的安装目录 basedir = D:\\tools\mysql-5.7.33-winx64 # 设置mysql数据库的数据的存放目录 datadir = D:\\tools\mysql-5.7.33-winx64\data # 允许最大连接数 max_connections=20 # 服务端使用的字符集默认为8比特编码的latin1字符集 character-set-server=utf8 # 创建新表时将使用的默认存储引擎 default-storage-engine=INNODB注意:1、修改自己安装路径 2、使用管理员权限执行下面的安装命令三、执行安装脚本使用管理员权限打开cmd命令工具,进入D:\tools\mysql-5.7.33-winx64\bin目录。执行安装:1、初始化,生成data文件夹.(建议使用,不设置root密码)mysqld --initialize-insecure 或者 mysqld --initialize(不建议使用,生成一个随机的root密码)2、安装MySql服务mysqld -install3、启动服务net start mysql4、设置mysql密码使用空密码进入mysql数据库,设置密码。set password for root@localhost = password('helloworld');5、环境变量配置打开电脑环境变量配置系统环境,PATH环境变量下,新增bing目录D:\tools\mysql-5.7.33-winx64\bin
2022年12月11日
172 阅读
1 评论
2 点赞
2022-12-08
Oracle 存储过程和函数
Oracle 存储过程和函数函数一般是工具性的东西。存储过程一般是一些操作。一、Oracle 自定义函数语法:Create function 函数名称 return 返回值类型 as Begin ... End 函数名称;示例:统计t_book记录数create or replace function getbookCount return number as begin declare bookNum number; begin select count(*) into bookNum from t_book; return bookNum; end; end getbookCount;函数调用set serveroutput on begin dbms_output.put_line('t_book表有:' || getbookCount() || '条记录'); end;输出结果:带参数的函数示例:create or replace function getBookSum(table_name varchar2) return number as begin declare sumCount number; count_sql varchar2(200); begin count_sql:='select count(*) from ' || table_name; execute immediate count_sql into sumCount; return sumCount; end; end getBookSum;带参函数调用set serveroutput on begin dbms_output.put_line('表有:' || getBookSum('t_booktype') || '条记录'); end;输出结果:二、Oracle 存储过程语法:Create procedure 存储过程名称 as Begin ... End 存储过程名称; In 只进不出 Out 只出不进 In out 可进可出参数In 只进不出示例:向t_book中添加数据。create or replace procedure addBook(bookname in varchar2, booktypeId in number,price in number) as begin declare maxId number; begin select max(id) into maxId from t_book; insert into t_book(id,bookname,type_id,price) values(maxId+1, bookname,booktypeId,price); commit;--自动提交 end; end addBook;存储过程调用execute 存储过程名称(参数1,参数2.。。);示例1:execute addBook('代码简洁之道',2,66);查询t_book结果:示例2:如果存在就不添加,没有记录才添加。create or replace procedure addCheckBook(boookn in varchar2, booktypeId in number,price in number) as begin declare maxId number; n number; begin select count(*) into n from t_book where bookname=boookn; if (n>0) then return; end if; select max(id) into maxId from t_book; insert into t_book(id,bookname,type_id,price) values(maxId+1, boookn,booktypeId,price); commit; end; end addCheckBook;调用存储过程execute addCheckBook('代码简洁之道2',2,66);此时已经存在,不在向t_book中添加记录。Out 只出不进示例1:返回添加记录前和添加记录后的总条数create or replace procedure addCheckBookCount(boookn in varchar2, booktypeId in number,price in number, befern out number, aftern out number) as begin declare maxId number; n number; begin select count(*) into befern from t_book; select count(*) into n from t_book where bookname=boookn; if (n>0) then return; end if; select max(id) into maxId from t_book; insert into t_book(id,bookname,type_id,price) values(maxId+1, boookn,booktypeId,price); select count(*) into aftern from t_book; commit; end; end addCheckBookCount;执行存储过程:execute用于执行单据。这里调用存储过程,需要单独调用:declare befern number; aftern number; begin addCheckBookCount('代码简洁之道3',2,66,befern,aftern); dbms_output.put_line('新增前条数:' || befern || '新增后条数:' ||aftern); end;输出结果:三、存储过程Debug1、打开调试,右键 Add debug information2、test调试, Test,然后进入调试界面。3、调试界面,输入参数4、开始调试5、查看参数鼠标放到对应参数上,即可显示当前值。四、程序包引入的目的,是为了有效的管理函数和存储过程,当项目模块很多的时候,用程序包管理就很有效了。语法:Create or replace package 包名 as 变量名称1 数据类型1; 变量名称2 数据类型2; ... ... Function 函数名称1(参数列表) return 数据类型1; Function 函数名称2(参数列表) return 数据类型2; ... ... Procedure 存储过程名称1(参数列表); Procedure 存储过程名称2(参数列表); ... ... End 包名;
2022年12月08日
158 阅读
0 评论
2 点赞
2022-12-07
Oracle 触发器
Oracle 触发器具备某些条件,由数据库自动执行的一些DML 操作行为。一、语句触发器语句触发器针对整个表,作用整个表操作。一般用于权限判断之类的。常用行触发器。语法结构Create trigger 触发器名称 Before/after 触发动作 On 作用对象触发器谓词:INSERTING、UPDATING、DELETING示例1:向t_book插入数据之前判断,用户是否有权限执行查询操作。创建语句触发器:create trigger tr_book before insert on t_book begin if user!='hello' then raise_application_error(-20001,'权限不足'); end if; end;执行插入数据:insert into t_book values(6,'ooo',2,68,'男');提示结果:修改触发器:create or replace trigger tr_book before insert on t_book begin if user!='SCOTT' then raise_application_error(-20001,'权限不足'); end if; end;重新执行插入数据即可插入。insert into t_book values(6,'ooo',2,68,'男');查询结果:注意:多个可以用or连接示例:create or replace trigger tr_book before insert or update or delete on t_book begin if user!='hello' then raise_application_error(-20001,'权限不足'); end if; end; before insert or update or delete:插入、修改、删除之前判断是否有权限。进行插入、修改、删除操作:insert into t_book values(7,'occcc',2,88,'男'); delete from t_book where id=1; update t_book set price=89 where id=1;此时都提示:示例2:在对t_book增删改之后记录日志到t_book_log表中。编写行触发器:create or replace trigger tr_book_log after insert or update or delete on t_book begin if inserting then insert into t_book_log values(user, 'insert', sysdate); else if updating then insert into t_book_log values(user, 'upate', sysdate); else if deleting then insert into t_book_log values(user, 'delete', sysdate); end if; end if; end if; end;分别执行增删改操作:insert into t_book values(8,'866',2,88,'男'); delete from t_book where id=8; update t_book set price=88 where id=1;此时t_book_log表中已经有记录了。select * from t_book_log;查询结果:注意:判断增删改用的不是insert、delte、update,而是inserting、updating、deleting。二、行触发器行触发器针对行记录。一般用于记录操作日志。语法结构Create trigger 触发器名称 Before/after 触发动作 For each row On 作用对象只是语句触发器多了一个For each row示例1:当修改t_book增加一条数据时,t_booktype对应的类型名称就改为t_book中新增的书名作为类型名。为插入数据前t_book\t_booktype表数据:编写行触发器:create or replace trigger tr_bookandtype after insert on t_book for each row begin update t_booktype set booktypename=:new.bookname where id=:new.type_id; end;注意:触发器内置变量:old 表示旧行记录。:new 表示新插入行记录。执行插入操作:insert into t_book values(8,'新增sql秘籍',2,55,'男');重新查看2表数据:可以看到,新增数据行8后,对应的typeid=2的类型名称改为书名了。示例2:当删除t_book一条数据后,将t_booktype中对应类型名称,改为被删除t_book记录中书名。create or replace trigger tr_bookdeltype after delete on t_book for each row begin update t_booktype set booktypename=:old.bookname where id=:old.type_id; end;注意:记录被删除了,所以可以用内置对象old.字段,获取删除记录的字段信息。t_book、t_booktype表原始数据:删除操作:delete from t_book where id=7;查看删除后t_book、t_booktype表数据:t_book中id=7记录已被删除。t_booktype中typename=www了。注意:一般行触发器用的较多。三、触发器禁用和开启禁用触发器alter trigger 触发器名称 disable启用触发器alter trigger 触发器名称 enable四、删除触发器drop trigger 触发器名称;五、修改触发器修改和其它修改视图、触发器、存储过程一样。create or replace只需要加上or replace
2022年12月07日
218 阅读
0 评论
2 点赞
2022-12-06
Oracle 游标
一、游标使用游标,我们可以对具体操作数据,比如查询的结果,对行,列数据进行更加细致的处理。以及对其他DML操作进行判断等操作;类似一个数据集合。1、语法declare cursor 游标名称 is 查询的sql;示例:declare cursor cu_book is select bookname,price from t_book;注意:游标默认是关闭的。2、打开右边open 游标名;示例:open cu_book;3、关闭游标close 游标名;示例:close cu_book;4、游标循环fetch 游标名 into 临时值1,临时值2...;循环将游标中的字段保存到临时值中。5、判断游标是否还有记录游标名%found二、示例set serverout on; --定义游标哦 declare cursor cu_book is select bookname,price from t_book; b_name varchar(20); b_price number; begin open cu_book; --打开游标 fetch cu_book into b_name,b_price; --循环游标取值 while cu_book%found loop --如果游标里面还有值就循环处理其它逻辑 dbms_output.put_line('书名:' || b_name || '价格' || b_price); fetch cu_book into b_name,b_price; end loop; close cu_book; end;输出结果:三、注意事项1、定义的游标默认是关闭的。2、while条件注意跳出。3、注意关闭游标。4、loop要有对应的end loop四、显示游标1、显示游标上面定义的临时变量的类型必须和游标里面查询表的字段类型一样,如果表的字段类型改了,定义的临时变量就会报错,类型不一样。因此就出现了显示游标。定义: 临时变量名 表明.字段名%type;上面一中示例改进成显示游标:set serverout on; --定义游标哦 declare cursor cu_book is select bookname,price from t_book; b_name t_book.bookname%type; b_price t_book.price%type; begin open cu_book; --打开游标 fetch cu_book into b_name,b_price; --循环游标取值 while cu_book%found loop --如果游标里面还有值就循环处理其它逻辑 dbms_output.put_line('书名:' || b_name || '价格' || b_price); fetch cu_book into b_name,b_price; end loop; close cu_book; end;输出结果:2、字段类型表示表中字段数据类型。%type3、行类型表示表中一行数据类型。%rowtype示例:set serverout on; --定义游标哦 declare cursor cu_book is select * from t_book; book t_book%rowtype; -- 行类型 begin open cu_book; --打开游标 fetch cu_book into book; --循环游标取值 while cu_book%found loop --如果游标里面还有值就循环处理其它逻辑 dbms_output.put_line('书名:' || book.bookname || '价格' || book.price); fetch cu_book into book; end loop; close cu_book; end;注意:1、游标数据变成select * 查询全部了。2、fetch循环游标时,是将游标里面记录复制到定义的book行类型变量里面了。3、取值时,是用行类型变量.字段名取值。输出结果:五、隐式游标不用显示定义游标,但是显示的功能更强大。1、DML 执行过程中为真,结束后为假SQL%ISOPEN 布尔型DML 执行过程中为真,结束后为假示例:begin if sql%isopen then dbms_output.put_line('游标打开的'); else dbms_output.put_line('游标未打开'); end if; end;输出结果:2、DML 语句成功执行的数据行数SQL%ROWCOUNT 整型代表DML 语句成功执行的数据行数示例:declare b_count number; begin select count(*) into b_count from t_book; dbms_output.put_line('游标捕获行数:'|| sql%rowcount); end;输出结果:只成功执行了一次DML查询操作。示例2:begin update t_book set bookname='xxxx' where id=11111; if sql%rowcount=1 then dbms_output.put_line('执行修改成功'); else dbms_output.put_line('未执行成功'); end if; end;输出结果:未成功,因为没有id=11111的记录。修改id=1,重新执行;begin update t_book set bookname='xxxx' where id=1; if sql%rowcount=1 then dbms_output.put_line('执行修改成功'); else dbms_output.put_line('未执行成功'); end if; end;修改成功,因为有id=1的记录,成功执行修改了的。3、插入、删除、更新或单行查询操作成功SQL%FOUND 布尔型值为TRUE 代表插入、删除、更新或单行查询操作成功4、与SQL%FOUND 属性返回值相反SQL%NOTFOUND 布尔型与SQL%FOUND 属性返回值相反六、动态游标1、定义强类型动态游标declare type 类名 is ref cursor return 表明%rowtype;2、定义一个动态游标对象游标对象 类名;示例:查询表中是否有"jvm并发"的书,如果有就输出改记录信息,没有则查询全部记录,并输出。--定义一个booktype类似一个类 declare type booktype is ref cursor return t_book%rowtype; --定义一个动态游标对象 cu_book booktype; --返回行数 b_count number; --定义一个行类型变量 b_rowdata t_book%rowtype; begin select count(*) into b_count from t_book where bookname='jvm并发1'; --如果没有查到JVM并发这本书,就查询全部记录,保存到动态游标中。 if b_count=0 then open cu_book for select * from t_book; --注意打开游标 else open cu_book for select * from t_book where bookname='jvm并发'; end if; --循环游标将每条记录,保存到行变量中。 因为cu_book动态游标里面可能一条数据或全部的记录。 fetch cu_book into b_rowdata; --判断游标是否还有数据,有就循环输出行变量中字段数据 while cu_book%found loop dbms_output.put_line('书名:'||b_rowdata.bookname || '价格:' || b_rowdata.price); --还有数据就循环将游标数据保存到行变量中 fetch cu_book into b_rowdata; end loop; close cu_book; end;当bookname='jvm并发1'时,不存在该记录,因此查询全部记录信息。输出结果:当bookname='jvm并发'时,存在该记录,因此输出记录信息。3、定义弱类型动态游标弱类型比强类型强大。弱类型动态游标定义,只是少了返回类型的定义,没有了返回值。declare type 游标类型 is ref cursor;需求:查询t_book中是否有书名为“jvm并发”的记录,有就输出t_book所有记录数据,没有则输出t_booktype表所有记录数据。原始表数据:代码:--定义弱类型动态游标 declare type notknowtype is ref cursor; --定义一个动态游标对象 notknow notknowtype; --用于统计是否存在记录 b_count number; cu_book t_book%rowtype; cu_booktype t_Booktype%Rowtype; begin select count(*) into b_count from t_book where bookname='jvm并发'; if b_count >0 then --不存在 open notknow for select * from t_book; --查询所有t_book数据到游标 --将游标对象循环赋值到t_book行数据中 fetch notknow into cu_book; while notknow%found --如果游标对象中有数据,则循环输出全部记录信息 loop dbms_output.put_line('书ID:' || cu_book.id || '书名:' || cu_book.bookname || '价格:' || cu_book.price); fetch notknow into cu_book;--继续循环一下条数据 end loop; close notknow; else open notknow for select * from t_booktype; fetch notknow into cu_booktype; while notknow%found loop dbms_output.put_line('t_booktype id:' || cu_booktype.id || 't_booktype name:' || cu_booktype.booktypename); fetch notknow into cu_booktype; end loop; close notknow; end if; end;输出结果:书ID:3书名:jvm并发价格:66 书ID:4书名:c++价格:99 书ID:5书名:C#价格:51 书ID:1书名:xxxx价格:55 书ID:2书名:go秘籍价格:78修改条件中书名为不存在书名,重新执行:--定义弱类型动态游标 declare type notknowtype is ref cursor; --定义一个动态游标对象 notknow notknowtype; --用于统计是否存在记录 b_count number; cu_book t_book%rowtype; cu_booktype t_Booktype%Rowtype; begin select count(*) into b_count from t_book where bookname='jvm并发111'; if b_count >0 then --不存在 open notknow for select * from t_book; --查询所有t_book数据到游标 --将游标对象循环赋值到t_book行数据中 fetch notknow into cu_book; while notknow%found --如果游标对象中有数据,则循环输出全部记录信息 loop dbms_output.put_line('书ID:' || cu_book.id || '书名:' || cu_book.bookname || '价格:' || cu_book.price); fetch notknow into cu_book;--继续循环一下条数据 end loop; close notknow; else open notknow for select * from t_booktype; fetch notknow into cu_booktype; while notknow%found loop dbms_output.put_line('t_booktype id:' || cu_booktype.id || 't_booktype name:' || cu_booktype.booktypename); fetch notknow into cu_booktype; end loop; close notknow; end if; end;输出结果:t_booktype id:2t_booktype name:go t_booktype id:1t_booktype name:java history注意:成对的结束,最后的分号,关闭游标。七、查看游标1、查看系统游标数(最大游标数)select value from v$parameter where name = 'open_cursors';2、查看当前打开的游标数目select count(*) from v$open_cursor;3、查看游标使用情况select o.sid, osuser, machine,o.sql_id,o.sql_text,o.cursor_type, count(*) num_curs from v$open_cursor o, v$session s where user_name = '用户名' and o.sid = s.sid group by o.sid, osuser, machine,o.sql_id,o.sql_text,o.cursor_type order by num_curs desc;4、修改Oracle最大游标数alter system set open_cursors=2000 scope=both;5、各用户的打开游标总数SELECT A.USER_NAME, COUNT(*) FROM V$OPEN_CURSOR A GROUP BY A.USER_NAME;6、查找数据库各用户各个终端的缓存游标数SELECT AA.USERNAME, AA.MACHINE, SUM(AA.VALUE) FROM ( SELECT A.VALUE, S.MACHINE, S.USERNAME FROM V$SESSTAT A, V$STATNAME B, V$SESSION S WHERE A.STATISTIC# = B.STATISTIC# AND S.SID = A.SID AND B.NAME = 'session cursor cache count' ) AA GROUP BY AA.USERNAME, AA.MACHINE ORDER BY AA.USERNAME, AA.MACHINE;7、查找数据库各用户各个终端的打开游标数SELECT AA.USERNAME, AA.MACHINE, SUM(AA.VALUE) FROM ( SELECT A.VALUE, S.MACHINE, S.USERNAME FROM V$SESSTAT A, V$STATNAME B, V$SESSION S WHERE A.STATISTIC# = B.STATISTIC# AND S.SID = A.SID AND B.NAME = 'opened cursors current' ) AA GROUP BY AA.USERNAME, AA.MACHINE ORDER BY AA.USERNAME, AA.MACHINE;
2022年12月06日
167 阅读
0 评论
3 点赞
2022-12-05
Oracle控制语句
一、Oracle控制语句1、编写语句--写了才会输出 set serverout on; --开始语句 begin --这里面开始写语句块 dbms_output.put_line('Hello'); --注意" ; "不要完了 --控制语句结束 end; --在对话框执行时,需要输入/2、执行语句3、定义变量-只定义变量 declare 变量名 数据类型; --定义变量并赋值 declare 变量名 数据类型:赋值;示例:--写了才会输出 set serverout on; --定义变量price并赋值,注意不是在语句块里面定义 declare price number:=100; --开始语句 begin --这里面开始写语句块 dbms_output.put_line('Hello'||price); --注意" ; "不要完了 --控制语句结束 end; --在对话框执行时,需要输入/注意:可以一次定义多个变量,不用每次都写declare。示例:set serverout on; --定义多个变量 declare price number:=100; productName varchar(20):='notebook'; begin -- 凭借符号|| dbms_output.put_line(price || productName); end;4、赋值定义变量并赋值--定义变量price并赋值100,注意不是在语句块里面定义 declare price number:=100;5、查询结果赋值into:将查询结果赋值给定义的变量select 查询字段1,查询字段2 into 赋值给定义的变量1,赋值给定义的变量1 from t_book where id=bs_id;示例:set serverout on; declare count_books number; begin select count(*) into count_books from t_book; dbms_output.put_line(count_books); end;定义多个变量查询结果赋值:直接有都好隔开。select id,price into new_id,max_price from t_book where id=bs_id;6、拼接拼接符号||set serverout on; begin -- 凭借符号|| dbms_output.put_line('Hello' || 'word'); end;二、控制语句注意:都是写在语句块里面。1、普通-if条件语句 if 条件 then 条件成立逻辑 else 不成立逻辑 end if;注意:end if;示例:set serverout on; declare count_books number; begin select count(*) into count_books from t_book; dbms_output.put_line(count_books); if count_books > 2 then dbms_output.put_line('总记录数大于2'); else dbms_output.put_line('总记录数不大于2'); end if; end;输出结果:2、复杂-else-if示例:set serverout on; declare count_books number; begin select count(*) into count_books from t_book; dbms_output.put_line(count_books); if count_books = 1 then dbms_output.put_line('总记录数等于1'); else if count_books >2 then dbms_output.put_line('总记录数大于2'); else dbms_output.put_line('总记录数为0'); end if; end if; end;注意: 有多少个if,就必须要有多少个end if。三、流程控制1、case when then case 判断值 when 判断值等与值1 then 1满足时成立时逻辑; when 判断值等与值2 then 1满足时成立时逻辑; when 判断值等与值3 then 1满足时成立时逻辑; else 都不满足时逻辑; end case;注意:不要忘记end case;示例:set serverout on; declare count_books number; begin select count(*) into count_books from t_book; dbms_output.put_line('总记录数=' || count_books); case count_books when 1 then dbms_output.put_line('有1条记录'); when 2 then dbms_output.put_line('有2条记录'); when 2 then dbms_output.put_line('有3条记录'); else dbms_output.put_line('都不满足条件'); end case; end;四、循环1、无条件循环-loop loop 跳出循环控制,不然一直循环; 要操作的逻辑; end loop;示例:查询表中从id为2的书的价格,当大于id大于4时,跳出无线循环。原始表数据:set serverout on; declare bs_id number:=2; max_price number; new_id number; begin loop --跳出无线循环的条件,exit后面必须要有分号。if必须要用endif if(bs_id >4) then exit; end if; select id,price into new_id,max_price from t_book where id=bs_id; dbms_output.put_line('书名ID为:'|| new_id || '的书价格为:' || max_price); bs_id:=bs_id+1; end loop; end;注意:1、必须要有跳出无线循环的条件(开关)2、exit跳出语句后面必须要有分号。3、if必须要用end if。4、loop 必须要用end loop5、注意条件要增加如上的 bs_id:=bs_id+1;输出结果:2、while循环格式:while 条件 满足条件执行逻辑。示例:输出ID冲2开始,小于4的书籍信息。原始表数据:编写sql:set serverout on; declare bs_id number:=2; b_price number; book_Name varchar(20); begin while (bs_id) < 4 loop select bookName,price into book_Name,b_price from t_book where id=bs_id; dbms_output.put_line('ID=' || bs_id || '书名:' || book_Name || '价格:' || b_price); bs_id:=bs_id+1; end loop; end;注意:循环条件,不然会导致死循环注意:while与loop的区别是,loop需要我们手动写exit;跳出循环语句,而while通过while条件来控制循环。3、for循环格式:for 循环参数 in 循环值 loop 其它操作逻辑; end loop;代码示例:输出id从2到4的书籍信息。set serverout on; declare b_price number; book_Name varchar(20); begin for bs_id in 2..4 loop select bookName,price into book_Name,b_price from t_book where id=bs_id; dbms_output.put_line('ID=' || bs_id || '书名:' || book_Name || '价格:' || b_price); end loop; end;注意:for后面的参数值从多少到多少是2个.输出结果:
2022年12月05日
143 阅读
0 评论
2 点赞
2022-12-02
Oracle 权限、视图、约束
Oracle 权限、视图、约束一、数据库权限1、查看用户所属角色select * from user_role_privs;2、查询角色权限CONNECT:仅连接权限select * from dba_sys_privs where grantee='CONNECT'查看RESOURCE权限:select * from dba_sys_privs where grantee='RESOURCE'DBA角色权限查看select * from dba_sys_privs where grantee='DBA'3、用户授权例如:授予scoot拥有dba的权限grant dba to scott;二、视图1、创建视图Create view 视图名称as 查询语句 create view v_emp1 as select ename,job from emp;2、修改视图create or replace view 始图名称 as 新的查询数据; create or replace view vemp as select empno,ename,job from EMP t;注意与创建视图的不同:多了一个or replace3、删除视图drop view 视图名称; drop view vemp2;4、使用视图select * from 视图; select * from vemp;就当作是一张虚拟的表使用。5、修改视图数据注意:修改视图数据,实际是修改实际表的数据,不是视图的数据。例如修改上面视图数据:update vemp set ename='xxxx' where job='CLERK' and empno='7369'修改后:最后实际修改的是,视图中查询数据表的实际数据。上面视图实际修改的是EMP表的数据:select * from EMP;注意:修改视图数据时,实际是修改实际表的数据,不是视图的数据。6、视图添加数据注意:向视图数据添加数据,实际是向实际表中添加数据和普通表添加数据一样,视图只是在向实际的表添加数据 insert into vemp values('1111','bbbbb','teacher');查看添加视图数据,实际是向实际表中添加了数据。select * from vemp;查看实际表中数据:select * from EMP;7、删除视图中数据和普通删除表数据一样,实际是删除的实际表中数据。delete from vemp where empno=1111总结:对视图中数据的CRUD实际是对真实表的数据操作,实际开发中一般都会将视图设置为只读,防止误操作。8、只读视图Create view 视图名称as 查询语句 with read only;当向视图添加、删除、修改数据时,都会提示“无法对只读视图执行dml操作”。三、约束1、主键约束(Primary)用来标识唯一一条记录的。非空,和其它表的外键有关系,其它表的外键关联该表的主键,起到约束作用。一般用作条件查询、条件修改、条件删除等操作。id设置为主键。查询一条数据:insert into t_bookType values(1,'java history');查看数据:select * from t_bookType;查看建外键sql语句:查看sql:这里就能看到建表、建主键的sql语句了:2、外键约束(Foreign)起到约束作用。新建表:创建外键:注意:另一张表t_bookType的主键id不能为空,否则建外键时会报错id不能为空。查看建外键sql语句和上面一样:约束作用:查看t_bookType数据:向t_book添加数据时:添加一条不存在的主键数据时,就会提示“违反完整约束条件”因此起到了,约束添加的外键只能是另一张表里面存在的主键。3、唯一性约束(Unique)约束某些字段值作为唯一标识,不能重复添加相同数据。添加方式查看sql脚本同上:没加约束前,可以添加相同bookTypeName的记录,加了约束后会提示报错"违反唯一约束条件"4、检查约束(针对所有记录,上面1、2、3都是正对一条记录)给某个字段添加,写入值的条件。t_book增加价格price字段:查询t_book:如果以后向每次添加数据时,检查price价格返回是否在50-100之间,就可以使用检查约束。添加检查约束sql:添加数据价格在范围内不报错,超出范围会提示报错“范围检查约束条件”5、默认值约束其实就是添加数据时,某些字段没有设置时,给字段设置默认值。t_book增加作者性别字段:添加一条数据:insert into T_BOOK(id,BOOKNAME,TYPE_ID,price) values(3,'jvm并发',1,66);新加数据sex性别为空,不符合实际逻辑,怎么会没有性别,因此想设置默认值。添加默认值约束:重新添加一条数据:此时就有默认值了。删除上面添加没有sex性别的记录,重新加添;insert into T_BOOK(id,BOOKNAME,TYPE_ID,price) values(3,'jvm并发',1,66);现在就有值了。添加默认值约束SQL:6、非空约束设置字段值不能为空勾选:允许为空,不勾选:不允许为空。查询一条件数据,价格不添加:添加数据值,为添加price的值,由于设置了非空约束,不能为空,所以报错“无法将null插入”
2022年12月02日
173 阅读
0 评论
2 点赞
2022-11-24
Oracle常用函数
一、Oracle 表空间一个数据库可以有多个表空间,一个表空间里可以有多个表。表空间就是存多个表的物理空间;可以指定表空间的大小位置等。创建表空间create tablespace ts1 datafile 'C:\tablespace\ts1.dbf' size 50M;自动扩展大小create tablespace ts2 datafile 'C:\tablespace\ts2.dbf' size 50M autoextend on next 10M;设置最大空间create tablespace ts3 datafile 'C:\tablespace\ts3.dbf' size 50M autoextend on next 10M maxsize 1024M;更改用户默认表空间alter database default tablespace ts1;表空间改名alter tablespace ts1 rename to tss1;删除表空间drop tablespace ts2 including contents and datafiles;二:虚拟表dual 表Dual 表是sys 用户下的一张虚表;提供一些运算和日期操作时候用到;例如,查询系统日期时间select sysdate from dual;三、序列序列作为数据库里的对象,主要作用是生成唯一的主键值;创建序列create sequence 序列名称; 重要属性: 序列名称.currval 当前值nextval 下一个值 指定初始值: start with xx序列属性Minvalue maxvalue 最大值和最小值默认最小值1 最大值10 的27 次方; Increment by 属性序列增长步长默认值1 Cycle 设置循环; (了解即可,不使用); Cache 缓存设置; 一次获取序列的多个连续值,默认20 ,放到内存中,方便下次快速获取;四、CRUD1、表的CRUD创建表create table 表名 ( 字段名1 字段类型 默认值 是否为空 , 字段名2 字段类型 默认值 是否为空, 字段名3 字段类型 默认值 是否为空, … );示例:create table T_BOOK_LOG ( operusers VARCHAR2(10) not null, optertype VARCHAR2(10), optertime DATE )修改表名rename 旧表名 to 新表名;示例:rename t_book to t_books;删除表删除所有数据,会删除表结构drop table 表名;示例:drop table T_BOOKs;修改表添加新字段alter table 表名 add(字段名 字段类型 默认值 是否为空);示例:alter table t_book add(booknum number(6) default 0 null);字段添加注释comment on column 表明.字段 is '注释内容';示例:comment on column t_book.booknum is '书本数量';修改字段类型属性alter table 表名 modify (字段名 字段类型 默认值 是否为空);示例:alter table t_book modify (booknum varchar2(20) default 'hello');修改字段名alter table 表名 rename column 列名 to 新列名;示例:alter table t_book rename column booknum to remark;删除字段alter table 表名 drop column 字段名;示例:alter table t_book drop column remark;2、数据的CRUDinsert给指定列插入数据:insert into dept(deptno,dname) values(50,'xx'); 插入全部列数据:insert into dept(deptno,dname,loc) values(60,'xx','lll'); 简写insert into dept values(70,'xxx','llll');update更新指定数据:update dept set dname='司法部' where deptno=50; update dept set dname='司法部' ,loc='china' where deptno=50;delete删除指定数据:delete from dept where deptno=70; 删除指定条件的数据:delete from dept where deptno>40;truncatetruncate是一次性删掉所有数据,不删除表结构。注意:如果表中有identity产生的自增id列,truncate后,会恢复初始值。truncate table 表名;查询所有select * from emp;指定字段查询select ename,sal from emp;where 条件查询select * from emp where sal>=800; select * from emp where sal>=1500 and job='SALESMAN';Distinct去重复记录Group by分组查询select job,count(ename) as num from EMP t group by job;Having 过滤分组select job,count(ename) as num from EMP t group by job having count(ename)>=2;Order by 排序select * from emp order by sal desc;子查询查询出基本工资大于平均工资的员工select * from emp where sal>(select avg(sal) from emp)union并集(去重复)select * from t_user1 union select * from t_user2;union all并集(不去重)select * from t_user1 union all select * from t_user2;intersect交集select * from t_user1 intersect select * from t_user2;minus差集select * from t_user1 minus select * from t_user2;inner内连接select * from emp t,dept d where t.deptno=d.deptno;和这个一样,inner join效率更高select * from emp e inner join dept d on e.deptno=d.deptno;left join左外连接select * from emp e left join dept d on e.deptno=d.deptno;right join右外连接select * from emp e right join dept d on e.deptno=d.deptno;五、数据类型1、字符串类型及函数字符类型分3 种,char(n) 、varchar(n)、varchar2(n) ;char(n):固定长度字符串,假如长度不足n,右边空格补齐;varchar(n):可变长度字符串,假如长度不足n,右边不会补齐;varchar2(n):可变长度字符串,Oracle 官方推荐使用,向后兼容性好;char(n) VS varchar2(n): char(n)查询效率相对较高,varchar2(n)存储空间相对较小;字符串常用方法lpad() 向左补全字符串:select lpad(stuno,6,'0') from t_user3; rpad() 向右补全字符串:select rpad(stuno,6,'0') from t_user3; lower() 返回字符串小写:select lower(userName) from t_user3; upper() 返回字符串大写:select upper(userName) from t_user3; initcap() 单词首字符大写:select initcap(userName) from t_user3; length() 返回字符串长度:select length(password) from t_user3; substr() 截取字符串:select substr(userName,1,2) from t_user3; instr() 获取字符串出现的位置:select instr(password,'23',2,2) from t_user3; ltrim() 删除左侧空格:select ltrim(userName) from t_user3; rtrim() 删除右侧空格:select rtrim(userName) from t_user3; trim() 删除两侧空格:select trim(userName) from t_user3; concat() 串联字符串:select concat(userName,password) from t_user3; reverse() 反转字符串:select reverse(userName) from t_user3;2、数值类型number:是oracle 中的数据类型;number(precision,scale);Precision,scale 均可选;Precision 代表精度,sacle 代表小数位的位数;Precision 范围[1,38] scale 范围[-84,127]举例: 12345.678 Precision 是8 scale 是3;数值类型常用方法abs() 求绝对值;select abs(n1) from t_number where id=1; round() 四舍五入:select round(n1,2) from t_number where id=1; ceil() 向上取整:select ceil(n1) from t_number where id=2; floor 向下取整:select floor(n1) from t_number where id=2; Mod()取模:select mod(5,3) from dual; Sign()正负性:select sign(n1) from t_number where id=1; Sqrt() 求平方根:select sqrt(9) from dual; Power()求乘方:select power(2,3) from dual; Trunc()截取:select trunc(123.456,2) from dual; To_char() 格式化数值:常见的字符匹配有0、9、,、$、FM、L、CTo_char() 格式化数值To_char() 格式化数值:常见的字符匹配有0、9、,、$、FM、L、C select to_char(123.45,'0000.000') from dual; select to_char(123.45,'9999.999') from dual; select to_char(123123,'99,999,999.99') from dual; select to_char(123123.3,'FM99,999,999.99') from dual; select to_char(123123.3,'$99,999,999.99') from dual; select to_char(123123.3,'L99,999,999.99') from dual; select to_char(123123.3,'99,999,999.99C') from dual;3、日期类型Date 和timestamp(时间戳)Date :包含信息century(世纪信息) year 年month 月day 日hour 小时minute 分钟second 秒Timestamp :一般用于日期时间要求非常精确的情况,精确到毫秒级;insert into t_date values(1,sysdate,systimestamp);日期类型常用方法select sysdate from dual; select systimestamp from dual; Add_months 添加月份select add_months(d1,2) from t_date where id=1; Last_day 返回指定日期月份的最后一天select last_day(d1) from t_date where id=1; update t_date set d3=to_date('2016-12-20','YYYY-MM-DD') where id=1; update t_date set d3=to_date('2016-12-20 18:31:34','YYYY-MM-DD HH24:MI:SS') where id=1; Months_between 返回两个日期的相差月数select months_between(d1,d3) from t_date where id=1; next_day 返回特定日期之后的一周内的日期:select next_day(d1,2) from t_date where id=1;Trunc 截取日期select trunc(d1,'YYYY') from t_date where id=1; select trunc(d1,'MM') from t_date where id=1; select trunc(d1,'DD') from t_date where id=1; select trunc(d1,'HH') from t_date where id=1; select trunc(d1,'MI') from t_date where id=1;Extract 返回日期的某个域select extract(year from sysdate) from dual; select extract(month from sysdate) from dual; select extract(day from sysdate) from dual; select extract(Hour from systimestamp) from dual; select extract(minute from systimestamp) from dual; select extract(second from systimestamp) from dual;To_char 将日期转换成字符串select to_char(d1,'YYYY-MM-DD') from t_date where id=1; select to_char(d1,'YYYY-MM-DD HH24:MI:SS') from t_date where id=1;4、其他常用处理函数常用的聚合函数Max 求最大值select max(sal) from emp ;Min 求最小值select min(sal) from emp ;Avg 求平均值select avg(sal) from emp ;Sum 求和select sum(sal) from emp ;Count 统计记录数select count(ename) from emp ;Nvl 空值处理select ename,nvl(sal,0) from emp;rownum 分页select * from (select a.*,rownum rn from (select * from emp) A where rownum<=10) where rn>5;Oracle 中的运算select 2+1 from dual; select 2-1 from dual; select 2*1 from dual; select 2/1 from dual;Between and条件判断式Between and 范围查询:select * from emp where sal between 900 and 1500; select * from emp where sal>=900 and sal<=1500;In 集合范围select ename,hiredate from emp where ename in (select distinct ename from bonus)Like 模糊查询select * from emp where ename like '%M%' select * from emp where ename like 'M%' select * from emp where ename like '_M%'
2022年11月24日
146 阅读
0 评论
2 点赞
2022-04-14
MyCat的安装及使用
MyCat的安装及使用一、MyCat的安装1、环境准备 本次课程使用的虚拟机环境是centos6.5,首先准备四台虚拟机,安装好mysql,方便后续做读写分离和主从复制。192.168.85.111 node01 192.168.85.112 node02 192.168.85.113 node03 192.168.85.114 node04 安装jdk 使用rpm的方式直接安装jdk,配置好具体的环境变量2、安装 从官网下载需要的安装包,并且上传到具体的虚拟机中,我们在使用的时候将包上传到node01这台虚拟机,由node01充当MyCat。 下载地址为:http://dl.mycat.org.cn/1.6.7.5/2020-4-10/解压文件到/usr/local文件夹下tar -zxvf Mycat-server-1.6.7.5-release-20200422133810-linux.tar.gz -C /usr/local配置环境变量vi /etc/profile 添加如下配置信息: export MYCAT_HOME=/usr/local/mycat export PATH=$MYCAT_HOME/bin:$PATH:$JAVA_HOME/bin当执行到这步的时候,其实就可以启动了,但是为了能正确显示出效果,最好修改下MyCat的具体配置,让我们能够正常进行访问。3、配置MyCat 进入到/usr/local/mycat/conf目录下,修改该文件夹下的配置文件1、修改server.xml文件<?xml version="1.0" encoding="UTF-8"?> <!-- - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. --> <!DOCTYPE mycat:server SYSTEM "server.dtd"> <mycat:server xmlns:mycat="http://io.mycat/"> <user name="root" defaultAccount="true"> <property name="password">123456</property> <property name="schemas">TESTDB</property> <property name="defaultSchema">TESTDB</property> </user> </mycat:server>2、修改schema.xml文件<?xml version="1.0"?> <!DOCTYPE mycat:schema SYSTEM "schema.dtd"> <mycat:schema xmlns:mycat="http://io.mycat/"> <schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1"> </schema> <dataNode name="dn1" dataHost="host1" database="msb" /> <dataHost name="host1" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100"> <heartbeat>select user()</heartbeat> <writeHost host="hostM1" url="192.168.85.111:3306" user="root" password="123456"> <readHost host="hostS1" url="192.168.85.112:3306" user="root" password="123456"></readHost> </writeHost> </dataHost> </mycat:schema>4、启动mycat mycat的启动有两种方式,一种是控制台启动,一种是后台启动,在初学的时候建议大家使用控制台启动的方式,当配置文件写错之后,可以方便的看到错误,及时修改,但是在生产环境中,使用后台启动的方式比较稳妥。 控制台启动:去mycat/bin目录下执行 ./mycat console 后台启动:去mycat/bin目录下执行 ./mycat start 按照如上配置在安装的时候应该不会报错,如果出现错误,根据错误的提示解决即可。5、登录验证 管理窗口的登录 从另外的虚拟机去登录访问当前MyCat,输入如下命令即可mysql -uroot -p123456 -P 9066 -h 192.168.85.111 此时访问的是MyCat的管理窗口,可以通过show @@help查看可以执行的命令 数据窗口的登录 从另外的虚拟机去登录访问MyCat,输入命令如下:mysql -uroot -p123456 -P8066 -h 192.168.85.111 当都能够成功的时候以为着mycat已经搭建完成。二、读写分离 通过MyCat和MySQL的主从复制配合搭建数据库的读写分离,可以实现MySQL的高可用性,下面我们来搭建MySQl的读写分离。1、一主一从(主从复制的原理之前讲解过了,需要的同学自行参阅文档)1、在node01上修改/etc/my.cnf的文件#mysql服务唯一id,不同的mysql服务必须拥有全局唯一的id server-id=1 #启动二进制日期 log-bin=mysql-bin #设置不要复制的数据库 binlog-ignore-db=mysql binlog-ignore-db=information-schema #设置需要复制的数据库 binlog-do-db=msb #设置binlog的格式 binlog_format=statement2、在node02上修改/etc/my.cnf文件#服务器唯一id server-id=2 #启动中继日志 relay-log=mysql-relay3、重新启动mysql服务4、在node01上创建账户并授权slavegrant replication slave on *.* to 'root'@'%' identified by '123456'; --在进行授权的时候,如果提示密码的问题,把密码验证取消 set global validate_password_policy=0; set global validate_password_length=1;5、查看master的状态show master status6、在node02上配置需要复制的主机CHANGE MASTER TO MASTER_HOST='192.168.85.111',MASTER_USER='root',MASTER_PASSWORD='123456',MASTER_LOG_FILE='mysql-bin.000001',MASTER_LOG_POS=437;7、启动从服务器复制功能start slave;8、查看从服务器状态show slave status\G 当执行完成之后,会看到两个关键的属性Slave_IO_Running,Slave_SQL_Running,当这两个属性都是yes的时候,表示主从复制已经准备好了,可以进行具体的操作了2、一主一从验证 下面我们通过实际的操作来验证主从复制是否完成。--在node01上创建数据库 create database msb; --在node01上创建具体的表 create table mytbl(id int,name varchar(20)); --在node01上插入数据 insert into mytbl values(1,'zhangsan'); --在node02上验证发现数据已经同步成功,表示主从复制完成 通过MyCat实现读写分离 在node01上插入如下sql语句,-- 把主机名插入数据库中 insert into mytbl values(2,@@hostname); -- 然后通过mycat进行数据的访问,这个时候大家发现无论怎么查询数据,最终返回的都是node01的数据,为什么呢? select * from mytbl; 在之前的mycat基本配置中,其实我们已经配置了读写分离,大家还记得readHost和writeHost两个标签吗?<writeHost host="hostM1" url="192.168.85.111:3306" user="root" password="123456"> <readHost host="hostS1" url="192.168.85.112:3306" user="root" password="123456"></readHost> </writeHost> 其实我们已经配置过了这两个标签,默认情况下node01是用来完成写入操作的,node02是用来完成读取操作的,但是刚刚通过我们的验证发现所有的读取都是node01完成的,这是什么原因呢? 原因很简单,就是因为我们在进行配置的时候在 dataHost 标签中缺失了一个非常重要的属性balance,此属性有四个值,用来做负载均衡的,下面我们来详细介绍 1、balance=0 :不开启读写分离机制,所有读操作都发送到当前可用的writehost上 2、balance=1:全部的readhost和stand by writehost参与select 语句的负载均衡,简单的说,当双主双从模式下,其他的节点都参与select语句的负载均衡 3、balance=2:所有读操作都随机的在writehost,readhost上分发 4、balance=3:所有读请求随机的分发到readhost执行,writehost不负担读压力 当了解了这个参数的含义之后,我们可以将此参数设置为2,就能够看到在两个主机上切换执行了。3、双主双从 在上述的一主一从的架构设计中,很容易出现单点的问题,所以我们要想让生产环境中的配置足够稳定,可以配置双主双从,解决单点的问题。 架构图如下所示: 在此架构中,可以让一台主机用来处理所有写请求,此时,它的从机和备机,以及备机的从机复制所有读请求,当主机宕机之后,另一台主机负责写请求,两台主机互为备机。 主机分布如下:编号角色ip主机名1master1192.168.85.111node012slave1192.168.85.112node023master2192.168.85.113node034slave2192.168.85.114node04 下面开始搭建双主双从。 1、修改node01上的/etc/my.cnf文件#主服务器唯一ID server-id=1 #启用二进制日志 log-bin=mysql-bin # 设置不要复制的数据库(可设置多个) binlog-ignore-db=mysql binlog-ignore-db=information_schema #设置需要复制的数据库 binlog-do-db=msb #设置logbin格式 binlog_format=STATEMENT # 在作为从数据库的时候, 有写入操作也要更新二进制日志文件 log-slave-updates #表示自增长字段每次递增的量,指自增字段的起始值,其默认值是1, 取值范围是1 .. 65535 auto-increment-increment=2 # 表示自增长字段从哪个数开始,指字段一次递增多少,他的取值范围是1 .. 65535 auto-increment-offset=1 2、修改node03上的/etc/my.cnf文件#主服务器唯一ID server-id=3 #启用二进制日志 log-bin=mysql-bin # 设置不要复制的数据库(可设置多个) binlog-ignore-db=mysql binlog-ignore-db=information_schema #设置需要复制的数据库 binlog-do-db=msb #设置logbin格式 binlog_format=STATEMENT # 在作为从数据库的时候,有写入操作也要更新二进制日志文件 log-slave-updates #表示自增长字段每次递增的量,指自增字段的起始值,其默认值是1,取值范围是1 .. 65535 auto-increment-increment=2 # 表示自增长字段从哪个数开始,指字段一次递增多少,他的取值范围是1 .. 65535 auto-increment-offset=2 3、修改node02上的/etc/my.cnf文件#从服务器唯一ID server-id=2 #启用中继日志 relay-log=mysql-relay 4、修改node04上的/etc/my.cnf文件#从服务器唯一ID server-id=4 #启用中继日志 relay-log=mysql-relay 5、所有主机重新启动mysql服务 6、在两台主机node01,node03上授权同步命令GRANT REPLICATION SLAVE ON *.* TO 'root'@'%' IDENTIFIED BY '123456'; 7、查看两台主机的状态show master status; 8、在node02上执行要复制的主机CHANGE MASTER TO MASTER_HOST='192.168.85.111',MASTER_USER='root',MASTER_PASSWORD='123456',MASTER_LOG_FILE='mysql-bin.000001',MASTER_LOG_POS=154; 9、在node04上执行要复制的主机CHANGE MASTER TO MASTER_HOST='192.168.85.113',MASTER_USER='root',MASTER_PASSWORD='123456',MASTER_LOG_FILE='mysql-bin.000001',MASTER_LOG_POS=154; 10、启动两个从机的slave并且查看状态,当看到两个参数都是yes的时候表示成功start slave; show slave status; 11、完成node01跟node03的相互复制--在node01上执行 CHANGE MASTER TO MASTER_HOST='192.168.85.113',MASTER_USER='root',MASTER_PASSWORD='123456',MASTER_LOG_FILE='mysql-bin.000001',MASTER_LOG_POS=442; --开启slave start slave --查看状态 show slave status\G --在node03上执行 CHANGE MASTER TO MASTER_HOST='192.168.85.111',MASTER_USER='root',MASTER_PASSWORD='123456',MASTER_LOG_FILE='mysql-bin.000002',MASTER_LOG_POS=442; --开启slave start slave --查看状态 show slave status\G4、双主双从验证在node01上执行如下sql语句:create database msb; create table mytbl(id int,name varchar(20)); insert into mytbl values(1,'zhangsan'); --完成上述命令之后可以去其他机器验证是否同步成功 当上述操作完成之后,我们可以验证mycat的读写分离,此时我们需要进行重新的配置,修改schema.xml文件。 在当前mysql架构中,我们使用的是双主双从的架构,因此可以将balance设置为1 除此之外我们需要注意,还需要了解一些参数: 参数writeType,表示写操作发送到哪台机器,此参数有两个值可以进行设置: writeType=0:所有写操作都发送到配置的第一个writeHost,第一个挂了切换到还生存的第二个 writeType=1:所有写操作都随机的发送到配置的writehost中,1.5之后废弃, 需要注意的是:writehost重新启动之后以切换后的为准,切换记录在配置文件dnindex.properties中 参数switchType:表示如何进行切换: switchType=1:默认值,自动切换 switchType=-1:表示不自动切换 switchType=2:基于mysql主从同步的状态决定是否切换<?xml version="1.0"?> <!DOCTYPE mycat:schema SYSTEM "schema.dtd"> <mycat:schema xmlns:mycat="http://io.mycat/"> <schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1"> </schema> <dataNode name="dn1" dataHost="host1" database="msb" /> <dataHost name="host1" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100"> <heartbeat>select user()</heartbeat> <writeHost host="hostM1" url="192.168.85.111:3306" user="root" password="123456"> <readHost host="hostS1" url="192.168.85.112:3306" user="root" password="123456"></readHost> </writeHost> <writeHost host="hostM2" url="192.168.85.113:3306" user="root" password="123456"> <readHost host="hostS2" url="192.168.85.114:3306" user="root" password="123456"></readHost> </writeHost> </dataHost> </mycat:schema> 下面开始进行读写分离的验证--插入以下语句,使数据不一致 insert into mytbl values(2,@@hostname); --通过查询mycat表中的数据,发现查询到的结果在node02,node03,node04之间切换,符合正常情况 select * from mytbl; --停止node01的mysql服务 service mysqld stop --重新插入语句 insert into mytbl values(3,@@hostname); --开启node01的mysql服务 service mysqld start --执行相同的查询语句,此时发现在noede01,node02,node04之间切换,符合情况 通过上述的验证,我们可以得到一个结论,node01,node03互做备机,负责写的宕机切换,其他机器充作读请求的响应。 做到此处,希望大家能够思考一个问题,在上述我们做的读写分离操作,其实都是基于主从复制的,也就是数据同步,但是在生产环境中会存在很多种情况造成主从复制延迟问题,那么我们应该如何解决延迟问题,这是一个值得思考的问题,到底如何解决呢?三、数据切分 数据切分指的是通过某种特定的条件,将我们存放在同一个数据库中的数据分散存放到多个数据库上面,以达到分散单台设备负载的效果。 数据的切分根据其切分规则的类型,可以分为两种切分模式。一种是按照不同的表来切分到不同的数据库之上,这种切可以称之为数据的垂直切分或者纵向切分,另外一种则是根据表中的数据的逻辑关系,将同一个表中的数据按照某种条件拆分到多台数据库上面,这种切分称之为数据的水平切分或者横向切分。 垂直切分的最大特点就是 规则简单,实施也更为方便,尤其适合各业务之间的耦合度非常低,相互影响很小,业务逻辑非常清晰的系统。在这种系统中,可以很容易做到将不同业务模块所使用的表分拆到不同的数据库中。根据不同的表来进行拆分,对应用程序的影响也很小,拆分规则也会比较简单清晰。 水平切分与垂直切分相比,相对来说稍微复杂一些。因为要将同一个表中的不同数据拆分到不同的数据库中,对于应用程序来说,拆分规则本身就较根据表明来拆分更为复杂,后期的数据维护也会更为复杂一些。1、垂直切分 一个数据库由很多表的构成,每个表对应着不同的业务,垂直切分是指按照业务将表进行分类,分布到不同的数据库上面,这样也就将数据或者压力分担到不同的库上面。 如上图所示,一个系统被切分成了用户系统、订单交易、支付系统等多个库。 一个架构设计较好的应用系统,其总体功能肯定是又多个功能模块所组成的。而每一个功能模块所需要的数据对应到数据库中就是一个或者多个表。而在架构设计中,各个功能模块相关质检的交互点越统一越少,系统的耦合度就越低,系统各个模块的维护性以及扩展性也就越好。这样的系统,实现数据的垂直切分也就越容易。 但是往往系统中有些表难以做到完全的独立,存在着跨库join的情况,对于这类的表,就需要去做平衡,是数据让步业务,共用一个数据源还是分成多个库,业务之间通过接口来做调用。在系统初期,数据量比较少,或者资源有限的情况下,会选择共用数据源,但是当数据发展到一定规模,负载很大的情况下就必须要做分割。 一般来讲业务存在着复杂join的场景是难以切分的,往往业务独立的易于切分。如何切分,切分到何种程度是考验技术架构的一个难题。下面来分析下垂直切分的优缺点: 优点: 1、拆分后业务清晰,拆分规则明确 2、系统之间整合或扩展容易 3、数据维护简单 缺点: 1、部分业务表无法实现join,只能通过接口方式解决,提高了系统复杂度 2、受每种业务不同的限制存在单库性能瓶颈,不易数据扩展跟性能提高 3、事务处理复杂2、水平切分 相对于垂直拆分,水平拆分不是将表做分类,而是按照某个字段的某种规则来分散到多个库中,每个表中包含一部分数据。简单来说,我们可以将数据的水平切分理解为是按照数据行切分,就是将表中的某些行切分到一个数据库,而另外的某些行又切分到其他的数据库中, 拆分数据就需要定义分片规则。关系型数据库是行列的二维模型,拆分的第一原则是找到拆分维度。比如从会员的角度来分析,商户订单交易类系统中查询会员某天某月某个订单,那么就需要按照会员结合日期来拆分,不同的数据按照会员id做分组,这样所有的数据查询join都会在单库内解决;如果从商户的角度来讲,要查询某个商家某天所有的订单数,就需要按照商户id做拆分;但是如果系统既想按照会员拆分,又想按照商家数据拆分,就会有一定的困难,需要综合考虑找到合适的分片。 几种典型的分片规则包括: 1、按照用户id取模,将数据分散到不同的数据库,具有相同数据用户的数据都被分散到一个库中; 2、按照日期,将不同月甚至日的数据分散到不同的库中; 3、按照某个特定的字段求模,或者根据特定范围段分散到不同的库中。 如图,切分原则都是根据业务找到适合的切分规则分散到不同的库,下图是用用户id求模的案例: 数据做完了水平拆分之后也是有优缺点的。 优点: 1、拆分规则抽象好,join操作基本可以数据库做; 2、不存在单库大数据,高并发的性能瓶颈; 3、应用端改造较少; 4、提高了系统的稳定性跟负载能力 缺点: 1、拆分规则难以抽象 2、分片事务一致性难以解决 3、数据多次扩展跟维护量极大 4、跨库join性能较差3、总结 数据切分的两种方式,会发现每种方式都有自己的缺点,但是他们之间有共同的缺点,分别是: 1、引入了分布式事务的问题 2、跨节点join的问题 3、跨节点合并排序分页的问题 4、多数据源管理的问题 针对数据源管理,目前主要有两种思路: 1、客户端模式,在每个应用程序模块中配置管理自己需要的一个或多个数据源,直接访问各个数据库,在模块内完成数据的整合 2、通过中间代理层来统一管理所有的数据源,后端数据库集群对前端应用程序透明; 在实际的生产环境中,我们都会选择第二种方案来解决问题,尤其是系统不断变得庞大复杂的时候,其实这是非常正确的,虽然短期内付出的成本可能会比较大,但是对整个系统的扩展性来说,是非常有帮助的。 MyCat通过数据切分解决传统数据库的缺陷,又有了nosql易于扩展的优点。通过中间代理层规避了多数据源的数据问题,对应用完全透明,同时对数据切分后存在的问题,也做了解决方案。 MyCat在做数据切分的时候应该尽可能的遵循以下原则,当然这也是经验之谈,最终的落地实现还是要看具体的应用场景在做具体的分析 第一原则:能不切分尽量不要切分 第二原则:如果要切分一定要选择合适的切分规则,提前规划好 第三原则:数据切分尽量通过数据冗余或表分组来降低跨库join的可能 第四原则:由于数据库中间件对数据join实现的优劣难以把握,而且实现高性能难度极大,业务读取尽量少使用多表join。四、MyCat的配置文件讲解 在之前的操作中,我们已经做了部分文件的配置,但是具体的属性并没有讲解,下面我们讲解下每一个配置文件具体的属性以及相关的基本配置。1、搞定schema.xml文件 schema.xml作为MyCat中重要地配置文件之一,管理着MyCat的逻辑库、表、分片规则、DataNode以及DataSource。1、schema标签<schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1"></schema> schema标签用于定义MyCat实例中的逻辑库,MyCat可以有多个逻辑库,每个逻辑库都有自己相关的配置,可以使用schema标签来划分这些不同的逻辑库。如果不配置schame,那么所有的表配置都将属于同一个默认的逻辑库。 dataNode:该属性用于绑定逻辑库到某个具体的database上。 checkSQLschema:当该值为true时,如果执行select from TESTDB.user,那么MyCat会将语句修改为select from user,即把表示schema的字符去掉,避免发送到后端数据库执行时报错。 sqlMaxLimit:当该值设置为某个数值的时候,每次执行的sql语句,如果没有加上limit语句,MyCat也会自动加上对应的值。例如,当设置值为100的时候,那么select from user的效果跟执行select from user limit 100相同。如果不设置该值的话,MyCat默认会把所有的数据信息都查询出来,造成过多的输出,所以,还是建议设置一个具体的值,以减少过多的数据返回。当然sql语句中可以显式的制定limit的大小,不受该属性的约束。2、table标签<table name="travelrecord" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" ></table> table标签定义了MyCat中的逻辑表,所有需要拆分的表都需要在这个标签中定义。 name:定义逻辑表的表名,这个名字就如同创建表的时候指定的表名一样,同个schema标签中定义的名字必须唯一。 dataNode:定义这个逻辑表所属的dataNode,该属性的值需要和dataNode标签中的name属性值对应,如果需要定义的dn过多,可以使用如下方法减少配置:<table name="travelrecord" dataNode="multipleDn$0-99,multipleDn2$100-199" rule="auto-shardinglong" ></table> <dataNode name="multipleDn$0-99" dataHost="localhost1" database="db$0-99" ></dataNode> <dataNode name="multipleDn2$100-199" dataHost="localhost1" database=" db$100-199" ></dataNode> rule:该属性用于指定逻辑表要使用的规则名字,规则名字在rule.xml中定义,必须与tableRule标签中的name属性值一一对应 ruleRequired:该属性用于指定表是否绑定分片规则,如果配置为true,但没有配置具体rule的话,程序会报错。 primaryKey:该逻辑表对应真实表的主键,例如:分片的规则是使用非主键进行分片的,那么在使用主键查询的时候,就会发送查询语句到所有配置的DN上,如果使用改属性配置真实表的主键。那么MyCat会缓存主键与具体DN的信息,那么再次使用非主键进行查询的时候就不会进行广播式的查询,就会直接发送语句到具体的DN,但是尽管配置改属性,如果缓存没有命中的话,还是会发送语句给具体的DN来获得数据 type:该属性定义了逻辑表的类型,目前逻辑表只有全局表和普通表两种类型。对应的配置: 全局表:global 普通表:不指定该值为global的所有表 autoIncrement:MySQL 对非自增长主键,使用 last_insert_id()是不会返回结果的,只会返回 0。所以,只有定义了自增长主键的表才可以用 last_insert_id()返回主键值。MyCat目前提供了自增长主键功能,但是如果对应的MySQL节点上数据表,没有定义 auto_increment,那么在MyCat层调用 last_insert_id()也是不会返回结果的。由于 insert 操作的时候没有带入分片键, MyCat 会先取下这个表对应的全局序列,然后赋值给分片键。 这样才能正常的插入到数据库中,最后使用 last_insert_id()才会返回插入的分片键值。如果要使用这个功能最好配合使用数据库模式的全局序列。使用 autoIncrement=“true” 指定这个表有使用自增长主键,这样 MyCat才会不抛出分片键找不到的异常。使用 autoIncrement=“false” 来禁用这个功能,当然你也可以直接删除掉这个属性。默认就是禁用的。 needAddLimit:指定表是否需要自动的在每个语句后面加上limit限制。由于使用了分库分表,数据量有时会特别巨大。这时候执行查询语句,如果恰巧又忘记了加上数量限制的话,那么查询所有的数据出来,就会执行很久的时间,所以mycat自动为我们加上了limit 100。当前如果语句中又limit,那么就不会添加了。3、childTable标签 childTable标签用于定义ER分片的子表。通过标签上的属性与父表进行关联。 name:定义子表的表名 joinKey:插入子表的时候会使用这个列的值查找父表存储的数据节点 parentKey:属性指定的值一般为与父表建立关联关系的列名。程序首先获取joinkey的值,再通过parentKey属性指定的列名产生查询语句,通过执行该语句得到父表存储再哪个分片上,从而确定子表存储的位置。 primaryKey:跟table标签所描述相同 needAddLimit:跟table标签所描述相同4、dataNode标签<dataNode name="dn1" dataHost="lch3307" database="db1" ></dataNode> dataNode标签定义了mycat中的数据节点,也就是我们通常说的数据分片,一个dataNode标签就是一个独立的数据分片。 name:定义数据节点的名字,这个名字需要是唯一的,我们需要再table标签上应用这个名字,来建立表与分片对应的关系 dataHost:该属性用于定义该分片属于哪个数据库实例,属性值是引用dataHost标签上定义的name属性。 database:该属性用于定义该分片属性哪个具体数据库实力上的具体库,5、dataHost标签 该标签定义了具体的数据库实例、读写分离配置和心跳语句<dataHost name="localhost1" maxCon="1000" minCon="10" balance="0" writeType="0" dbType="mysql" dbDriver="native"> <heartbeat>select user()</heartbeat> <!-- can have multi write hosts --> <writeHost host="hostM1" url="localhost:3306" user="root" password="123456"> <!-- can have multi read hosts --> <!-- <readHost host="hostS1" url="localhost:3306" user="root" password="123456" /> --> </writeHost> <!-- <writeHost host="hostM2" url="localhost:3316" user="root" password="123456"/> --> </dataHost> name:唯一标识dataHost标签,供上层的标签使用 maxcon:指定每个读写实例连接池的最大连接 mincon:指定每个读写实例连接连接池的最小链接,初始化连接池的大小 balance:负载均衡类型: 0:不开启读写分离机制,所有读操作都发送到当前可用的writeHost上 1:全部的readHost和stand by writeHost参与select语句的负载均衡,简单的说,当双主双从模式(M1->S1,M2->S2,并且M1与M2互为主备),正常情况下,M2,S1,S2都参与select语句的负载均衡 2:所有读操作都随机的再writeHost、readHost上分发 3:所有读请求随机的分发到writeHost对应readHost执行,writeHost不负担读压力,在之后的版本中失效。 writeType:写类型 writeType=0:所有的写操作发送到配置的第一个writeHost,第一个挂了切换到还生存的第二个writeHost,重启之后以切换后的为准,切换记录保存在配置文件 dnindex.properties writeType=1:所有写操作都随机的发送到配置的writeHost,1.5以后废弃不推荐 dbType:指定后端连接的数据库类型,如MySQL,mongodb,oracle dbDriver:指定连接后端数据库使用的Driver,目前可选的值有native和JDBC。使用native的话,因为这个值执行的是二进制的mysql协议,所以可以使用mysql和maridb,其他类型的数据库则需要使用JDBC驱动来支持。 switchType:是否进行主从切换 -1:表示不自动切换 1:默认值,自动切换 2:基于mysql主从同步的状态决定是否切换6、heartbeat标签 这个标签指明用于和后端数据库进行心跳检测的语句。2、搞定server.xml文件 server.xml几乎保存了所有mycat需要的系统配置信息。<user name="test"> <property name="password">test</property> <property name="schemas">TESTDB</property> <property name="schemas">TESTDB</property> <property name="schemas">TESTDB</property> <property name="schemas">TESTDB</property> <privileges check="false"> <schema name="TESTDB" dml="0010" showTables="custome/mysql"> <table name="tbl_user" dml="0110"></table> <table name="tbl_dynamic" dml="1111"></table> </schema> </privileges> </user> server.xml中的标签本就不多,这个标签主要用于定义登录mycat的用户和权限。 property标签用来声明具体的属性值,name用来指定用户名,password用来修改密码,readonly用来限制用户是否是只读的,schemas用来控制用户课访问的schema,如果有多个的话,用逗号分隔 privileges标签是对用户的schema及下级的table进行精细化的DML控制,privileges节点的check属性适用于标识是否开启DML权限检查,默认false标识不检查,当然不配置等同于不检查 在进行检查的时候,是通过四个二进制位来标识的,insert,update,select,delete按照顺序标识,0表示未检查,1表示要检查 system标签表示系统的相关属性:属性含义备注charset字符集设置utf8,utf8mb4defaultSqlParser指定的默认解析器druidparser,fdbparser(1.4之后作废)processors系统可用的线程数,默认为机器CPU核心线程数processorBufferChunk每次分配socket direct buffer的大小默认是4096个字节processorExecutor指定NIOProcessor共享的businessExecutor固定线程池大小,mycat在处理异步逻辑的时候会把任务提交到这个线程池中 sequnceHandlerTypemycat全局序列的类型0为本地文件,1为数据库方式,2为时间戳方式,3为分布式ZK ID生成器,4为zk递增id生成3、rule.xml rule.xml里面就定义了我们对表进行拆分所涉及到的规则定义。我们可以灵活的对表使用不同的分片算法,或者对表使用相同的算法但具体的参数不同,这个文件里面主要有tableRule和function这两个标签。1、tableRule 这个标签被用来定义表规则,定义的表规则在schema.xml文件中<tableRule name="rule1"> <rule> <columns>id</columns> <algorithm>func1</algorithm> </rule> </tableRule> name属性指定唯一的名字,用来标识不同的表规则 内嵌的rule标签则指定对物理表中的哪一列进行拆分和使用什么路由算法 columns内指定要拆分的列的名字 algorithm使用function标签中的那么属性,连接表规则和具体路由算法。当然,多个表规则可以连接到同一个路由算法上。2、function<function name="hash-int" class="io.mycat.route.function.PartitionByFileMap"> <property name="mapFile">partition-hash-int.txt</property> </function> name指定算法的名字 class指定路由算法具体的类名字 property为具体算法需要用到的一些属性
2022年04月14日
241 阅读
0 评论
5 点赞
2022-04-14
MyCat
MyCat一、基础知识1、分布式系统 分布式系统是指其组件分布在网络上,组件之间通过传递消息进行通信和动作协调的系统。它的核心理念是让多台服务器协同工作,完成单台服务器无法处理的任务,尤其是高并发或者大数据量的额任务。它的特点是: 透明性:分布式系统对于用户是透明的,一个分布式系统在用户面前的表现就像一个传统的单处理机分时系统,可用用户不必了解其内部结构就能使用; 扩展性:分布式系统的最大特点是可扩展性,它能够根据需求的增加而扩展,可以通过横向扩展使集群的整体性能得到线性提升,也可以通过纵向扩展单台服务器的性能使服务器集群的性能得到提升; 可靠性:分布式系统不允许单点失效的问题存在,它的基本思想是,如果一台机器坏了,则其他机器能够接替它进行工作,具有持续服务的特性; 高性能:高性能才是设计分布式系统的初衷. 分布式系统的缺点: 1、在节点通信部分的开销比较大,线程安全问题也变得复杂,需要在保证数据完整性的同时兼顾性能 2、过分依赖网络,网络信息的丢失和饱和将会抵消分布式系统的大部分优势 3、有潜在的数据安全和网络安全等安全性问题。2、分布式数据库 随着技术的发展,各个行业所产生的数据量呈爆炸式增长,动辄就达到数百TB或者PB的级别,已经远远超过了传统单机数据库的处理能力,因此分布式数据库已经成为了最最迫切的需求。 分布式数据库是指数据在物理上分步而在逻辑上集中管理的数据库系统。物理上分步是指分布式数据库的数据分步在物理位置不同并由网络连接的节点或站点上;逻辑上集中是指各数据库节点之间在逻辑上是一个整体,并由统一的数据库管理系统管理,不同的节点分步可以跨不同的机房、城市甚至国家。 分布式数据库的特点: 透明性:用户不必关系数据的逻辑分区和物理位置分步的细节,也不必关系重复副本的一致性问题,同时不必关系在局部场地上数据库支持哪种数据模型,对于开发工程师而言,当数据库从一个场地移到另一个场地时必须改写应用程序,使用起来如果一个数据库。 数据冗余性:分布式数据库通过冗余实现系统的可靠性、可用性,并改善其性能。多个节点存储数据副本,当某一个节点的数据遭到破坏时,冗余的副本可保证数据的完整性;当工作的节点受损害时,可通过心跳等机制进行切换,系统整体不被破坏。还可以通过热点数据的就近分步原则减少网络通信的消耗,加快访问速度,改善性能。 易于扩展性:在分布式数据库中能够方便地通过水平扩展提高系统的整体性能,也能够通过垂直扩展来提高性能,扩展并不需要修改系统程序。 自治性:各节点上的数据由本地的DBMS管理,具有自治处理能力,完成本场地的应用或局部应用 分布式数据库还具有经济、性能优越、响应速度更快、灵活的体系结构、易于继承现有系统等特点。3、分布式数据库的实现原理 分布式数据库具有逻辑整体性、物理分布式,正是因为其物理分布性才使得分布式数据库的实现变得更加复杂,因为数据划分后存储在不同的节点上,而为了保证可靠性,需要存储多个副本,所以产生了数据复制的问题。为了保证良好的性能,分布式数据库必须易于扩展,具体来讲分布式数据库应有4个优势:数据分片及复制管理、具有事务的可靠性存取、良好的性能、易于扩展,所以分布式数据库在设计上需要实现数据库数据库的目录管理、数据分片、分布式查询处理、分布式并发控制、分布式锁管理、分布式存储、分布式网络架构、分布式安全管理等。 1、分布式数据库的目录管理 分布式数据库的目录存放着系统元数据及数据库的元数据的全部信息,这些数据的存在是为了正确、有效地访问数据。数据的增删改查操作都需要用到目录,用户授权、安全管理及并发控制等也都需要用到目录,目录结构的合理性直接影响数据库的性能。目录一般包括各级的描述、访问方法的描述、关于数据库的统计数据和一致性信息等,系统根据这些信息将用户查询转换为物理数据库上的查询,选择一条最佳的存取路径进行事务管理及安全性、完整性检查等。 分布式数据库的目录课分为全局目录、分布式目录、全局与本地混合目录。 2、数据分片 当数据库过于庞大,尤其是写入过于频繁且很难由一台主机支撑时,我们还是会面临扩展瓶颈。我们将存放在同一个数据库实例中的数据分散存放到多个数据库实例上,进行多台设备存取以提高性能,在切分数据的同时可以提高系统整体的可用性。 数据分片是指将数据全局地划分为相关的逻辑片段,有水平切分、垂直切分、混合切分三种类型。 水平切分:按照某个字段的某种规则分散到多个节点库中,每个节点中包含一部分数据。可以将数据的水平切分简单理解为按照数据行进行切分,就是将表中的某些行却分到一个节点,将另外某些行切分到其他节点,从分布式的整体来看它们是一个整体的表 垂直切分:一个数据库由很多表构成,每个表对应不同的业务,垂直切分是指按照业务将表进行分类并分不到不同的节点上,垂直拆分简单明了,拆分规则明确,应用程序模块清晰、明确、容易整合,但是某个表的数据量达到一定程度后扩展起来比较困难。 混合切分:水平切分和垂直切分的结合 3、分布式查询处理 分布式查询处理的任务就是把一个分布式数据库上的高层次查询映射为在本地数据库上的操作,查询的解析必须拆分为代数查询的关系运算序列,将要查询的数据定位到各节点,使得查询在各节点进行,最后通过网络通信的操作汇聚查询结果。 4、分布式并发控制 并发控制是分布式事务管理的基本任务之一,其目的是保证分布式数据库中的多个事务并发高效、正确的执行。并发控制用来保证事务的可串行性,也就是说事务的并发执行等价于它们按某种次序的串行执行,从而为用户提供并发的透明性。进行并发控制的方法主要有三种:加锁并发控制、时间戳控制、乐观并发控制。加锁并发控制应用广泛,但是容易发生死锁;时间戳控制消除了死锁,一旦发生冲突变回重启而不是等待,需要有全局的统一时钟;乐观并发控制对于冲突较少的系统较为合适,对于冲突多的系统则效率低下。4、OLTP和OLAP 在互联网时代,海量数据的存储和访问成为系统设计与使用的瓶颈,对于海量数据处理,按照使用场景,主要分为两种类型:联机事务处理(OLTP)和联级分析处理(OLAP)。 联机事务处理也称为面向事务的处理系统,其基本特征是原始数据可以立即传送到计算中心进行处理,在很短的时间内给出处理结果。 联级分析处理是指通过多维的方式对数据进行分析、查询和报表,可以同数据挖掘工具、统计分析工具配合使用,增强决策分析功能。 两者之间的区别: OLTPOLAP系统功能日常交易处理统计、分析、报表DB设计面向实时交易类应用面向统计分析类应用数据处理当前的,最新的细节的,历史的、聚集的、多维的、集成的实时性实时读写要求高实时要求读写低事务强一致性弱事务分析要求低,简单高,复杂5、关系型数据库和NoSQL 关系型数据库是建立在关系模型基础上的数据库,其借助于集合代数等数学概念和方法来处理数据库中的数据,主流的是Oracle,DB2,SQL Server,MySQL NoSQL数据库,全称为Not Only SQL,意思就是适用关系型数据库的时候就是用关系型数据库,不适用的时候也没必要非使用关系型数据库不可,可以考虑更加合适的数据存储,主要分为临时性键值存储(Memcached,Redis),永久性键值存储(Redis),面向文档的数据库(MongoDB,CouchDB),面向列的数据库(Cassandra,HBase),每种NoSQL都有其特有的使用场景及优点。 关系型数据库NoSQL数据库特点数据关系模型基于关系模型,结构化存储,完整性约束基于二维表及其之间的联系,需要连接、并、交、差等操作采用结构化的查询语言做数据读写操作需要数据的一致性,需要事务甚至强一致性非结构化的存储基于多维关系模型具有特色的使用场景优点保证数据的一致性可以进行join等复杂查询通用化,技术成熟高并发、大数据下读写能力强支持分布式,易于扩展,可伸缩简单,弱结构化存储缺点数据读写必须经过sql解析,大量数据、高并发读写性能不足对数据做读写,或修改数据结构时需要加锁,影响并发操作无法适应非结构化存储扩展困难昂贵、复杂join等复杂操作能力较弱事务支持较弱通用性差无完整约束复杂业务场景支持较差二、MyCat介绍1、MyCat是什么 MyCat 是什么?从定义和分类来看,它是一个开源的分布式数据库系统,是一个实现了 MySQL 协议的Server,前端用户可以把它看作是一个数据库代理,用 MySQL 客户端工具和命令行访问,而其后端可以用MySQL 原生(Native) 协议与多个 MySQL 服务器通信,也可以用 JDBC 协议与大多数主流数据库服务器通信,其核心功能是分表分库,即将一个大表水平分割为 N 个小表,存储在后端 MySQL 服务器里或者其他数据库里。 MyCat 发展到目前的版本,已经不是一个单纯的 MySQL 代理了,它的后端可以支持 MySQL、 SQL Server、Oracle、 DB2、 PostgreSQL 等主流数据库,也支持 MongoDB 这种新型 NoSQL 方式的存储,未来还会支持更多类型的存储。而在最终用户看来,无论是那种存储方式,在 MyCat 里,都是一个传统的数据库表,支持标准的SQL 语句进行数据的操作,这样一来,对前端业务系统来说,可以大幅降低开发难度,提升开发速度,在测试阶段,可以将一个表定义为任何一种 MyCat 支持的存储方式,比如 MySQL 的 MyASIM 表、内存表、或者MongoDB、 LevelDB 以及号称是世界上最快的内存数据库 MemSQL 上。试想一下,用户表存放在 MemSQL 上,大量读频率远超过写频率的数据如订单的快照数据存放于 InnoDB 中,一些日志数据存放于 MongoDB 中,而且还能把 Oracle 的表跟 MySQL 的表做关联查询,你是否有一种不能呼吸的感觉?而未来,还能通过 MyCat 自动将一些计算分析后的数据灌入到 Hadoop 中,并能用 MyCat+Storm/Spark Stream 引擎做大规模数据分析,看到这里,你大概明白了, MyCat 是什么? MyCat 就是 BigSQL, Big Data On SQL Database。 对于不同的角色,MyCat到底是个啥? 对于DBA而言,可以这么理解MyCat: MyCat就是MySQL Server,而Mycat后面连接的MySQL Server,就好像是MySQL的存储引擎,如InnoDB,MyISAM等,因此,MyCat本身并不存储数据,数据是再后端的MySQL上存储的,因此数据可靠性以及事务都是MySQL保证的,简单说,MyCat就s是MySQL最佳伴侣,它再一定程度上让MySQL拥有了能跟Oracle PK的能力。 对于软件工程师来说,可以这么理解MyCat: MyCat就是一个近似等于MySQL的数据库服务器,你可以用连接MySQL的方式去连接MyCat,除了端口不同,默认的MyCat端口是8066而不是MySQL的3306,因此需要再连接字符串上增加端口信息,大多数情况下,可以用你熟悉的对象映射框架使用MyCat,但建议对于分片表,尽量使用基础的SQL语句,因为这样能达到最佳性能,特别是几千万甚至几百亿条记录的情况下。 对于架构师来说,可以这么理解MyCat: MyCat是一个强大的数据库中间件,不仅仅可以用作读写分离、以及分库分表、容灾备份,而且可以用于多租户应用开发,云平台基础设施,让你的架构具备很强的适应性和灵活性,借助于即将发布的mycat只能优化模块,系统的数据访问瓶颈和热点一目了然,根据这些统计分析数据,你可以自动或手工调整后端存储,将不同的表映射到不同的存储引擎上,而整个应用的代码一行也不用改变。2、MyCat的原理 MyCat的原理并不复杂,复杂的是代码,如果代码也不复杂,那么早就成为了一个传说了。 MyCat的原理中最重要的一个动作是“拦截”,它拦截了用户发送过来的SQL语句,首先对SQL语句做了一些特定的分析:如分片分析、路由分析、读写分离分析、缓存分析等,然后将此SQL发送后端的真实数据库,并将返回的结果做适当的处理,最终再返回给用户。 上述图片里,orders表被分为了三个分片DataNode(简称dn),这三个分片是分布在两台MySQL Server上(Datahost),即datanode=database@datahost方式,因此你可以用一台到N台服务器来分片,分片规则为(sharding rule)典型的字符串枚举分片规则,一个规则的定义是分片字段(sharding column)+分片函数(rule function),这里的分片字段为prov而分片函数为字符串枚举方式。 当MyCat收到一个SQL时,会先解析这个SQL,查找涉及到的表,然后看此表的定义,如果有分片规则,则获取到SQL里分片字段的值,并分配分片函数,得到该SQL对应的分片列表,然后将SQL发往这些分片去执行,最后收集和处理所有分片返回的结果数据,并输出到客户端,以select * from orders where prov = ?语句为例,查到prov=wuhan,按照分片函数,wuhan返回dn1,于是sql就发给了mysql1,去取db1上的查询结果,并返回给用户。 如果上述sql改为select * from orders where prov in (wuhan,beijing),那么,sql就会发给MySQL1和MySQL2去执行,然后结果集合并后输出给用户。但通常业务中我们的SQL会有order by以及limit翻页语法,此时就设计到结果集在MyCat端的二次处理,这部分代码也比较复杂,而最复杂的则属两个表的join,为此,MyCat提出了创新性的ER分片,全局表,HBT(human brain tech)人工智能的Catlet,以及结合Storm/Spark引擎等十八般武艺的解决办法,从而称为目前业界最强大的方案,这就是开源的力量。3、应用场景 MyCat发展到现在,使用的场景已经很丰富,而且不断有新用户给出新的创新性的方案,以下是典型的应用场景: 1、单纯的读写分离,此时配置最为简单,支持读写分离,主从切换 2、分库分表,对于超过1000万的表进行分片,最大支持1000亿的单表分片 3、多租户应用,每个应用一个库,但应用程序只连接MyCat,从而不改造程序本身,实现多租户化 4、报表系统,借助MyCat的分表能力,处理大规模报表的统计 5、整合多数据源 6、作为海量数据实时查询的一种简单有效方案,比如100亿条频繁查询的记录需要在3秒内查询出来结果,除了基于主键的查询,还可能存在范围查询或其他属性查询,此时MyCat可能是最简单有效的选择 7、数据库路由器,MyCat基于MySQL实例的连接池复用机制,可以让每一个应用最大程度地共享一个MySQL实例的所有连接池,让数据库的并发访问能力大大提升4、为什么使用MyCat 1、java与数据库紧耦合 2、高访问量高并发对数据库的压力 3、读写请求数据不一致5、数据库中间件对比对比项目mycatmangocobarheisenbergaltasamoeba数据切片支持支持支持支持支持支持读写分离支持支持支持支持支持支持宕机自动切换支持不支持支持不支持半支持,影响写不支持mysql协议前后端支持JDBC前端支持前后端支持前后端支持JDBC支持的数据库mysql,oracle,mongodb,postgresqlmysqlmysqlmysqlmysqlmysql,mongodb社区活跃度高活跃停滞低中等停滞文档资料极丰富较齐全较齐全较少中等缺少是否开源开源开源开源开源开源开源是否支持事务弱XA支持单库强一致,分布式弱事务单库强一致,多库弱事务单库强一致,分布弱事务不支持三、MyCat的核心概念 MyCat是数据库中间件,就是介于数据库与应用之间,进行数据处理和交互的中间服务。从原有的一个库,被切分为多个分片数据库,所有的分片数据库集群构成了整个完整的数据库存储。 如上图所示,数据被分到多个分片数据库之后,应用如果需要读取数据,就要处理多个数据源的数据。如果没有数据库中间件,那么应用将直接面对分片集群,数据源切换、事务处理、数据聚合都需要应用直接处理,原本该是专注于业务的应用,将会话大量的工作来处理分片后的问题,最重要的是每个应用处理将是完全的重复造轮子。1、逻辑库 对于实际应用而言,其实并不需要知道中间件的存在,开发人员只需要知道数据库的概念即可,所以数据库中间件可以被看作是一个或多个数据库集群构成的逻辑库。 在云计算时代,数据库中间件可以以多租户的形式给一个或多个应用提供服务,每个应用访问的可能是一个独立或者共享的物理库,常见的如阿里云数据库服务器RDS2、逻辑表 既然有逻辑库,那么就应该有逻辑表,在分布式数据库中,对应用来说,读写数据的表就是逻辑表。逻辑表可以使数据切分后,分步在一个或多个分片库中,也可以不做数据切分,不分片,只有一个表构成3、分片表 分片表,是指哪些原有的很大数据的表,需要切分到多个数据库的表,这样每一个分片都会有一部分数据,所有分片构成了完整的数据。4、非分片表 一个数据库中并不是所有的表都很大,某些表是可以不用进行切分的,非分片是相对分片表来说的,就是那些不需要进行数据切分的表。5、ER表 关系型数据库是基于实体关系模型之上,通过其描述了真实世界中事物与关系,MyCat中的ER表既是来源于此。根据这一思路,提出了基于ER关系的数据分片策略,子表的记录与所关联的父表记录存放在同一个数据分片上,即子类依赖于父类,通过表分组保证数据join不会跨库操作。 表分组是解决跨分片数据join的一种很好的思路,也是数据切分规划的重要一条规则。6、全局表 一个真实的业务系统中,往往存在大量的类似字典表的表,这些表基本上很少变动,字典表具有以下几个特点: 1、变动不频繁 2、数据量总体变化不大 3、数据规模不大,很少有超过数十万条记录 对于这类的表,在分片的情况下,当业务表因为规模而进行分片以后,业务表与这些附属的字典表之间的关联,就成了比较棘手的问题,所以MyCat中通过数据冗余来解决这类表的join,即所有的分片都有一份数据的拷贝,所有将字典表或者符合字典表特性的一些表定义为全局表。 数据冗余是解决跨分片数据join的一种很好思路,也是数据切分规划的另外一条重要原则7、分片节点(dataNode) 数据切分后,一个大表被分到不同的分片数据库上面,每个表分片所在的数据库就是分片节点(dataNode)8、节点主机(dataHost) 数据切分后,每个分片节点(dataNode)不一定都会独占一台机器,同一机器上面可以有多个分片数据库,这样一个或多个分片节点(dataNode)所在的机器就是节点主机(dataHost),为了规避单节点主机并发数限制,尽量将读写压力高的分片节点(dataNode)均衡的放在不同的节点主机(dataHost)。9、分片规则 数据切分是指一个大表被分成若干个分片表,就需要一定的规则,这样按照某种规则把数据分到某个分片的规则就是分片规则,数据切分选择合适的分片规则非常重要,将极大的避免后续数据处理的难度。10、全局序列号 数据切分后,原有的关系数据库中的主键约束在分布式条件下将无法使用,因此需要引入外部机制保证数据唯一性标识,这种保证全局性的数据唯一标识的机制就是全局序列号。11、多租户 多租户技术或称多重租赁技术,是一种软件架构技术,它是在探讨与实现如何于多用户的环境下共用相同的系统或程序组件,并且扔可确保各用户间数据的隔离性。在云计算时代,多租户技术在共用的数据中心以单一系统架构与服务提供多数客户端相同甚至可定制化的服务,并且仍然可以保障客户的数据隔离。目前各种各样的云计算服务就是这类技术范畴,例如阿里云数据库服务(RDS),阿里云服务器等等。 多租户在数据存储上存在三种主要的方案,分别是:1、独立数据库 一个租户一个数据库,这种方案的用户数据隔离级别最高,安全性最好,但成本也高。 优点:为不同的租户提供独立的数据库,有助于简化数据模型的扩展设计,满足不同租户的独特需求,如果出现故障,恢复数据比较简单。 缺点:增大了数据库的安装数量,随之带来维护成本和购置成本的增加2、共享数据库,隔离数据架构 多个或者所有租户共享database,但是每一个租户一个schema 优点:为安全性要求较高的租户提供了一定程度的逻辑数据隔离,并不是完全隔离;每个数据库可以支持更多的租户数量 缺点:如果出现故障,数据恢复比较困难,因此恢复数据库将牵扯到其他租户的数据,如果需要跨租户统计数据,存在一定困难3、共享数据库,共享数据结构 租户共享同一个database,同一个schema,但在表中通过tenantID区分租户的数据。这是共享程度最高、隔离级别最低的模式 优点:维护和购置成本最低,运行每个数据库支持的租户数量最多 缺点:隔离级别最低,安全性最低,需要在设计开发时加大对安全的开发量,数据备份和恢复最困难,需要逐表逐条备份和还原。
2022年04月14日
282 阅读
0 评论
3 点赞
1
2